diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-01-18 03:00:05 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-01-18 03:00:05 +0000 |
| commit | 9680e6483f4a811e147dd75bf3f5ccab989f01e0 (patch) | |
| tree | 55d13b9a50e139561b127cde18e57fc85fb3db82 /lib/sqlalchemy | |
| parent | e9076d04b0ec82a403a885be7999eab7d346923b (diff) | |
| download | sqlalchemy-9680e6483f4a811e147dd75bf3f5ccab989f01e0.tar.gz | |
- added native INTERVAL type to the dialect. This supports
only the DAY TO SECOND interval type so far due to lack
of support in cx_oracle for YEAR TO MONTH. [ticket:1467]
- The Interval type includes a "native" flag which controls
if native INTERVAL types (postgresql + oracle) are selected
if available, or not. "day_precision" and "second_precision"
arguments are also added which propagate as appropriately
to these native types. Related to [ticket:1467].
- DefaultDialect.type_descriptor moves back to being per-dialect.
TypeEngine/TypeDecorator key type impls to the dialect class
+ server_version_info so that the colspecs dict can be modified
per-dialect based on server version.
- Fixed TypeDecorator's incorrect usage of _impl_dict
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/__init__.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/base.py | 51 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/oracle/cx_oracle.py | 8 | ||||
| -rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 4 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/default.py | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/types.py | 53 | ||||
| -rw-r--r-- | lib/sqlalchemy/util.py | 2 |
7 files changed, 109 insertions, 20 deletions
diff --git a/lib/sqlalchemy/dialects/oracle/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py index 7b4d6aeab..eb47e80cb 100644 --- a/lib/sqlalchemy/dialects/oracle/__init__.py +++ b/lib/sqlalchemy/dialects/oracle/__init__.py @@ -5,11 +5,11 @@ base.dialect = cx_oracle.dialect from sqlalchemy.dialects.oracle.base import \ VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, NUMBER,\ BLOB, BFILE, CLOB, NCLOB, TIMESTAMP, RAW,\ - FLOAT, DOUBLE_PRECISION, LONG, dialect + FLOAT, DOUBLE_PRECISION, LONG, dialect, INTERVAL __all__ = ( 'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'DATETIME', 'NUMBER', 'BLOB', 'BFILE', 'CLOB', 'NCLOB', 'TIMESTAMP', 'RAW', -'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect' +'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect', 'INTERVAL' ) diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index b63953959..882505a40 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -162,6 +162,40 @@ class BFILE(sqltypes.Binary): class LONG(sqltypes.Text): __visit_name__ = 'LONG' + +class INTERVAL(sqltypes.TypeEngine): + __visit_name__ = 'INTERVAL' + + def __init__(self, + day_precision=None, + second_precision=None): + """Construct an INTERVAL. + + Note that only DAY TO SECOND intervals are currently supported. + This is due to a lack of support for YEAR TO MONTH intervals + within available DBAPIs (cx_oracle and zxjdbc). + + :param day_precision: the day precision value. this is the number of digits + to store for the day field. Defaults to "2" + :param second_precision: the second precision value. this is the number of digits + to store for the fractional seconds field. Defaults to "6". + + """ + self.day_precision = day_precision + self.second_precision = second_precision + + @classmethod + def _adapt_from_generic_interval(cls, interval): + return INTERVAL(day_precision=interval.day_precision, + second_precision=interval.second_precision) + + def adapt(self, impltype): + return impltype(day_precision=self.day_precision, + second_precision=self.second_precision) + + @property + def _type_affinity(self): + return sqltypes.Interval class _OracleBoolean(sqltypes.Boolean): def get_dbapi_type(self, dbapi): @@ -169,6 +203,7 @@ class _OracleBoolean(sqltypes.Boolean): colspecs = { sqltypes.Boolean : _OracleBoolean, + sqltypes.Interval : INTERVAL, } ischema_names = { @@ -204,7 +239,17 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler): def visit_unicode(self, type_): return self.visit_NVARCHAR(type_) - + + def visit_INTERVAL(self, type_): + return "INTERVAL DAY%s TO SECOND%s" % ( + type_.day_precision is not None and + "(%d)" % type_.day_precision or + "", + type_.second_precision is not None and + "(%d)" % type_.second_precision or + "", + ) + def visit_DOUBLE_PRECISION(self, type_): return self._generate_numeric(type_, "DOUBLE PRECISION") @@ -512,6 +557,10 @@ class OracleDialect(default.DefaultDialect): self.implicit_returning = self.server_version_info > (10, ) and \ self.__dict__.get('implicit_returning', True) + if self.server_version_info < (9,): + self.colspecs = self.colspecs.copy() + self.colspecs.pop(sqltypes.Interval) + def do_release_savepoint(self, connection, name): # Oracle does not support RELEASE SAVEPOINT pass diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index 5a94efccb..6b1d7e5b9 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -167,15 +167,19 @@ class _OracleBinary(_LOBMixin, sqltypes.Binary): def bind_processor(self, dialect): return None - +class _OracleInterval(oracle.INTERVAL): + def get_dbapi_type(self, dbapi): + return dbapi.INTERVAL + class _OracleRaw(oracle.RAW): pass - colspecs = { sqltypes.Date : _OracleDate, sqltypes.Binary : _OracleBinary, sqltypes.Boolean : oracle._OracleBoolean, + sqltypes.Interval : _OracleInterval, + oracle.INTERVAL : _OracleInterval, sqltypes.Text : _OracleText, sqltypes.UnicodeText : _OracleUnicodeText, sqltypes.CHAR : _OracleChar, diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 308e23bae..2c8f896e9 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -109,6 +109,10 @@ class INTERVAL(sqltypes.TypeEngine): def adapt(self, impltype): return impltype(self.precision) + @classmethod + def _adapt_from_generic_interval(cls, interval): + return INTERVAL(precision=interval.second_precision) + @property def _type_affinity(self): return sqltypes.Interval diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 5d37741bd..4b2e3b681 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -57,6 +57,8 @@ class DefaultDialect(base.Dialect): supports_default_values = False supports_empty_insert = True + server_version_info = None + # indicates symbol names are # UPPERCASEd if they are case insensitive # within the database. @@ -142,8 +144,7 @@ class DefaultDialect(base.Dialect): cursor.close() return result - @classmethod - def type_descriptor(cls, typeobj): + def type_descriptor(self, typeobj): """Provide a database-specific ``TypeEngine`` object, given the generic object which comes from the types module. @@ -152,7 +153,7 @@ class DefaultDialect(base.Dialect): and passes on to ``types.adapt_type()``. """ - return sqltypes.adapt_type(typeobj, cls.colspecs) + return sqltypes.adapt_type(typeobj, self.colspecs) def reflecttable(self, connection, table, include_columns): insp = reflection.Inspector.from_engine(connection) diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 47cc37c2d..5ed631a86 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -31,7 +31,7 @@ import sys schema.types = expression.sqltypes =sys.modules['sqlalchemy.types'] from sqlalchemy.util import pickle from sqlalchemy.sql.visitors import Visitable -import sqlalchemy.util as util +from sqlalchemy import util NoneType = type(None) if util.jython: import array @@ -131,10 +131,12 @@ class TypeEngine(AbstractType): return {} def dialect_impl(self, dialect, **kwargs): + key = (dialect.__class__, dialect.server_version_info) + try: - return self._impl_dict[dialect.__class__] + return self._impl_dict[key] except KeyError: - return self._impl_dict.setdefault(dialect.__class__, dialect.__class__.type_descriptor(self)) + return self._impl_dict.setdefault(key, dialect.type_descriptor(self)) def __getstate__(self): d = self.__dict__.copy() @@ -256,19 +258,18 @@ class TypeDecorator(AbstractType): return cls() def dialect_impl(self, dialect): + key = (dialect.__class__, dialect.server_version_info) try: - return self._impl_dict[dialect.__class__] - except AttributeError: - self._impl_dict = {} + return self._impl_dict[key] except KeyError: pass # adapt the TypeDecorator first, in # the case that the dialect maps the TD # to one of its native types (i.e. PGInterval) - adapted = dialect.__class__.type_descriptor(self) + adapted = dialect.type_descriptor(self) if adapted is not self: - self._impl_dict[dialect] = adapted + self._impl_dict[key] = adapted return adapted # otherwise adapt the impl type, link @@ -280,7 +281,7 @@ class TypeDecorator(AbstractType): raise AssertionError("Type object %s does not properly implement the copy() " "method, it must return an object of type %s" % (self, self.__class__)) tt.impl = typedesc - self._impl_dict[dialect] = tt + self._impl_dict[key] = tt return tt @util.memoized_property @@ -304,7 +305,7 @@ class TypeDecorator(AbstractType): if isinstance(self.impl, TypeDecorator): return self.impl.dialect_impl(dialect) else: - return dialect.__class__.type_descriptor(self.impl) + return dialect.type_descriptor(self.impl) def __getattr__(self, key): """Proxy all other undefined accessors to the underlying implementation.""" @@ -348,6 +349,7 @@ class TypeDecorator(AbstractType): def copy(self): instance = self.__class__.__new__(self.__class__) instance.__dict__.update(self.__dict__) + instance._impl_dict = {} return instance def get_dbapi_type(self, dbapi): @@ -938,7 +940,6 @@ class SchemaType(object): self._on_metadata_create) table.metadata.append_ddl_listener('after-drop', self._on_metadata_drop) - @property def bind(self): @@ -1237,6 +1238,36 @@ class Interval(TypeDecorator): impl = DateTime epoch = dt.datetime.utcfromtimestamp(0) + def __init__(self, native=True, + second_precision=None, + day_precision=None): + """Construct an Interval object. + + :param native: when True, use the actual + INTERVAL type provided by the database, if + supported (currently Postgresql, Oracle). + Otherwise, represent the interval data as + an epoch value regardless. + + :param second_precision: For native interval types + which support a "fractional seconds precision" parameter, + i.e. Oracle and Postgresql + + :param day_precision: for native interval types which + support a "day precision" parameter, i.e. Oracle. + + """ + super(Interval, self).__init__() + self.native = native + self.second_precision = second_precision + self.day_precision = day_precision + + def adapt(self, cls): + if self.native: + return cls._adapt_from_generic_interval(self) + else: + return self + def bind_processor(self, dialect): impl_processor = self.impl.bind_processor(dialect) epoch = self.epoch diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index bd988dd20..cfa891554 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -338,7 +338,7 @@ def get_cls_kwargs(cls): if has_kw: stack.update(class_.__bases__) args.discard('self') - return list(args) + return args def get_func_kwargs(func): """Return the full set of legal kwargs for the given `func`.""" |
