summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-01-18 03:00:05 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2010-01-18 03:00:05 +0000
commit9680e6483f4a811e147dd75bf3f5ccab989f01e0 (patch)
tree55d13b9a50e139561b127cde18e57fc85fb3db82 /lib/sqlalchemy
parente9076d04b0ec82a403a885be7999eab7d346923b (diff)
downloadsqlalchemy-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__.py4
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py51
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py8
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py4
-rw-r--r--lib/sqlalchemy/engine/default.py7
-rw-r--r--lib/sqlalchemy/types.py53
-rw-r--r--lib/sqlalchemy/util.py2
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`."""