diff options
author | Wolfgang Müller | 2021-05-05 17:19:22 +0200 |
---|---|---|
committer | Wolfgang Müller | 2021-05-05 17:19:22 +0200 |
commit | 517458eeb9c7e1f3c41d8071978e48bc9822a1b2 (patch) | |
tree | 8ddb696813487ae070097006618b9842c4acc7dd | |
parent | f67f0f93e1c90eb778a51016c9777c9f7dae7839 (diff) | |
download | quarg-517458eeb9c7e1f3c41d8071978e48bc9822a1b2.tar.gz |
tables: Correctly handle Integer timestamps from the SQLite backend
Quassel stores its timestamps as INTEGERs when using the SQLite backend. Since
SQLAlchemy expects strings here instead[1], we have to handle this manually.
This commit extends the existing process_result_value() function to handle a
conversion from a unix timestamp to a proper datetime object, makes sure that
the right impl is chosen for both SQLite and PostgreSQL, and adds the
process_bind_param() function which takes care of the conversion back to a unix
timestamp when binding values for SELECT statements.
[1] https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.DateTime
-rw-r--r-- | quarg/database/tables.py | 28 |
1 files changed, 22 insertions, 6 deletions
diff --git a/quarg/database/tables.py b/quarg/database/tables.py index 743ffa2..cdb3b68 100644 --- a/quarg/database/tables.py +++ b/quarg/database/tables.py @@ -1,7 +1,7 @@ import datetime from sqlalchemy.schema import Column, ForeignKey -from sqlalchemy.types import BigInteger, Boolean, DateTime, Integer, Text, TypeDecorator +from sqlalchemy.types import BigInteger, Boolean, DateTime, Integer, Text, TypeDecorator, TypeEngine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship @@ -10,17 +10,33 @@ from sqlalchemy.orm import relationship # Timestamps are saved in the database in UTC without timezone info, so attach # a UTC timezone to the datetime object class DateTimeUTC(TypeDecorator): - # pylint complains that process_{bind,literal}_param and python_type are + # pylint complains that process_literal_param and python_type are # abstract but not overriden. This seems to not be necessary with # SQLAlchemy, so squash those warnings # pylint: disable=abstract-method - impl = DateTime + impl = TypeEngine + + def load_dialect_impl(self, dialect): + if dialect.name == 'sqlite': + return dialect.type_descriptor(Integer) + + return dialect.type_descriptor(DateTime) + + def process_bind_param(self, value, dialect): + if dialect.name == 'sqlite': + return value.timestamp() * 1000 - def process_result_value(self, value, dialect): - if value is not None: - value = value.replace(tzinfo=datetime.timezone.utc) return value + def process_result_value(self, value, dialect): + if value is None: + return value + + if dialect.name == 'sqlite': + return datetime.datetime.fromtimestamp(value / 1000, datetime.timezone.utc) + + return value.replace(tzinfo=datetime.timezone.utc) + Base = declarative_base() # Note: We have commented out unused columns to keep SQLAlchemy from selecting |