summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/databases/mssql.py
diff options
context:
space:
mode:
authorMichael Trier <mtrier@gmail.com>2009-01-05 22:05:51 +0000
committerMichael Trier <mtrier@gmail.com>2009-01-05 22:05:51 +0000
commitbc2c1b2f94a9815b4f18cc185eb3bc13c83063aa (patch)
treea928d96b62bc1ad4fc484bac1a27bc762df90470 /lib/sqlalchemy/databases/mssql.py
parentc55d3f8a7c42963e916626de92b7b9dec20cd86b (diff)
downloadsqlalchemy-bc2c1b2f94a9815b4f18cc185eb3bc13c83063aa.tar.gz
mssql date / time refactor.
- Added new MSSmallDateTime, MSDateTime2, MSDateTimeOffset, MSTime types - Refactored the Date/Time types. The smalldatetime data type no longer truncates to a date only, and will now be mapped to the MSSmallDateTime type. Closes #1254.
Diffstat (limited to 'lib/sqlalchemy/databases/mssql.py')
-rw-r--r--lib/sqlalchemy/databases/mssql.py134
1 files changed, 91 insertions, 43 deletions
diff --git a/lib/sqlalchemy/databases/mssql.py b/lib/sqlalchemy/databases/mssql.py
index 15f30942a..35d167c91 100644
--- a/lib/sqlalchemy/databases/mssql.py
+++ b/lib/sqlalchemy/databases/mssql.py
@@ -209,6 +209,16 @@ render::
If ``nullable`` is ``True`` or ``False`` then the column will be
``NULL` or ``NOT NULL`` respectively.
+Date / Time Handling
+--------------------
+For MSSQL versions that support the ``DATE`` and ``TIME`` types
+(MSSQL 2008+) the data type is used. For versions that do not
+support the ``DATE`` and ``TIME`` types a ``DATETIME`` type is used
+instead and the MSSQL dialect handles converting the results
+properly. This means ``Date()`` and ``Time()`` are fully supported
+on all versions of MSSQL. If you do not desire this behavior then
+do not use the ``Date()`` or ``Time()`` types.
+
Compatibility Levels
--------------------
MSSQL supports the notion of setting compatibility levels at the
@@ -402,32 +412,77 @@ class MSSmallInteger(MSInteger):
return "SMALLINT"
-class MSDateTime(sqltypes.DateTime):
- def __init__(self, *a, **kw):
- super(MSDateTime, self).__init__(False)
+class _DateTimeType(object):
+ """Base for MSSQL datetime types."""
+ def bind_processor(self, dialect):
+ # if we receive just a date we can manipulate it
+ # into a datetime since the db-api may not do this.
+ def process(value):
+ if type(value) is datetime.date:
+ return datetime.datetime(value.year, value.month, value.day)
+ return value
+ return process
+
+
+class MSDateTime(_DateTimeType, sqltypes.DateTime):
def get_col_spec(self):
return "DATETIME"
-class MSSmallDate(sqltypes.Date):
- def __init__(self, *a, **kw):
- super(MSSmallDate, self).__init__(False)
+class MSDate(sqltypes.Date):
+ def get_col_spec(self):
+ return "DATE"
+
+
+class MSTime(sqltypes.Time):
+ def __init__(self, precision=None, **kwargs):
+ self.precision = precision
+ super(MSTime, self).__init__()
def get_col_spec(self):
+ if self.precision:
+ return "TIME(%s)" % self.precision
+ else:
+ return "TIME"
+
+
+class MSSmallDateTime(_DateTimeType, sqltypes.TypeEngine):
+ def get_col_spec(self):
return "SMALLDATETIME"
- def result_processor(self, dialect):
- def process(value):
- # If the DBAPI returns the value as datetime.datetime(), truncate it back to datetime.date()
- if type(value) is datetime.datetime:
- return value.date()
- return value
- return process
+class MSDateTime2(_DateTimeType, sqltypes.TypeEngine):
+ def __init__(self, precision=None, **kwargs):
+ self.precision = precision
-class MSDate(sqltypes.Date):
- def __init__(self, *a, **kw):
+ def get_col_spec(self):
+ if self.precision:
+ return "DATETIME2(%s)" % self.precision
+ else:
+ return "DATETIME2"
+
+
+class MSDateTimeOffset(_DateTimeType, sqltypes.TypeEngine):
+ def __init__(self, precision=None, **kwargs):
+ self.precision = precision
+
+ def get_col_spec(self):
+ if self.precision:
+ return "DATETIMEOFFSET(%s)" % self.precision
+ else:
+ return "DATETIMEOFFSET"
+
+
+class MSDateTimeAsDate(_DateTimeType, MSDate):
+ """ This is an implementation of the Date type for versions of MSSQL that
+ do not support that specific type. In order to make it work a ``DATETIME``
+ column specification is used and the results get converted back to just
+ the date portion.
+
+ """
+
+ def __init__(self, *args, **kwargs):
super(MSDate, self).__init__(False)
def get_col_spec(self):
@@ -435,14 +490,22 @@ class MSDate(sqltypes.Date):
def result_processor(self, dialect):
def process(value):
- # If the DBAPI returns the value as datetime.datetime(), truncate it back to datetime.date()
+ # If the DBAPI returns the value as datetime.datetime(), truncate
+ # it back to datetime.date()
if type(value) is datetime.datetime:
return value.date()
return value
return process
-class MSTime(sqltypes.Time):
+class MSDateTimeAsTime(MSTime):
+ """ This is an implementation of the Time type for versions of MSSQL that
+ do not support that specific type. In order to make it work a ``DATETIME``
+ column specification is used and the results get converted back to just
+ the time portion.
+
+ """
+
__zero_date = datetime.date(1900, 1, 1)
def __init__(self, *a, **kw):
@@ -481,24 +544,6 @@ class MSDateTime_adodbapi(MSDateTime):
return process
-class MSDateTime_pyodbc(MSDateTime):
- def bind_processor(self, dialect):
- def process(value):
- if type(value) is datetime.date:
- return datetime.datetime(value.year, value.month, value.day)
- return value
- return process
-
-
-class MSDate_pyodbc(MSDate):
- def bind_processor(self, dialect):
- def process(value):
- if type(value) is datetime.date:
- return datetime.datetime(value.year, value.month, value.day)
- return value
- return process
-
-
class MSText(_StringType, sqltypes.Text):
"""MSSQL TEXT type, for variable-length text up to 2^31 characters."""
@@ -915,8 +960,11 @@ class MSSQLDialect(default.DefaultDialect):
'numeric' : MSNumeric,
'float' : MSFloat,
'datetime' : MSDateTime,
+ 'datetime2' : MSDateTime2,
+ 'datetimeoffset' : MSDateTimeOffset,
'date': MSDate,
- 'smalldatetime' : MSSmallDate,
+ 'time': MSTime,
+ 'smalldatetime' : MSSmallDateTime,
'binary' : MSBinary,
'varbinary' : MSVarBinary,
'bit': MSBoolean,
@@ -1267,6 +1315,13 @@ class MSSQLDialect_pyodbc(MSSQLDialect):
def __init__(self, description_encoding='latin-1', **params):
super(MSSQLDialect_pyodbc, self).__init__(**params)
self.description_encoding = description_encoding
+
+ self.colspecs = MSSQLDialect.colspecs.copy()
+ self.ischema_names = MSSQLDialect.ischema_names.copy()
+ if self.server_version_info < (10,):
+ self.colspecs[sqltypes.Date] = MSDateTimeAsDate
+ self.colspecs[sqltypes.Time] = MSDateTimeAsTime
+
# FIXME: scope_identity sniff should look at server version, not the ODBC driver
# whether use_scope_identity will work depends on the version of pyodbc
try:
@@ -1280,13 +1335,6 @@ class MSSQLDialect_pyodbc(MSSQLDialect):
import pyodbc as module
return module
- colspecs = MSSQLDialect.colspecs.copy()
- colspecs[sqltypes.Date] = MSDate_pyodbc
- colspecs[sqltypes.DateTime] = MSDateTime_pyodbc
- ischema_names = MSSQLDialect.ischema_names.copy()
- ischema_names['smalldatetime'] = MSDate_pyodbc
- ischema_names['datetime'] = MSDateTime_pyodbc
-
def make_connect_string(self, keys, query):
if 'max_identifier_length' in keys:
self.max_identifier_length = int(keys.pop('max_identifier_length'))