diff options
| author | Gaëtan de Menten <gdementen@gmail.com> | 2009-11-17 18:35:06 +0000 |
|---|---|---|
| committer | Gaëtan de Menten <gdementen@gmail.com> | 2009-11-17 18:35:06 +0000 |
| commit | 1bca0c42a3f4626f0273e61cf1b59ece37c3bd26 (patch) | |
| tree | 43f62bb857eb9fbd9824ff50a466b7c36f30ed6b /lib/sqlalchemy/dialects/sqlite/base.py | |
| parent | f96130aceffd4f69dd5ce4a1afaddd6f7f209cf7 (diff) | |
| download | sqlalchemy-1bca0c42a3f4626f0273e61cf1b59ece37c3bd26.tar.gz | |
- sqlite
- DATE, TIME and DATETIME types can now take optional storage_format and
regexp argument. storage_format can be used to store those types using
a custom string format. regexp allows to use a custom regular expression
to match string values from the database.
- Time and DateTime types now use by a default a stricter regular
expression to match strings from the database. Use the regexp argument
if you are using data stored in a legacy format.
- __legacy_microseconds__ on SQLite Time and DateTime types is not
supported anymore. You should use the storage_format argument instead.
- Date, Time and DateTime types are now stricter in what they accept as
bind parameters: Date type only accepts date objects (and datetime ones,
because they inherit from date), Time only accepts time objects, and
DateTime only accepts date and datetime objects.
Diffstat (limited to 'lib/sqlalchemy/dialects/sqlite/base.py')
| -rw-r--r-- | lib/sqlalchemy/dialects/sqlite/base.py | 126 |
1 files changed, 71 insertions, 55 deletions
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 33feaeaae..17619d2b5 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -15,7 +15,7 @@ SQLite does not have built-in DATE, TIME, or DATETIME types, and pysqlite does n out of the box functionality for translating values between Python `datetime` objects and a SQLite-supported format. SQLAlchemy's own :class:`~sqlalchemy.types.DateTime` and related types provide date formatting and parsing functionality when SQlite is used. -The implementation classes are :class:`_SLDateTime`, :class:`_SLDate` and :class:`_SLTime`. +The implementation classes are :class:`DATETIME`, :class:`DATE` and :class:`TIME`. These types represent dates and times as ISO formatted strings, which also nicely support ordering. There's no reliance on typical "libc" internals for these functions so historical dates are fully supported. @@ -59,76 +59,92 @@ class _SLFloat(_NumericMixin, sqltypes.Float): # or JDBC would similarly have no built in date support, so the "string" based logic # would apply to all implementing dialects. class _DateTimeMixin(object): - def _bind_processor(self, format, elements): - def process(value): - if not isinstance(value, (NoneType, datetime.date, datetime.datetime, datetime.time)): - raise TypeError("SQLite Date, Time, and DateTime types only accept Python datetime objects as input.") - elif value is not None: - return format % tuple([getattr(value, attr, 0) for attr in elements]) - else: - return None - return process + _reg = None + _storage_format = None - def _result_processor(self, fn, regexp): - rmatch = regexp.match + def __init__(self, storage_format=None, regexp=None, **kwargs): + if regexp is not None: + self._reg = re.compile(regexp) + if storage_format is not None: + self._storage_format = storage_format + + def _result_processor(self, fn): + rmatch = self._reg.match # Even on python2.6 datetime.strptime is both slower than this code # and it does not support microseconds. def process(value): if value is not None: - return fn(*[int(x or 0) for x in rmatch(value).groups()]) + return fn(*map(int, rmatch(value).groups(0))) else: return None return process -class _SLDateTime(_DateTimeMixin, sqltypes.DateTime): - __legacy_microseconds__ = False - +class DATETIME(_DateTimeMixin, sqltypes.DateTime): + _reg = re.compile(r"(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)\.(\d+)") + _storage_format = "%04d-%02d-%02d %02d:%02d:%02d.%06d" + def bind_processor(self, dialect): - if self.__legacy_microseconds__: - return self._bind_processor( - "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%s", - ("year", "month", "day", "hour", "minute", "second", "microsecond") - ) - else: - return self._bind_processor( - "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%06d", - ("year", "month", "day", "hour", "minute", "second", "microsecond") - ) + datetime_datetime = datetime.datetime + datetime_date = datetime.date + format = self._storage_format + def process(value): + if value is None: + return None + elif isinstance(value, datetime_datetime): + return format % (value.year, value.month, value.day, + value.hour, value.minute, value.second, + value.microsecond) + elif isinstance(value, datetime_date): + return format % (value.year, value.month, value.day, + 0, 0, 0, 0) + else: + raise TypeError("SQLite DateTime type only accepts Python " + "datetime and date objects as input.") + return process - _reg = re.compile(r"(\d+)-(\d+)-(\d+)(?: (\d+):(\d+):(\d+)(?:\.(\d+))?)?") def result_processor(self, dialect, coltype): - return self._result_processor(datetime.datetime, self._reg) - -class _SLDate(_DateTimeMixin, sqltypes.Date): - def bind_processor(self, dialect): - return self._bind_processor( - "%4.4d-%2.2d-%2.2d", - ("year", "month", "day") - ) + return self._result_processor(datetime.datetime) +class DATE(_DateTimeMixin, sqltypes.Date): _reg = re.compile(r"(\d+)-(\d+)-(\d+)") + _storage_format = "%04d-%02d-%02d" + + def bind_processor(self, dialect): + datetime_date = datetime.date + format = self._storage_format + def process(value): + if value is None: + return None + elif isinstance(value, datetime_date): + return format % (value.year, value.month, value.day) + else: + raise TypeError("SQLite Date type only accepts Python " + "date objects as input.") + return process + def result_processor(self, dialect, coltype): - return self._result_processor(datetime.date, self._reg) + return self._result_processor(datetime.date) -class _SLTime(_DateTimeMixin, sqltypes.Time): - __legacy_microseconds__ = False +class TIME(_DateTimeMixin, sqltypes.Time): + _reg = re.compile(r"(\d+):(\d+):(\d+)\.(\d+)") + _storage_format = "%02d:%02d:%02d.%06d" def bind_processor(self, dialect): - if self.__legacy_microseconds__: - return self._bind_processor( - "%2.2d:%2.2d:%2.2d.%s", - ("hour", "minute", "second", "microsecond") - ) - else: - return self._bind_processor( - "%2.2d:%2.2d:%2.2d.%06d", - ("hour", "minute", "second", "microsecond") - ) - - _reg = re.compile(r"(\d+):(\d+):(\d+)(?:\.(\d+))?") + datetime_time = datetime.time + format = self._storage_format + def process(value): + if value is None: + return None + elif isinstance(value, datetime_time): + return format % (value.hour, value.minute, value.second, + value.microsecond) + else: + raise TypeError("SQLite Time type only accepts Python " + "time objects as input.") + return process + def result_processor(self, dialect, coltype): - return self._result_processor(datetime.time, self._reg) - + return self._result_processor(datetime.time) class _SLBoolean(sqltypes.Boolean): def bind_processor(self, dialect): @@ -147,11 +163,11 @@ class _SLBoolean(sqltypes.Boolean): colspecs = { sqltypes.Boolean: _SLBoolean, - sqltypes.Date: _SLDate, - sqltypes.DateTime: _SLDateTime, + sqltypes.Date: DATE, + sqltypes.DateTime: DATETIME, sqltypes.Float: _SLFloat, sqltypes.Numeric: _SLNumeric, - sqltypes.Time: _SLTime, + sqltypes.Time: TIME, } ischema_names = { |
