]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- sqlite
authorGaëtan de Menten <gdementen@gmail.com>
Tue, 17 Nov 2009 18:35:06 +0000 (18:35 +0000)
committerGaëtan de Menten <gdementen@gmail.com>
Tue, 17 Nov 2009 18:35:06 +0000 (18:35 +0000)
    - 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.

CHANGES
lib/sqlalchemy/dialects/sqlite/base.py

diff --git a/CHANGES b/CHANGES
index 0e45bf8526a3acfce09092f0c4a059bcfde24de2..989e6145d1f44cdbf3ad97949ffc70c65d18e450 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -258,7 +258,7 @@ CHANGES
     method call counts when fetching columns that have no 
     type-level processing applied.   Provides a 100% speed
     improvement when fetching large result sets with no unicode
-    conversion.  Many thanks to Elixir's Gaëtan de Menten
+    conversion as tuples.  Many thanks to Elixir's Gaëtan de Menten
     for this dramatic improvement !  [ticket:1586]
     
   - setting echo=False on create_engine() now sets the loglevel
@@ -582,13 +582,6 @@ CHANGES
     - using new dialect.initialize() feature to set up
       version-dependent behavior.
 
-- new dialects
-    - postgresql+pg8000
-    - postgresql+pypostgresql (partial)
-    - postgresql+zxjdbc
-    - mysql+pyodbc
-    - mysql+zxjdbc
-
 - mssql
     - MSSQL + Pyodbc + FreeTDS now works for the most part,
       with possible exceptions regarding binary data as well as
@@ -613,6 +606,28 @@ CHANGES
       enabled through the use of "default=Sequence()". See
       the MSSQL dialect documentation for more information.
 
+- 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.
+
+- new dialects
+    - postgresql+pg8000
+    - postgresql+pypostgresql (partial)
+    - postgresql+zxjdbc
+    - mysql+pyodbc
+    - mysql+zxjdbc
+
 - types
     - The construction of types within dialects has been totally
       overhauled.  Dialects now define publically available types
index 33feaeaaeb5b47b9cd81971558744cd0520646d9..17619d2b54b90d5856244a779a377540a809e1dd 100644 (file)
@@ -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 = {