From: Mike Bayer Date: Mon, 13 Dec 2010 05:15:32 +0000 (-0500) Subject: - why type.dialect_impl(dialect).bind_processor(dialect), caching just the impl? X-Git-Tag: rel_0_7b1~167 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86e8e7c558b296018613ed979012b2aca493ffbb;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - why type.dialect_impl(dialect).bind_processor(dialect), caching just the impl? just call type._cached_bind_processor(dialect), cache the impl *and* the processor function. same for result sets. - use plain dict + update for defaultexecutioncontext.execution_options --- diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 00aaca01ee..f3f32f833f 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -701,7 +701,7 @@ class Compiled(object): """Return the string text of the generated SQL or DDL.""" return self.string or '' - + def construct_params(self, params=None): """Return the bind params for this compiled object. @@ -2221,8 +2221,7 @@ class ResultMetaData(object): name, obj, type_ = \ colname, None, typemap.get(coltype, types.NULLTYPE) - processor = type_.dialect_impl(dialect).\ - result_processor(dialect, coltype) + processor = type_._cached_result_processor(dialect, coltype) processors.append(processor) rec = (processor, i) diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 22fff33120..8647ba385f 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -307,7 +307,6 @@ class DefaultDialect(base.Dialect): class DefaultExecutionContext(base.ExecutionContext): - execution_options = util.frozendict() isinsert = False isupdate = False isdelete = False @@ -329,12 +328,10 @@ class DefaultExecutionContext(base.ExecutionContext): self.compiled = compiled = compiled_ddl self.isddl = True - if compiled.statement._execution_options: - self.execution_options = compiled.statement._execution_options + self.execution_options = compiled.statement._execution_options if connection._execution_options: - self.execution_options = self.execution_options.union( - connection._execution_options - ) + self.execution_options = dict(self.execution_options) + self.execution_options.update(connection._execution_options) if not dialect.supports_unicode_statements: self.unicode_statement = unicode(compiled) @@ -366,12 +363,10 @@ class DefaultExecutionContext(base.ExecutionContext): if not compiled.can_execute: raise exc.ArgumentError("Not an executable clause: %s" % compiled) - if compiled.statement._execution_options: - self.execution_options = compiled.statement._execution_options + self.execution_options = compiled.statement._execution_options if connection._execution_options: - self.execution_options = self.execution_options.union( - connection._execution_options - ) + self.execution_options = dict(self.execution_options) + self.execution_options.update(connection._execution_options) # compiled clauseelement. process bind params, process table defaults, # track collections used by ResultProxy to target and process results @@ -451,9 +446,7 @@ class DefaultExecutionContext(base.ExecutionContext): self.engine = connection.engine # plain text statement - if connection._execution_options: - self.execution_options = self.execution_options.\ - union(connection._execution_options) + self.execution_options = connection._execution_options if not parameters: if self.dialect.positional: @@ -493,9 +486,7 @@ class DefaultExecutionContext(base.ExecutionContext): self.dialect = dialect self._connection = self.root_connection = connection self.engine = connection.engine - if connection._execution_options: - self.execution_options = self.execution_options.\ - union(connection._execution_options) + self.execution_options = connection._execution_options self.cursor = self.create_cursor() return self diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 6d6a4485d9..cb6b27e367 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -504,7 +504,7 @@ class Column(SchemaItem, expression.ColumnClause): usage within the :mod:`~sqlalchemy.ext.declarative` extension. :param type\_: The column's type, indicated using an instance which - subclasses :class:`~sqlalchemy.types.AbstractType`. If no arguments + subclasses :class:`~sqlalchemy.types.TypeEngine`. If no arguments are required for the type, the class of the type can be sent as well, e.g.:: @@ -684,9 +684,9 @@ class Column(SchemaItem, expression.ColumnClause): if args: coltype = args[0] - if (isinstance(coltype, types.AbstractType) or + if (isinstance(coltype, types.TypeEngine) or (isinstance(coltype, type) and - issubclass(coltype, types.AbstractType))): + issubclass(coltype, types.TypeEngine))): if type_ is not None: raise exc.ArgumentError( "May not pass type_ positionally and as a keyword.") diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index d5b877a358..0c76f3e74f 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -262,7 +262,7 @@ class SQLCompiler(engine.Compiled): self._memos[key] = processors = dict( (key, value) for key, value in ( (self.bind_names[bindparam], - bindparam.bind_processor(dialect)) + bindparam.type._cached_bind_processor(dialect)) for bindparam in self.bind_names ) if value is not None ) @@ -596,7 +596,7 @@ class SQLCompiler(engine.Compiled): def render_literal_bindparam(self, bindparam, **kw): value = bindparam.value - processor = bindparam.bind_processor(self.dialect) + processor = bindparam.type._cached_bind_processor(self.dialect) if processor: value = processor(value) return self.render_literal_value(value, bindparam.type) diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index bf055f0b2f..f090d14d64 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -2470,9 +2470,6 @@ class _BindParamClause(ColumnElement): self.key = _generated_label('%%(%d %s)s' % (id(self), self._orig_key or 'param')) - def bind_processor(self, dialect): - return self.type.dialect_impl(dialect).bind_processor(dialect) - def compare(self, other, **kw): """Compare this :class:`_BindParamClause` to the given clause.""" diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 434fb07c39..480d9f8833 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -41,28 +41,43 @@ if util.jython: import array class AbstractType(Visitable): + """Base for all types - not needed except for backwards + compatibility.""" + +class TypeEngine(AbstractType): + """Base for built-in types.""" def copy_value(self, value): return value def bind_processor(self, dialect): - """Defines a bind parameter processing function. - + """Return a conversion function for processing bind values. + + Returns a callable which will receive a bind parameter value + as the sole positional argument and will return a value to + send to the DB-API. + + If processing is not necessary, the method should return ``None``. + :param dialect: Dialect instance in use. """ - return None def result_processor(self, dialect, coltype): - """Defines a result-column processing function. - + """Return a conversion function for processing result row values. + + Returns a callable which will receive a result row column + value as the sole positional argument and will return a value + to return to the user. + + If processing is not necessary, the method should return ``None``. + :param dialect: Dialect instance in use. :param coltype: DBAPI coltype argument received in cursor.description. - - """ + """ return None def compare_values(self, x, y): @@ -115,6 +130,44 @@ class AbstractType(Visitable): typ = t else: return self.__class__ + + @util.memoized_property + def _impl_dict(self): + return {} + + def __getstate__(self): + d = self.__dict__.copy() + d.pop('_impl_dict', None) + return d + + def dialect_impl(self, dialect, **kwargs): + key = dialect.__class__, dialect.server_version_info + try: + return self._impl_dict[key] + except KeyError: + return self._impl_dict.setdefault(key, + dialect.type_descriptor(self)) + + def _cached_bind_processor(self, dialect): + key = "bind", dialect.__class__, dialect.server_version_info + try: + return self._impl_dict[key] + except KeyError: + self._impl_dict[key] = bp = \ + self.dialect_impl(dialect).bind_processor(dialect) + return bp + + def _cached_result_processor(self, dialect, coltype): + key = "result", dialect.__class__, dialect.server_version_info, coltype + try: + return self._impl_dict[key] + except KeyError: + self._impl_dict[key] = rp = self.dialect_impl(dialect).\ + result_processor(dialect, coltype) + return rp + + def adapt(self, cls): + return cls() def _coerce_compared_value(self, op, value): _coerced_type = _type_map.get(type(value), NULLTYPE) @@ -164,52 +217,6 @@ class AbstractType(Visitable): ", ".join("%s=%r" % (k, getattr(self, k, None)) for k in inspect.getargspec(self.__init__)[0][1:])) -class TypeEngine(AbstractType): - """Base for built-in types.""" - - @util.memoized_property - def _impl_dict(self): - return {} - - def dialect_impl(self, dialect, **kwargs): - key = dialect.__class__, dialect.server_version_info - try: - return self._impl_dict[key] - except KeyError: - return self._impl_dict.setdefault(key, - dialect.type_descriptor(self)) - - def __getstate__(self): - d = self.__dict__.copy() - d.pop('_impl_dict', None) - return d - - def bind_processor(self, dialect): - """Return a conversion function for processing bind values. - - Returns a callable which will receive a bind parameter value - as the sole positional argument and will return a value to - send to the DB-API. - - If processing is not necessary, the method should return ``None``. - - """ - return None - - def result_processor(self, dialect, coltype): - """Return a conversion function for processing result row values. - - Returns a callable which will receive a result row column - value as the sole positional argument and will return a value - to return to the user. - - If processing is not necessary, the method should return ``None``. - - """ - return None - - def adapt(self, cls): - return cls() class UserDefinedType(TypeEngine): """Base for user defined types. @@ -264,7 +271,7 @@ class UserDefinedType(TypeEngine): """ return op -class TypeDecorator(AbstractType): +class TypeDecorator(TypeEngine): """Allows the creation of types which add additional functionality to an existing type. @@ -355,9 +362,6 @@ class TypeDecorator(AbstractType): "type being decorated") self.impl = to_instance(self.__class__.impl, *args, **kwargs) - def adapt(self, cls): - return cls() - def dialect_impl(self, dialect): key = (dialect.__class__, dialect.server_version_info) @@ -385,10 +389,6 @@ class TypeDecorator(AbstractType): self._impl_dict[key] = tt return tt - @util.memoized_property - def _impl_dict(self): - return {} - @util.memoized_property def _type_affinity(self): return self.impl._type_affinity @@ -1320,6 +1320,7 @@ class _Binary(TypeEngine): def bind_processor(self, dialect): DBAPIBinary = dialect.dbapi.Binary def process(value): + x = self if value is not None: return DBAPIBinary(value) else: @@ -1618,7 +1619,13 @@ class PickleType(MutableType, TypeDecorator): self.mutable = mutable self.comparator = comparator super(PickleType, self).__init__() - + + def __reduce__(self): + return PickleType, (self.protocol, + None, + self.mutable, + self.comparator) + def bind_processor(self, dialect): impl_processor = self.impl.bind_processor(dialect) dumps = self.pickler.dumps diff --git a/lib/sqlalchemy/util/_collections.py b/lib/sqlalchemy/util/_collections.py index ef315445d0..98c894e5b8 100644 --- a/lib/sqlalchemy/util/_collections.py +++ b/lib/sqlalchemy/util/_collections.py @@ -186,7 +186,7 @@ class OrderedDict(dict): return [self[key] for key in self._list] def itervalues(self): - return iter(self.values()) + return iter([self[key] for key in self._list]) def keys(self): return list(self._list) diff --git a/test/aaa_profiling/test_orm.py b/test/aaa_profiling/test_orm.py index 758086426d..71a5153e6a 100644 --- a/test/aaa_profiling/test_orm.py +++ b/test/aaa_profiling/test_orm.py @@ -82,7 +82,7 @@ class MergeTest(_base.MappedTest): @profiling.function_call_count(1194, versions={'2.5':1191, '2.6':1191, '2.6+cextension':1194, - '2.4': 807} + '2.4': 763} ) def go(): p2 = sess2.merge(p1) diff --git a/test/aaa_profiling/test_zoomark.py b/test/aaa_profiling/test_zoomark.py index 4bd4c9d09a..1744659971 100644 --- a/test/aaa_profiling/test_zoomark.py +++ b/test/aaa_profiling/test_zoomark.py @@ -361,15 +361,15 @@ class ZooMarkTest(TestBase): def test_profile_1_create_tables(self): self.test_baseline_1_create_tables() - @profiling.function_call_count(5371, {'2.4': 3650}) + @profiling.function_call_count(5045, {'2.4': 3650}) def test_profile_1a_populate(self): self.test_baseline_1a_populate() - @profiling.function_call_count(259, {'2.4': 186}) + @profiling.function_call_count(245, {'2.4': 172}) def test_profile_2_insert(self): self.test_baseline_2_insert() - @profiling.function_call_count(3834, {'2.4': 2158}) + @profiling.function_call_count(3634, {'2.4': 2158}) def test_profile_3_properties(self): self.test_baseline_3_properties() @@ -385,12 +385,13 @@ class ZooMarkTest(TestBase): def test_profile_5_aggregates(self): self.test_baseline_5_aggregates() - @profiling.function_call_count(1904, {'2.4': 1193}) + @profiling.function_call_count(1904, {'2.4': 1118}) def test_profile_6_editing(self): self.test_baseline_6_editing() - @profiling.function_call_count(2431, {'2.4': 1673, '2.6+cextension' - : 2502}) + @profiling.function_call_count(2598, {'2.4': 1673, + '2.7+cextension':2431, + '2.6+cextension': 2502}) def test_profile_7_multiview(self): self.test_baseline_7_multiview() diff --git a/test/aaa_profiling/test_zoomark_orm.py b/test/aaa_profiling/test_zoomark_orm.py index c864d27121..c381ce16b6 100644 --- a/test/aaa_profiling/test_zoomark_orm.py +++ b/test/aaa_profiling/test_zoomark_orm.py @@ -348,7 +348,7 @@ class ZooMarkTest(TestBase): @profiling.function_call_count(6783, { '2.6': 7194, '2.7': 7298, - '2.7+cextension': 7288, + '2.7+cextension': 6894, '2.6+cextension': 7184, }) def test_profile_3_properties(self): @@ -356,7 +356,7 @@ class ZooMarkTest(TestBase): # and this number go down slightly when using the C extensions - @profiling.function_call_count(22510, {'2.6': 24055, '2.7': 24214}) + @profiling.function_call_count(22510, {'2.6': 24055, '2.7': 22921}) def test_profile_4_expressions(self): self.test_baseline_4_expressions()