From c28bd008dd6710b941f3841d5b8c1624eddd32d2 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 3 Oct 2006 21:08:14 +0000 Subject: [PATCH] - removed "extension()" MapperOption - TypeEngine objects can report on DBAPI types - added set_input_sizes() to default dialect - oracle dialect gets Timestamp type added, may need to call set_input_sizes() to make it work with sub-second resolution [ticket:304] --- lib/sqlalchemy/databases/oracle.py | 19 +++++++++++++++---- lib/sqlalchemy/engine/default.py | 23 +++++++++++++++++++++++ lib/sqlalchemy/orm/__init__.py | 7 +------ lib/sqlalchemy/types.py | 27 +++++++++++++++++++++++---- 4 files changed, 62 insertions(+), 14 deletions(-) diff --git a/lib/sqlalchemy/databases/oracle.py b/lib/sqlalchemy/databases/oracle.py index e6d331109c..2a548fb782 100644 --- a/lib/sqlalchemy/databases/oracle.py +++ b/lib/sqlalchemy/databases/oracle.py @@ -38,6 +38,13 @@ class OracleDateTime(sqltypes.DateTime): # Oracle does not allow milliseconds in DATE # Oracle does not support TIME columns +# only if cx_oracle contains TIMESTAMP +class OracleTimestamp(sqltypes.DateTime): + def get_col_spec(self): + return "TIMESTAMP" + def get_dbapi_type(self, dialect): + return dialect.TIMESTAMP + class OracleText(sqltypes.TEXT): def get_col_spec(self): return "CLOB" @@ -79,17 +86,18 @@ colspecs = { sqltypes.Binary : OracleBinary, sqltypes.Boolean : OracleBoolean, sqltypes.TEXT : OracleText, + sqltypes.TIMESTAMP : OracleTimestamp, sqltypes.CHAR: OracleChar, } - ischema_names = { 'VARCHAR2' : OracleString, 'DATE' : OracleDateTime, 'DATETIME' : OracleDateTime, 'NUMBER' : OracleNumeric, 'BLOB' : OracleBinary, - 'CLOB' : OracleText + 'CLOB' : OracleText, + 'TIMESTAMP' : OracleTimestamp } constraintSQL = """SELECT @@ -122,8 +130,10 @@ def descriptor(): ]} class OracleExecutionContext(default.DefaultExecutionContext): - pass - + def pre_exec(self, engine, proxy, compiled, parameters): + super(OracleExecutionContext).pre_exec(engine, proxy, compiled, parameters) + #self.set_input_sizes(proxy(), parameters) + class OracleDialect(ansisql.ANSIDialect): def __init__(self, use_ansi=True, module=None, threaded=True, **kwargs): self.use_ansi = use_ansi @@ -132,6 +142,7 @@ class OracleDialect(ansisql.ANSIDialect): self.module = cx_Oracle else: self.module = module + self.supports_timestamp = hasattr(self.module, 'TIMESTAMP' ) ansisql.ANSIDialect.__init__(self, **kwargs) def dbapi(self): diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index dd9a0d278c..4a3791c35b 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -164,6 +164,29 @@ class DefaultExecutionContext(base.ExecutionContext): return self._last_updated_params def lastrow_has_defaults(self): return self._lastrow_has_defaults + def set_input_sizes(self, cursor, parameters): + """given a cursor and ClauseParameters, call the appropriate style of + setinputsizes() on the cursor, using DBAPI types from the bind parameter's + TypeEngine objects.""" + if isinstance(parameters, list): + plist = parameters + else: + plist = [parameters] + if self.dialect.positional: + inputsizes = [] + for params in plist[0]: + for key in params.positional: + typeengine = params.binds[key].type + inputsizes.append(typeengine.get_dbapi_type(self.dialect.module)) + cursor.setinputsizes(*inputsizes) + else: + inputsizes = {} + for params in plist[0]: + for key in params.keys(): + typeengine = params.binds[key].type + inputsizes[key] = typeengine.get_dbapi_type(self.dialect.module) + cursor.setinputsizes(**inputsizes) + def _process_defaults(self, engine, proxy, compiled, parameters): """INSERT and UPDATE statements, when compiled, may have additional columns added to their VALUES and SET lists corresponding to column defaults/onupdates that are present on the diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index ee0a6a4f12..b00151e943 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -17,7 +17,7 @@ import properties, strategies from session import Session as create_session __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', - 'mapper', 'clear_mappers', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query', + 'mapper', 'clear_mappers', 'sql', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query', 'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'EXT_PASS' ] @@ -58,11 +58,6 @@ def clear_mapper(m): new primary mapper.""" del mapper_registry[m.hash_key] -def extension(ext): - """returns a MapperOption that will add the given MapperExtension to the - mapper returned by mapper.options().""" - return ExtensionOption(ext) - def eagerload(name): """returns a MapperOption that will convert the property of the given name into an eager load.""" diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 31c6a232ed..9ce36cffdb 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -33,6 +33,11 @@ class AbstractType(object): return x is y def is_mutable(self): return False + def get_dbapi_type(self, dbapi): + """return the corresponding type object from the underlying DBAPI, if any. + + this can be useful for calling setinputsizes(), for example.""" + return None class TypeEngine(AbstractType): def __init__(self, *args, **params): @@ -91,6 +96,8 @@ class TypeDecorator(AbstractType): instance = self.__class__.__new__(self.__class__) instance.__dict__.update(self.__dict__) return instance + def get_dbapi_type(self, dbapi): + return self.impl.get_dbapi_type(dbapi) class MutableType(object): """a mixin that marks a Type as holding a mutable object""" @@ -158,7 +165,9 @@ class String(TypeEngine): return value else: return value.decode(dialect.encoding) - + def get_dbapi_type(self, dbapi): + return dbapi.STRING + class Unicode(TypeDecorator): impl = String def convert_bind_param(self, value, dialect): @@ -174,8 +183,9 @@ class Unicode(TypeDecorator): class Integer(TypeEngine): """integer datatype""" - pass - + def get_dbapi_type(self, dbapi): + return dbapi.NUMBER + class SmallInteger(Integer): """ smallint datatype """ pass @@ -187,6 +197,8 @@ class Numeric(TypeEngine): self.length = length def adapt(self, impltype): return impltype(precision=self.precision, length=self.length) + def get_dbapi_type(self, dbapi): + return dbapi.NUMBER class Float(Numeric): def __init__(self, precision = 10): @@ -200,10 +212,13 @@ class DateTime(TypeEngine): self.timezone = timezone def adapt(self, impltype): return impltype(timezone=self.timezone) + def get_dbapi_type(self, dbapi): + return dbapi.DATETIME class Date(TypeEngine): """implements a type for datetime.date() objects""" - pass + def get_dbapi_type(self, dbapi): + return dbapi.DATETIME class Time(TypeEngine): """implements a type for datetime.time() objects""" @@ -211,6 +226,8 @@ class Time(TypeEngine): self.timezone = timezone def adapt(self, impltype): return impltype(timezone=self.timezone) + def get_dbapi_type(self, dbapi): + return dbapi.DATETIME class Binary(TypeEngine): def __init__(self, length=None): @@ -224,6 +241,8 @@ class Binary(TypeEngine): return value def adapt(self, impltype): return impltype(length=self.length) + def get_dbapi_type(self, dbapi): + return dbapi.BINARY class PickleType(MutableType, TypeDecorator): impl = Binary -- 2.47.2