]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Try to measure new style caching in the ORM, take two
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 9 Mar 2020 21:12:35 +0000 (17:12 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 1 Apr 2020 20:12:23 +0000 (16:12 -0400)
Supercedes: If78fbb557c6f2cae637799c3fec2cbc5ac248aaf

Trying to see if by making the cache key memoized, we
still can have the older "identity" form of caching
which is the cheapest of all, at the same time as the
newer "cache key each time" version that is not nearly
as cheap; but still much cheaper than no caching at all.

Also needed is a per-execution update of _keymap when
we invoke from a cached select, so that Column objects
that are anonymous or otherwise adapted will match up.
this is analogous to the adaption of bound parameters
from the cache key.

Adds test coverage for the keymap / construct_params()
 changes related to caching.  Also hones performance
to a large extent for statement construction and
cache key generation.

Also includes a new memoized attribute
approach that vastly simplifies the previous approach
of "group_expirable_memoized_property" and finally
integrates cleanly with _clone(), _generate(), etc.
no more hardcoding of attributes is needed, as well
as that most _reset_memoization() calls are no longer
needed as the reset is inherent in a _generate() call;
this also has dramatic performance improvements.

Change-Id: I95c560ffcbfa30b26644999412fb6a385125f663

33 files changed:
examples/performance/short_selects.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/engine/result.py
lib/sqlalchemy/orm/instrumentation.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/relationships.py
lib/sqlalchemy/sql/annotation.py
lib/sqlalchemy/sql/base.py
lib/sqlalchemy/sql/coercions.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/crud.py
lib/sqlalchemy/sql/dml.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/functions.py
lib/sqlalchemy/sql/schema.py
lib/sqlalchemy/sql/selectable.py
lib/sqlalchemy/sql/traversals.py
lib/sqlalchemy/sql/type_api.py
lib/sqlalchemy/sql/util.py
lib/sqlalchemy/sql/visitors.py
lib/sqlalchemy/util/__init__.py
lib/sqlalchemy/util/langhelpers.py
test/aaa_profiling/test_memusage.py
test/aaa_profiling/test_misc.py
test/ext/test_baked.py
test/profiles.txt
test/sql/test_compare.py
test/sql/test_compiler.py
test/sql/test_external_traversal.py
test/sql/test_functions.py
test/sql/test_resultset.py
test/sql/test_selectable.py

index 376f18f02d652b3a8a02dc7ebc45ed3f0c9e31e5..db8ab8789c18c6cacaa4d3e5adbfe80192a4c34f 100644 (file)
@@ -13,6 +13,7 @@ from sqlalchemy import select
 from sqlalchemy import String
 from sqlalchemy.ext import baked
 from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.future import select as future_select
 from sqlalchemy.orm import deferred
 from sqlalchemy.orm import Session
 from . import Profiler
@@ -116,6 +117,20 @@ def test_core_new_stmt_each_time(n):
             tuple(row)
 
 
+@Profiler.profile
+def test_core_new_stmt_each_time_compiled_cache(n):
+    """test core, creating a new statement each time, but using the cache."""
+
+    compiled_cache = {}
+    with engine.connect().execution_options(
+        compiled_cache=compiled_cache
+    ) as conn:
+        for id_ in random.sample(ids, n):
+            stmt = select([Customer.__table__]).where(Customer.id == id_)
+            row = conn.execute(stmt).first()
+            tuple(row)
+
+
 @Profiler.profile
 def test_core_reuse_stmt(n):
     """test core, reusing the same statement (but recompiling each time)."""
@@ -132,8 +147,8 @@ def test_core_reuse_stmt(n):
 def test_core_reuse_stmt_compiled_cache(n):
     """test core, reusing the same statement + compiled cache."""
 
-    compiled_cache = {}
     stmt = select([Customer.__table__]).where(Customer.id == bindparam("id"))
+    compiled_cache = {}
     with engine.connect().execution_options(
         compiled_cache=compiled_cache
     ) as conn:
@@ -142,5 +157,15 @@ def test_core_reuse_stmt_compiled_cache(n):
             tuple(row)
 
 
+@Profiler.profile
+def test_core_just_statement_construct_plus_cache_key(n):
+    for i in range(n):
+        stmt = future_select(Customer.__table__).where(
+            Customer.id == bindparam("id")
+        )
+
+        stmt._generate_cache_key()
+
+
 if __name__ == "__main__":
     Profiler.main()
index 4ed3b9af7a98bc014100c1d97b307a9fa3884e3b..34a4f04a968f02b0cb754471133c8d3e6bc0d2f8 100644 (file)
@@ -1000,7 +1000,8 @@ class Connection(Connectable):
             tuple or scalar positional parameters.
 
         """
-        if isinstance(object_, util.string_types[0]):
+
+        if isinstance(object_, util.string_types):
             util.warn_deprecated_20(
                 "Passing a string to Connection.execute() is "
                 "deprecated and will be removed in version 2.0.  Use the "
@@ -1098,26 +1099,33 @@ class Connection(Connectable):
             keys = []
 
         dialect = self.dialect
+
         if "compiled_cache" in self._execution_options:
+            elem_cache_key, extracted_params = elem._generate_cache_key()
             key = (
                 dialect,
-                elem,
+                elem_cache_key,
                 tuple(sorted(keys)),
                 bool(self._schema_translate_map),
                 len(distilled_params) > 1,
             )
-            compiled_sql = self._execution_options["compiled_cache"].get(key)
+            cache = self._execution_options["compiled_cache"]
+            compiled_sql = cache.get(key)
+
             if compiled_sql is None:
                 compiled_sql = elem.compile(
                     dialect=dialect,
+                    cache_key=(elem_cache_key, extracted_params),
                     column_keys=keys,
                     inline=len(distilled_params) > 1,
                     schema_translate_map=self._schema_translate_map,
                     linting=self.dialect.compiler_linting
                     | compiler.WARN_LINTING,
                 )
-                self._execution_options["compiled_cache"][key] = compiled_sql
+                cache[key] = compiled_sql
+
         else:
+            extracted_params = None
             compiled_sql = elem.compile(
                 dialect=dialect,
                 column_keys=keys,
@@ -1133,6 +1141,8 @@ class Connection(Connectable):
             distilled_params,
             compiled_sql,
             distilled_params,
+            elem,
+            extracted_params,
         )
         if self._has_events or self.engine._has_events:
             self.dispatch.after_execute(self, elem, multiparams, params, ret)
@@ -1156,6 +1166,8 @@ class Connection(Connectable):
             parameters,
             compiled,
             parameters,
+            None,
+            None,
         )
         if self._has_events or self.engine._has_events:
             self.dispatch.after_execute(
index c44f07538e6c70c6386a748766ac885094304a05..af61be034fc07e669400fd3a5270851c65fcc147 100644 (file)
@@ -750,7 +750,14 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
 
     @classmethod
     def _init_compiled(
-        cls, dialect, connection, dbapi_connection, compiled, parameters
+        cls,
+        dialect,
+        connection,
+        dbapi_connection,
+        compiled,
+        parameters,
+        invoked_statement,
+        extracted_parameters,
     ):
         """Initialize execution context for a Compiled construct."""
 
@@ -758,7 +765,8 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
         self.root_connection = connection
         self._dbapi_connection = dbapi_connection
         self.dialect = connection.dialect
-
+        self.extracted_parameters = extracted_parameters
+        self.invoked_statement = invoked_statement
         self.compiled = compiled
 
         # this should be caught in the engine before
@@ -778,7 +786,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
             compiled._textual_ordered_columns,
             compiled._loose_column_name_matching,
         )
-
         self.isinsert = compiled.isinsert
         self.isupdate = compiled.isupdate
         self.isdelete = compiled.isdelete
@@ -792,10 +799,18 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
             )
 
         if not parameters:
-            self.compiled_parameters = [compiled.construct_params()]
+            self.compiled_parameters = [
+                compiled.construct_params(
+                    extracted_parameters=extracted_parameters
+                )
+            ]
         else:
             self.compiled_parameters = [
-                compiled.construct_params(m, _group_number=grp)
+                compiled.construct_params(
+                    m,
+                    _group_number=grp,
+                    extracted_parameters=extracted_parameters,
+                )
                 for grp, m in enumerate(parameters)
             ]
 
index ac033a5aecabf1767cad38ed57a5c6f31297069b..986edd617a9f6b24640017ba4df73f3eb3a7369e 100644 (file)
@@ -114,6 +114,44 @@ class CursorResultMetaData(ResultMetaData):
         "keys",
     )
 
+    def _adapt_to_context(self, context):
+        """When using a cached result metadata against a new context,
+        we need to rewrite the _keymap so that it has the specific
+        Column objects in the new context inside of it.  this accommodates
+        for select() constructs that contain anonymized columns and
+        are cached.
+
+        """
+        if not context.compiled._result_columns:
+            return self
+
+        compiled_statement = context.compiled.statement
+        invoked_statement = context.invoked_statement
+
+        # same statement was invoked as the one we cached against,
+        # return self
+        if compiled_statement is invoked_statement:
+            return self
+
+        # make a copy and add the columns from the invoked statement
+        # to the result map.
+        md = self.__class__.__new__(self.__class__)
+
+        md._keymap = self._keymap.copy()
+
+        # match up new columns positionally to the result columns
+        for existing, new in zip(
+            context.compiled._result_columns,
+            invoked_statement._exported_columns_iterator(),
+        ):
+            md._keymap[new] = md._keymap[existing[RM_NAME]]
+
+        md.case_sensitive = self.case_sensitive
+        md.matched_on_name = self.matched_on_name
+        md._processors = self._processors
+        md.keys = self.keys
+        return md
+
     def __init__(self, parent, cursor_description):
         context = parent.context
         dialect = context.dialect
@@ -1107,7 +1145,9 @@ class BaseResult(object):
         if strat.cursor_description is not None:
             if self.context.compiled:
                 if self.context.compiled._cached_metadata:
-                    self._metadata = self.context.compiled._cached_metadata
+                    cached_md = self.context.compiled._cached_metadata
+                    self._metadata = cached_md._adapt_to_context(self.context)
+
                 else:
                     self._metadata = (
                         self.context.compiled._cached_metadata
index 785f5408598e8b07d11f1e8439ea021fb6f0571f..432bff7d41326b738c63ed509b2fd3aad27db9b4 100644 (file)
@@ -36,12 +36,10 @@ from . import exc
 from . import interfaces
 from . import state
 from .. import util
+from ..util import HasMemoized
 
 
-_memoized_key_collection = util.group_expirable_memoized_property()
-
-
-class ClassManager(dict):
+class ClassManager(HasMemoized, dict):
     """tracks state information at the class level."""
 
     MANAGER_ATTR = base.DEFAULT_MANAGER_ATTR
@@ -122,17 +120,17 @@ class ClassManager(dict):
     def is_mapped(self):
         return "mapper" in self.__dict__
 
-    @_memoized_key_collection
+    @HasMemoized.memoized_attribute
     def _all_key_set(self):
         return frozenset(self)
 
-    @_memoized_key_collection
+    @HasMemoized.memoized_attribute
     def _collection_impl_keys(self):
         return frozenset(
             [attr.key for attr in self.values() if attr.impl.collection]
         )
 
-    @_memoized_key_collection
+    @HasMemoized.memoized_attribute
     def _scalar_loader_impls(self):
         return frozenset(
             [
@@ -142,7 +140,7 @@ class ClassManager(dict):
             ]
         )
 
-    @_memoized_key_collection
+    @HasMemoized.memoized_attribute
     def _loader_impls(self):
         return frozenset([attr.impl for attr in self.values()])
 
@@ -261,7 +259,7 @@ class ClassManager(dict):
         else:
             self.local_attrs[key] = inst
             self.install_descriptor(key, inst)
-        _memoized_key_collection.expire_instance(self)
+        self._reset_memoizations()
         self[key] = inst
 
         for cls in self.class_.__subclasses__():
@@ -291,7 +289,7 @@ class ClassManager(dict):
         else:
             del self.local_attrs[key]
             self.uninstall_descriptor(key)
-        _memoized_key_collection.expire_instance(self)
+        self._reset_memoizations()
         del self[key]
         for cls in self.class_.__subclasses__():
             manager = manager_of_class(cls)
index cd974190b441235e22f651155906be0d76bab93c..f4e20afdfdb1df45c10f1a28f9e755ab8bd9316a 100644 (file)
@@ -52,13 +52,12 @@ from ..sql import operators
 from ..sql import roles
 from ..sql import util as sql_util
 from ..sql import visitors
+from ..util import HasMemoized
 
 
 _mapper_registry = weakref.WeakKeyDictionary()
 _already_compiling = False
 
-_memoized_configured_property = util.group_expirable_memoized_property()
-
 
 # a constant returned by _get_attr_by_column to indicate
 # this mapper is not handling an attribute for a particular
@@ -1635,14 +1634,14 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
     _validate_polymorphic_identity = None
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _version_id_prop(self):
         if self.version_id_col is not None:
             return self._columntoproperty[self.version_id_col]
         else:
             return None
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _acceptable_polymorphic_identities(self):
         identities = set()
 
@@ -1655,7 +1654,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
         return identities
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _prop_set(self):
         return frozenset(self._props.values())
 
@@ -1708,7 +1707,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
                     col = m.local_table.corresponding_column(prop.columns[0])
                     if col is not None:
                         for m2 in path:
-                            m2.persist_selectable._reset_exported()
+                            m2.persist_selectable._refresh_for_new_column(col)
                         col = self.persist_selectable.corresponding_column(
                             prop.columns[0]
                         )
@@ -1859,7 +1858,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
                         # mapped table, this corresponds to adding a
                         # column after the fact to the local table.
                         # [ticket:1523]
-                        self.persist_selectable._reset_exported()
+                        self.persist_selectable._refresh_for_new_column(mc)
                     mc = self.persist_selectable.corresponding_column(c)
                     if mc is None:
                         raise sa_exc.ArgumentError(
@@ -1929,7 +1928,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
     def _expire_memoizations(self):
         for mapper in self.iterate_to_root():
-            _memoized_configured_property.expire_instance(mapper)
+            mapper._reset_memoizations()
 
     @property
     def _log_desc(self):
@@ -2078,7 +2077,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
         return from_obj
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _single_table_criterion(self):
         if self.single and self.inherits and self.polymorphic_on is not None:
             return self.polymorphic_on._annotate({"parentmapper": self}).in_(
@@ -2087,7 +2086,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
         else:
             return None
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _with_polymorphic_mappers(self):
         if Mapper._new_mappers:
             configure_mappers()
@@ -2095,7 +2094,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             return []
         return self._mappers_from_spec(*self.with_polymorphic)
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _with_polymorphic_selectable(self):
         if not self.with_polymorphic:
             return self.persist_selectable
@@ -2114,7 +2113,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
     """
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _insert_cols_evaluating_none(self):
         return dict(
             (
@@ -2126,7 +2125,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             for table, columns in self._cols_by_table.items()
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _insert_cols_as_none(self):
         return dict(
             (
@@ -2143,7 +2142,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             for table, columns in self._cols_by_table.items()
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _propkey_to_col(self):
         return dict(
             (
@@ -2155,14 +2154,14 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             for table, columns in self._cols_by_table.items()
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _pk_keys_by_table(self):
         return dict(
             (table, frozenset([col.key for col in pks]))
             for table, pks in self._pks_by_table.items()
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _pk_attr_keys_by_table(self):
         return dict(
             (
@@ -2172,7 +2171,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             for table, pks in self._pks_by_table.items()
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _server_default_cols(self):
         return dict(
             (
@@ -2188,7 +2187,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             for table, columns in self._cols_by_table.items()
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _server_default_plus_onupdate_propkeys(self):
         result = set()
 
@@ -2202,7 +2201,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
         return result
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _server_onupdate_default_cols(self):
         return dict(
             (
@@ -2258,7 +2257,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
         else:
             return mappers, self._selectable_from_mappers(mappers, innerjoin)
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _polymorphic_properties(self):
         return list(
             self._iterate_polymorphic_properties(
@@ -2294,7 +2293,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
                     continue
                 yield c
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def attrs(self):
         """A namespace of all :class:`.MapperProperty` objects
         associated this mapper.
@@ -2332,7 +2331,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             configure_mappers()
         return util.ImmutableProperties(self._props)
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def all_orm_descriptors(self):
         """A namespace of all :class:`.InspectionAttr` attributes associated
         with the mapped class.
@@ -2379,7 +2378,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             dict(self.class_manager._all_sqla_attributes())
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     @util.preload_module("sqlalchemy.orm.descriptor_props")
     def synonyms(self):
         """Return a namespace of all :class:`.SynonymProperty`
@@ -2395,7 +2394,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
         return self._filter_properties(descriptor_props.SynonymProperty)
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def column_attrs(self):
         """Return a namespace of all :class:`.ColumnProperty`
         properties maintained by this :class:`.Mapper`.
@@ -2409,7 +2408,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
         return self._filter_properties(properties.ColumnProperty)
 
     @util.preload_module("sqlalchemy.orm.relationships")
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def relationships(self):
         """A namespace of all :class:`.RelationshipProperty` properties
         maintained by this :class:`.Mapper`.
@@ -2436,7 +2435,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             util.preloaded.orm_relationships.RelationshipProperty
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     @util.preload_module("sqlalchemy.orm.descriptor_props")
     def composites(self):
         """Return a namespace of all :class:`.CompositeProperty`
@@ -2461,7 +2460,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             )
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _get_clause(self):
         """create a "get clause" based on the primary key.  this is used
         by query.get() and many-to-one lazyloads to load this item
@@ -2477,7 +2476,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             util.column_dict(params),
         )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _equivalent_columns(self):
         """Create a map of all equivalent columns, based on
         the determination of column pairs that are equated to
@@ -2610,7 +2609,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
             yield m
             m = m.inherits
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def self_and_descendants(self):
         """The collection including this mapper and all descendant mappers.
 
@@ -2737,7 +2736,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
         )
         return identity_key[1]
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _persistent_sortkey_fn(self):
         key_fns = [col.type.sort_key_function for col in self.primary_key]
 
@@ -2756,25 +2755,25 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
         return key
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _identity_key_props(self):
         return [self._columntoproperty[col] for col in self.primary_key]
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _all_pk_props(self):
         collection = set()
         for table in self.tables:
             collection.update(self._pks_by_table[table])
         return collection
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _should_undefer_in_wildcard(self):
         cols = set(self.primary_key)
         if self.polymorphic_on is not None:
             cols.add(self.polymorphic_on)
         return cols
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _primary_key_propkeys(self):
         return {prop.key for prop in self._all_pk_props}
 
@@ -2993,7 +2992,7 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
 
         return q, enable_opt, disable_opt
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _subclass_load_via_in_mapper(self):
         return self._subclass_load_via_in(self)
 
@@ -3074,11 +3073,11 @@ class Mapper(sql_base.HasCacheKey, InspectionAttr):
                     )
                 )
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _compiled_cache(self):
         return util.LRUCache(self._compiled_cache_size)
 
-    @_memoized_configured_property
+    @HasMemoized.memoized_attribute
     def _sorted_tables(self):
         table_to_mapper = {}
 
index 227543485f63b39dcc4bdb0ab473b537125c42d0..8b7a4b549dd22ea77572610117ef4518a353a265 100644 (file)
@@ -1412,13 +1412,14 @@ class RelationshipProperty(StrategizedProperty):
             if self.property.direction == MANYTOONE:
                 state = attributes.instance_state(other)
 
-                def state_bindparam(x, state, col):
+                def state_bindparam(local_col, state, remote_col):
                     dict_ = state.dict
                     return sql.bindparam(
-                        x,
+                        local_col.key,
+                        type_=local_col.type,
                         unique=True,
                         callable_=self.property._get_attr_w_warn_on_none(
-                            self.property.mapper, state, dict_, col
+                            self.property.mapper, state, dict_, remote_col
                         ),
                     )
 
index 7984dc7ea385ecd9708307f93bf09f1b067f0d19..d895e730c1a1b51c7aa439b52d6f89ec78f5799e 100644 (file)
@@ -13,6 +13,7 @@ associations.
 
 from . import operators
 from .base import HasCacheKey
+from .traversals import anon_map
 from .visitors import InternalTraversal
 from .. import util
 
@@ -20,12 +21,13 @@ from .. import util
 class SupportsAnnotations(object):
     @util.memoized_property
     def _annotations_cache_key(self):
+        anon_map_ = anon_map()
         return (
             "_annotations",
             tuple(
                 (
                     key,
-                    value._gen_cache_key(None, [])
+                    value._gen_cache_key(anon_map_, [])
                     if isinstance(value, HasCacheKey)
                     else value,
                 )
@@ -38,7 +40,7 @@ class SupportsCloneAnnotations(SupportsAnnotations):
     _annotations = util.immutabledict()
 
     _clone_annotations_traverse_internals = [
-        ("_annotations_cache_key", InternalTraversal.dp_plain_obj)
+        ("_annotations", InternalTraversal.dp_annotations_key)
     ]
 
     def _annotate(self, values):
@@ -133,6 +135,8 @@ class Annotated(object):
 
     """
 
+    _is_column_operators = False
+
     def __new__(cls, *args):
         if not args:
             # clone constructor
@@ -200,7 +204,7 @@ class Annotated(object):
         return self._hash
 
     def __eq__(self, other):
-        if isinstance(self.__element, operators.ColumnOperators):
+        if self._is_column_operators:
             return self.__element.__class__.__eq__(self, other)
         else:
             return hash(other) == hash(self)
@@ -208,7 +212,9 @@ class Annotated(object):
 
 # hard-generate Annotated subclasses.  this technique
 # is used instead of on-the-fly types (i.e. type.__new__())
-# so that the resulting objects are pickleable.
+# so that the resulting objects are pickleable; additionally, other
+# decisions can be made up front about the type of object being annotated
+# just once per class rather than per-instance.
 annotated_classes = {}
 
 
@@ -310,8 +316,11 @@ def _new_annotation_type(cls, base_cls):
 
     if "_traverse_internals" in cls.__dict__:
         anno_cls._traverse_internals = list(cls._traverse_internals) + [
-            ("_annotations_cache_key", InternalTraversal.dp_plain_obj)
+            ("_annotations", InternalTraversal.dp_annotations_key)
         ]
+
+    anno_cls._is_column_operators = issubclass(cls, operators.ColumnOperators)
+
     return anno_cls
 
 
index 974ca6ddb44e94b5793e0e39799f1ab7a18c775e..eea4003f24296baee4cfcc785ce3a80700804b8d 100644 (file)
@@ -19,6 +19,7 @@ from .visitors import ClauseVisitor
 from .visitors import InternalTraversal
 from .. import exc
 from .. import util
+from ..util import HasMemoized
 
 if util.TYPE_CHECKING:
     from types import ModuleType
@@ -58,18 +59,6 @@ class SingletonConstant(Immutable):
         cls._singleton = obj
 
 
-class HasMemoized(object):
-    def _reset_memoizations(self):
-        self._memoized_property.expire_instance(self)
-
-    def _reset_exported(self):
-        self._memoized_property.expire_instance(self)
-
-    def _copy_internals(self, **kw):
-        super(HasMemoized, self)._copy_internals(**kw)
-        self._reset_memoizations()
-
-
 def _from_objects(*elements):
     return itertools.chain.from_iterable(
         [element._from_objects for element in elements]
@@ -461,13 +450,14 @@ class CompileState(object):
         self.statement = statement
 
 
-class Generative(object):
+class Generative(HasMemoized):
     """Provide a method-chaining pattern in conjunction with the
     @_generative decorator."""
 
     def _generate(self):
+        skip = self._memoized_keys
         s = self.__class__.__new__(self.__class__)
-        s.__dict__ = self.__dict__.copy()
+        s.__dict__ = {k: v for k, v in self.__dict__.items() if k not in skip}
         return s
 
 
index 679d9c6e9bd9df8e3cf781946202e5b56e114d96..e605b486b44036171fd350d51c685c8ab3c3b2f2 100644 (file)
@@ -320,11 +320,7 @@ class BinaryElementImpl(
             self._raise_for_expected(element, err=err)
 
     def _post_coercion(self, resolved, expr, **kw):
-        if (
-            isinstance(resolved, (elements.Grouping, elements.BindParameter))
-            and resolved.type._isnull
-            and not expr.type._isnull
-        ):
+        if resolved.type._isnull and not expr.type._isnull:
             resolved = resolved._with_binary_element_type(expr.type)
         return resolved
 
index 87ae5232e7b69d992d4883efe836652e61bab20c..799fca2f583441b924b44ed1260acd934ced7ce9 100644 (file)
@@ -470,7 +470,7 @@ class Compiled(object):
 
         return self.string or ""
 
-    def construct_params(self, params=None):
+    def construct_params(self, params=None, extracted_parameters=None):
         """Return the bind params for this compiled object.
 
         :param params: a dict of string/object pairs whose values will
@@ -664,6 +664,7 @@ class SQLCompiler(Compiled):
         self,
         dialect,
         statement,
+        cache_key=None,
         column_keys=None,
         inline=False,
         linting=NO_LINTING,
@@ -687,6 +688,8 @@ class SQLCompiler(Compiled):
         """
         self.column_keys = column_keys
 
+        self.cache_key = cache_key
+
         # compile INSERT/UPDATE defaults/sequences inlined (no pre-
         # execute)
         self.inline = inline or getattr(statement, "_inline", False)
@@ -818,9 +821,38 @@ class SQLCompiler(Compiled):
     def sql_compiler(self):
         return self
 
-    def construct_params(self, params=None, _group_number=None, _check=True):
+    def construct_params(
+        self,
+        params=None,
+        _group_number=None,
+        _check=True,
+        extracted_parameters=None,
+    ):
         """return a dictionary of bind parameter keys and values"""
 
+        if extracted_parameters:
+            # related the bound parameters collected in the original cache key
+            # to those collected in the incoming cache key.  They will not have
+            # matching names but they will line up positionally in the same
+            # way.   The parameters present in self.bind_names may be clones of
+            # these original cache key params in the case of DML but the .key
+            # will be guaranteed to match.
+            try:
+                orig_extracted = self.cache_key[1]
+            except TypeError as err:
+                util.raise_(
+                    exc.CompileError(
+                        "This compiled object has no original cache key; "
+                        "can't pass extracted_parameters to construct_params"
+                    ),
+                    replace_context=err,
+                )
+            resolved_extracted = dict(
+                zip([b.key for b in orig_extracted], extracted_parameters)
+            )
+        else:
+            resolved_extracted = None
+
         if params:
             pd = {}
             for bindparam in self.bind_names:
@@ -844,11 +876,18 @@ class SQLCompiler(Compiled):
                             % bindparam.key,
                             code="cd3x",
                         )
-
-                elif bindparam.callable:
-                    pd[name] = bindparam.effective_value
                 else:
-                    pd[name] = bindparam.value
+                    if resolved_extracted:
+                        value_param = resolved_extracted.get(
+                            bindparam.key, bindparam
+                        )
+                    else:
+                        value_param = bindparam
+
+                    if bindparam.callable:
+                        pd[name] = value_param.effective_value
+                    else:
+                        pd[name] = value_param.value
             return pd
         else:
             pd = {}
@@ -868,10 +907,19 @@ class SQLCompiler(Compiled):
                             code="cd3x",
                         )
 
+                if resolved_extracted:
+                    value_param = resolved_extracted.get(
+                        bindparam.key, bindparam
+                    )
+                else:
+                    value_param = bindparam
+
                 if bindparam.callable:
-                    pd[self.bind_names[bindparam]] = bindparam.effective_value
+                    pd[
+                        self.bind_names[bindparam]
+                    ] = value_param.effective_value
                 else:
-                    pd[self.bind_names[bindparam]] = bindparam.value
+                    pd[self.bind_names[bindparam]] = value_param.value
             return pd
 
     @property
@@ -2144,7 +2192,9 @@ class SQLCompiler(Compiled):
                         assert False
                     recur_cols = [
                         c
-                        for c in util.unique_list(col_source.inner_columns)
+                        for c in util.unique_list(
+                            col_source._exported_columns_iterator()
+                        )
                         if c is not None
                     ]
 
@@ -3375,7 +3425,7 @@ class DDLCompiler(Compiled):
     def type_compiler(self):
         return self.dialect.type_compiler
 
-    def construct_params(self, params=None):
+    def construct_params(self, params=None, extracted_parameters=None):
         return None
 
     def visit_ddl(self, ddl, **kwargs):
index 2827a5817395a9a8935884409d140551adeb2e3d..114dbec9eb04ba183a8410e105e41c9a02811b93 100644 (file)
@@ -16,7 +16,6 @@ from . import coercions
 from . import dml
 from . import elements
 from . import roles
-from .elements import ClauseElement
 from .. import exc
 from .. import util
 
@@ -198,11 +197,8 @@ def _handle_values_anonymous_param(compiler, col, value, name, **kw):
     if value.type._isnull:
         # either unique parameter, or other bound parameters that were
         # passed in directly
-        # clone using base ClauseElement to retain unique key
-        value = ClauseElement._clone(value)
-
         # set type to that of the column unconditionally
-        value.type = col.type
+        value = value._with_binary_element_type(col.type)
 
     return value._compiler_dispatch(compiler, **kw)
 
index 5c75e068fa539fa554c9576d8714cfd5eef10866..cbcf54d1c6a960cdc3b9c9502d670442081ce36e 100644 (file)
@@ -14,6 +14,7 @@ from . import coercions
 from . import roles
 from .base import _from_objects
 from .base import _generative
+from .base import ColumnCollection
 from .base import CompileState
 from .base import DialectKWArgs
 from .base import Executable
@@ -364,6 +365,28 @@ class UpdateBase(
         """
         self._returning = cols
 
+    def _exported_columns_iterator(self):
+        """Return the RETURNING columns as a sequence for this statement.
+
+        .. versionadded:: 1.4
+
+        """
+
+        return self._returning or ()
+
+    @property
+    def exported_columns(self):
+        """Return the RETURNING columns as a column collection for this
+        statement.
+
+        .. versionadded:: 1.4
+
+        """
+        # TODO: no coverage here
+        return ColumnCollection(
+            (c.key, c) for c in self._exported_columns_iterator()
+        ).as_immutable()
+
     @_generative
     def with_hint(self, text, selectable=None, dialect_name="*"):
         """Add a table hint for a single table to this
index 2b994c513dfd2f8733bf370232db89b39398a9ae..57d41b06fb84a11a628aebb39378f062358cabe0 100644 (file)
@@ -175,7 +175,7 @@ def not_(clause):
 
 @inspection._self_inspects
 class ClauseElement(
-    roles.SQLRole, SupportsWrappingAnnotations, HasCacheKey, Traversible
+    roles.SQLRole, SupportsWrappingAnnotations, HasCacheKey, Traversible,
 ):
     """Base class for elements of a programmatically constructed SQL
     expression.
@@ -215,10 +215,9 @@ class ClauseElement(
         the _copy_internals() method.
 
         """
+        skip = self._memoized_keys
         c = self.__class__.__new__(self.__class__)
-        c.__dict__ = self.__dict__.copy()
-        ClauseElement._cloned_set._reset(c)
-        ColumnElement.comparator._reset(c)
+        c.__dict__ = {k: v for k, v in self.__dict__.items() if k not in skip}
 
         # this is a marker that helps to "equate" clauses to each other
         # when a Select returns its list of FROM clauses.  the cloning
@@ -250,7 +249,7 @@ class ClauseElement(
         """
         return self.__class__
 
-    @util.memoized_property
+    @HasMemoized.memoized_attribute
     def _cloned_set(self):
         """Return the set consisting all cloned ancestors of this
         ClauseElement.
@@ -276,6 +275,7 @@ class ClauseElement(
     def __getstate__(self):
         d = self.__dict__.copy()
         d.pop("_is_clone_of", None)
+        d.pop("_generate_cache_key", None)
         return d
 
     def _execute_on_connection(self, connection, multiparams, params):
@@ -740,15 +740,7 @@ class ColumnElement(
     def type(self):
         return type_api.NULLTYPE
 
-    def _with_binary_element_type(self, type_):
-        cloned = self._clone()
-        cloned._copy_internals(
-            clone=lambda element: element._with_binary_element_type(type_)
-        )
-        cloned.type = type_
-        return cloned
-
-    @util.memoized_property
+    @HasMemoized.memoized_attribute
     def comparator(self):
         try:
             comparator_factory = self.type.comparator_factory
@@ -1022,6 +1014,7 @@ class BindParameter(roles.InElementRole, ColumnElement):
     _is_crud = False
     _expanding_in_types = ()
     _is_bind_parameter = True
+    _key_is_anon = False
 
     def __init__(
         self,
@@ -1273,9 +1266,6 @@ class BindParameter(roles.InElementRole, ColumnElement):
 
 
         """
-        if isinstance(key, ColumnClause):
-            type_ = key.type
-            key = key.key
         if required is NO_ARG:
             required = value is NO_ARG and callable_ is None
         if value is NO_ARG:
@@ -1297,8 +1287,12 @@ class BindParameter(roles.InElementRole, ColumnElement):
                     else "param",
                 )
             )
+            self._key_is_anon = True
+        elif key:
+            self.key = key
         else:
-            self.key = key or _anonymous_label("%%(%d param)s" % id(self))
+            self.key = _anonymous_label("%%(%d param)s" % id(self))
+            self._key_is_anon = True
 
         # identifying key that won't change across
         # clones, used to identify the bind's logical
@@ -1366,6 +1360,11 @@ class BindParameter(roles.InElementRole, ColumnElement):
         else:
             return self.value
 
+    def _with_binary_element_type(self, type_):
+        c = ClauseElement._clone(self)
+        c.type = type_
+        return c
+
     def _clone(self):
         c = ClauseElement._clone(self)
         if self.unique:
@@ -1390,7 +1389,7 @@ class BindParameter(roles.InElementRole, ColumnElement):
             id_,
             self.__class__,
             self.type._static_cache_key,
-            traversals._resolve_name_for_compare(self, self.key, anon_map),
+            self.key % anon_map if self._key_is_anon else self.key,
         )
 
     def _convert_to_unique(self):
@@ -2790,7 +2789,7 @@ class Cast(WrapsColumnExpression, ColumnElement):
         return self.clause
 
 
-class TypeCoerce(HasMemoized, WrapsColumnExpression, ColumnElement):
+class TypeCoerce(WrapsColumnExpression, ColumnElement):
     """Represent a Python-side type-coercion wrapper.
 
     :class:`.TypeCoerce` supplies the :func:`.expression.type_coerce`
@@ -2815,8 +2814,6 @@ class TypeCoerce(HasMemoized, WrapsColumnExpression, ColumnElement):
         ("type", InternalTraversal.dp_type),
     ]
 
-    _memoized_property = util.group_expirable_memoized_property()
-
     def __init__(self, expression, type_):
         r"""Associate a SQL expression with a particular type, without rendering
         ``CAST``.
@@ -2889,7 +2886,7 @@ class TypeCoerce(HasMemoized, WrapsColumnExpression, ColumnElement):
     def _from_objects(self):
         return self.clause._from_objects
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def typed_expression(self):
         if isinstance(self.clause, BindParameter):
             bp = self.clause._clone()
@@ -3435,7 +3432,7 @@ class BinaryExpression(ColumnElement):
         # refer to BinaryExpression directly and pass strings
         if isinstance(operator, util.string_types):
             operator = operators.custom_op(operator)
-        self._orig = (hash(left), hash(right))
+        self._orig = (left.__hash__(), right.__hash__())
         self.left = left.self_group(against=operator)
         self.right = right.self_group(against=operator)
         self.operator = operator
@@ -3450,7 +3447,7 @@ class BinaryExpression(ColumnElement):
 
     def __bool__(self):
         if self.operator in (operator.eq, operator.ne):
-            return self.operator(self._orig[0], self._orig[1])
+            return self.operator(*self._orig)
         else:
             raise TypeError("Boolean value of this clause is not defined")
 
@@ -3546,6 +3543,9 @@ class Grouping(GroupedElement, ColumnElement):
         self.element = element
         self.type = getattr(element, "type", type_api.NULLTYPE)
 
+    def _with_binary_element_type(self, type_):
+        return Grouping(self.element._with_binary_element_type(type_))
+
     @util.memoized_property
     def _is_implicitly_boolean(self):
         return self.element._is_implicitly_boolean
@@ -4015,7 +4015,7 @@ class FunctionFilter(ColumnElement):
         )
 
 
-class Label(HasMemoized, roles.LabeledColumnExprRole, ColumnElement):
+class Label(roles.LabeledColumnExprRole, ColumnElement):
     """Represents a column label (AS).
 
     Represent a label, as typically applied to any column-level
@@ -4031,8 +4031,6 @@ class Label(HasMemoized, roles.LabeledColumnExprRole, ColumnElement):
         ("_element", InternalTraversal.dp_clauseelement),
     ]
 
-    _memoized_property = util.group_expirable_memoized_property()
-
     def __init__(self, name, element, type_=None):
         """Return a :class:`Label` object for the
         given :class:`.ColumnElement`.
@@ -4075,7 +4073,7 @@ class Label(HasMemoized, roles.LabeledColumnExprRole, ColumnElement):
     def _is_implicitly_boolean(self):
         return self.element._is_implicitly_boolean
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def _allow_label_resolve(self):
         return self.element._allow_label_resolve
 
@@ -4089,7 +4087,7 @@ class Label(HasMemoized, roles.LabeledColumnExprRole, ColumnElement):
             self._type or getattr(self._element, "type", None)
         )
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def element(self):
         return self._element.self_group(against=operators.as_)
 
@@ -4116,7 +4114,6 @@ class Label(HasMemoized, roles.LabeledColumnExprRole, ColumnElement):
         return self.element.foreign_keys
 
     def _copy_internals(self, clone=_clone, anonymize_labels=False, **kw):
-        self._reset_memoizations()
         self._element = clone(self._element, **kw)
         if anonymize_labels:
             self.name = self._resolve_label = _anonymous_label(
@@ -4194,8 +4191,6 @@ class ColumnClause(
 
     _is_multiparam_column = False
 
-    _memoized_property = util.group_expirable_memoized_property()
-
     def __init__(self, text, type_=None, is_literal=False, _selectable=None):
         """Produce a :class:`.ColumnClause` object.
 
@@ -4312,7 +4307,7 @@ class ColumnClause(
         else:
             return []
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def _from_objects(self):
         t = self.table
         if t is not None:
@@ -4327,18 +4322,18 @@ class ColumnClause(
         else:
             return self.name.encode("ascii", "backslashreplace")
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def _key_label(self):
         if self.key != self.name:
             return self._gen_label(self.key)
         else:
             return self._label
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def _label(self):
         return self._gen_label(self.name)
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def _render_label_in_columns_clause(self):
         return self.table is not None
 
@@ -4599,14 +4594,14 @@ def _corresponding_column_or_error(fromclause, column, require_embedded=False):
 class AnnotatedColumnElement(Annotated):
     def __init__(self, element, values):
         Annotated.__init__(self, element, values)
-        ColumnElement.comparator._reset(self)
+        self.__dict__.pop("comparator", None)
         for attr in ("name", "key", "table"):
             if self.__dict__.get(attr, False) is None:
                 self.__dict__.pop(attr)
 
     def _with_annotations(self, values):
         clone = super(AnnotatedColumnElement, self)._with_annotations(values)
-        ColumnElement.comparator._reset(clone)
+        clone.__dict__.pop("comparator", None)
         return clone
 
     @util.memoized_property
index 6004f6b51ca024f408f6aaad9448c07600e40ba2..7973871f335b30ee270f45f6337a5c44bf365e92 100644 (file)
@@ -17,6 +17,7 @@ from . import sqltypes
 from . import util as sqlutil
 from .base import ColumnCollection
 from .base import Executable
+from .base import HasMemoized
 from .elements import _type_from_args
 from .elements import BinaryExpression
 from .elements import BindParameter
@@ -85,8 +86,6 @@ class FunctionElement(Executable, ColumnElement, FromClause):
 
     _has_args = False
 
-    _memoized_property = FromClause._memoized_property
-
     def __init__(self, *clauses, **kwargs):
         r"""Construct a :class:`.FunctionElement`.
 
@@ -141,7 +140,7 @@ class FunctionElement(Executable, ColumnElement, FromClause):
         col = self.label(None)
         return ColumnCollection(columns=[(col.key, col)])
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def clauses(self):
         """Return the underlying :class:`.ClauseList` which contains
         the arguments for this :class:`.FunctionElement`.
index 02c14d75132bb8f58b4a8b3bb9949a807ea67ed1..5c6b1f3c6e73ab64a1d3982ff2d79421e855f25f 100644 (file)
@@ -1412,7 +1412,7 @@ class Column(DialectKWArgs, SchemaItem, ColumnClause):
                 "assign a non-blank .name before adding to a Table."
             )
 
-        Column._memoized_property.expire_instance(self)
+        self._reset_memoizations()
 
         if self.key is None:
             self.key = self.name
index 4eab60801af71717b9de752aa37f6c74b341936d..e39d61fdbb4602afdf07a9511833959edc280e3a 100644 (file)
@@ -106,31 +106,33 @@ class ReturnsRows(roles.ReturnsRowsRole, ClauseElement):
     def selectable(self):
         raise NotImplementedError()
 
+    def _exported_columns_iterator(self):
+        """An iterator of column objects that represents the "exported"
+        columns of this :class:`.ReturnsRows`.
 
-class Selectable(ReturnsRows):
-    """mark a class as being selectable.
-
-    """
+        This is the same set of columns as are returned by
+        :meth:`.ReturnsRows.exported_columns` except they are returned
+        as a simple iterator or sequence, rather than as a
+        :class:`.ColumnCollection` namespace.
 
-    __visit_name__ = "selectable"
-
-    is_selectable = True
+        Subclasses should re-implement this method to bypass the interim
+        creation of the :class:`.ColumnCollection` if appropriate.
 
-    @property
-    def selectable(self):
-        return self
+        """
+        return iter(self.exported_columns)
 
     @property
     def exported_columns(self):
         """A :class:`.ColumnCollection` that represents the "exported"
-        columns of this :class:`.Selectable`.
+        columns of this :class:`.ReturnsRows`.
 
         The "exported" columns represent the collection of
         :class:`.ColumnElement` expressions that are rendered by this SQL
-        construct.   There are two primary varieties which are the
+        construct.   There are primary varieties which are the
         "FROM clause columns" of a FROM clause, such as a table, join,
-        or subquery, and the "SELECTed columns", which are the columns in
-        the "columns clause" of a SELECT statement.
+        or subquery, the "SELECTed columns", which are the columns in
+        the "columns clause" of a SELECT statement, and the RETURNING
+        columns in a DML statement..
 
         .. versionadded:: 1.4
 
@@ -143,6 +145,20 @@ class Selectable(ReturnsRows):
 
         raise NotImplementedError()
 
+
+class Selectable(ReturnsRows):
+    """mark a class as being selectable.
+
+    """
+
+    __visit_name__ = "selectable"
+
+    is_selectable = True
+
+    @property
+    def selectable(self):
+        return self
+
     def _refresh_for_new_column(self, column):
         raise NotImplementedError()
 
@@ -312,7 +328,7 @@ class HasSuffixes(object):
         )
 
 
-class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
+class FromClause(roles.AnonymizedFromClauseRole, Selectable):
     """Represent an element that can be used within the ``FROM``
     clause of a ``SELECT`` statement.
 
@@ -350,8 +366,6 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
 
     _use_schema_map = False
 
-    _memoized_property = util.group_expirable_memoized_property(["_columns"])
-
     @util.deprecated(
         "1.1",
         message="The :meth:`.FromClause.count` method is deprecated, "
@@ -571,7 +585,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
         """
         return self.columns
 
-    @_memoized_property
+    @util.memoized_property
     def columns(self):
         """A named-based collection of :class:`.ColumnElement` objects
         maintained by this :class:`.FromClause`.
@@ -589,7 +603,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
             self._populate_column_collection()
         return self._columns.as_immutable()
 
-    @_memoized_property
+    @util.memoized_property
     def primary_key(self):
         """Return the collection of Column objects which comprise the
         primary key of this FromClause."""
@@ -598,7 +612,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
         self._populate_column_collection()
         return self.primary_key
 
-    @_memoized_property
+    @util.memoized_property
     def foreign_keys(self):
         """Return the collection of ForeignKey objects which this
         FromClause references."""
@@ -607,6 +621,23 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
         self._populate_column_collection()
         return self.foreign_keys
 
+    def _reset_column_collection(self):
+        """Reset the attributes linked to the FromClause.c attribute.
+
+        This collection is separate from all the other memoized things
+        as it has shown to be sensitive to being cleared out in situations
+        where enclosing code, typically in a replacement traversal scenario,
+        has already established strong relationships
+        with the exported columns.
+
+        The collection is cleared for the case where a table is having a
+        column added to it as well as within a Join during copy internals.
+
+        """
+
+        for key in ["_columns", "columns", "primary_key", "foreign_keys"]:
+            self.__dict__.pop(key, None)
+
     c = property(
         attrgetter("columns"),
         doc="An alias for the :attr:`.columns` attribute.",
@@ -659,7 +690,7 @@ class FromClause(HasMemoized, roles.AnonymizedFromClauseRole, Selectable):
         derivations.
 
         """
-        self._reset_exported()
+        self._reset_column_collection()
 
 
 class Join(FromClause):
@@ -1239,7 +1270,7 @@ class AliasedReturnsRows(NoInit, FromClause):
         # same object.  don't reset exported .c. collections and other
         # memoized details if nothing changed
         if element is not self.element:
-            self._reset_exported()
+            self._reset_column_collection()
             self.element = element
 
     @property
@@ -2141,7 +2172,6 @@ class SelectBase(
     roles.DMLSelectRole,
     roles.CompoundElementRole,
     roles.InElementRole,
-    HasMemoized,
     HasCTE,
     Executable,
     SupportsCloneAnnotations,
@@ -2158,8 +2188,6 @@ class SelectBase(
 
     _is_select_statement = True
 
-    _memoized_property = util.group_expirable_memoized_property()
-
     def _generate_fromclause_column_proxies(self, fromclause):
         # type: (FromClause) -> None
         raise NotImplementedError()
@@ -2254,7 +2282,7 @@ class SelectBase(
     def outerjoin(self, *arg, **kw):
         return self._implicit_subquery.outerjoin(*arg, **kw)
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def _implicit_subquery(self):
         return self.subquery()
 
@@ -2315,15 +2343,6 @@ class SelectBase(
         """
         return Lateral._factory(self, name)
 
-    def _generate(self):
-        """Override the default _generate() method to also clear out
-        exported collections."""
-
-        s = self.__class__.__new__(self.__class__)
-        s.__dict__ = self.__dict__.copy()
-        s._reset_memoizations()
-        return s
-
     @property
     def _from_objects(self):
         return [self]
@@ -2431,6 +2450,9 @@ class SelectStatementGrouping(GroupedElement, SelectBase):
     def _generate_proxy_for_new_column(self, column, subquery):
         return self.element._generate_proxy_for_new_column(subquery)
 
+    def _exported_columns_iterator(self):
+        return self.element._exported_columns_iterator()
+
     @property
     def selected_columns(self):
         """A :class:`.ColumnCollection` representing the columns that
@@ -3046,6 +3068,9 @@ class CompoundSelect(HasCompileState, GenerativeSelect):
         for select in self.selects:
             select._refresh_for_new_column(column)
 
+    def _exported_columns_iterator(self):
+        return self.selects[0]._exported_columns_iterator()
+
     @property
     def selected_columns(self):
         """A :class:`.ColumnCollection` representing the columns that
@@ -3339,8 +3364,6 @@ class Select(
     _from_obj = ()
     _auto_correlate = True
 
-    _memoized_property = SelectBase._memoized_property
-
     _traverse_internals = (
         [
             ("_from_obj", InternalTraversal.dp_clauseelement_list),
@@ -3400,8 +3423,7 @@ class Select(
 
         self = cls.__new__(cls)
         self._raw_columns = [
-            coercions.expect(roles.ColumnsClauseRole, ent)
-            for ent in util.to_list(entities)
+            coercions.expect(roles.ColumnsClauseRole, ent) for ent in entities
         ]
 
         GenerativeSelect.__init__(self)
@@ -3739,8 +3761,12 @@ class Select(
         """an iterator of all ColumnElement expressions which would
         be rendered into the columns clause of the resulting SELECT statement.
 
+        This method is legacy as of 1.4 and is superseded by the
+        :attr:`.Select.exported_columns` collection.
+
         """
-        return _select_iterables(self._raw_columns)
+
+        return self._exported_columns_iterator()
 
     def is_derived_from(self, fromclause):
         if self in fromclause._cloned_set:
@@ -3786,7 +3812,10 @@ class Select(
             clone=clone, omit_attrs=("_from_obj",), **kw
         )
 
-        self._reset_memoizations()
+        # memoizations should be cleared here as of
+        # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this
+        # is the case for now.
+        self._assert_no_memoizations()
 
     def get_children(self, **kwargs):
         return list(set(self._iterate_from_elements())) + super(
@@ -3809,7 +3838,10 @@ class Select(
             :class:`.Select` object.
 
         """
-        self._reset_memoizations()
+        # memoizations should be cleared here as of
+        # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this
+        # is the case for now.
+        self._assert_no_memoizations()
 
         self._raw_columns = self._raw_columns + [
             coercions.expect(roles.ColumnsClauseRole, column,)
@@ -3861,7 +3893,7 @@ class Select(
         """
         return self.with_only_columns(
             util.preloaded.sql_util.reduce_columns(
-                self.inner_columns,
+                self._exported_columns_iterator(),
                 only_synonyms=only_synonyms,
                 *(self._where_criteria + self._from_obj)
             )
@@ -3935,7 +3967,12 @@ class Select(
         being asked to select both from ``table1`` as well as itself.
 
         """
-        self._reset_memoizations()
+
+        # memoizations should be cleared here as of
+        # I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this
+        # is the case for now.
+        self._assert_no_memoizations()
+
         rc = []
         for c in columns:
             c = coercions.expect(roles.ColumnsClauseRole, c,)
@@ -4112,7 +4149,7 @@ class Select(
                 coercions.expect(roles.FromClauseRole, f) for f in fromclauses
             )
 
-    @_memoized_property
+    @HasMemoized.memoized_attribute
     def selected_columns(self):
         """A :class:`.ColumnCollection` representing the columns that
         this SELECT statement or similar construct returns in its result set.
@@ -4167,6 +4204,9 @@ class Select(
 
         return ColumnCollection(collection).as_immutable()
 
+    def _exported_columns_iterator(self):
+        return _select_iterables(self._raw_columns)
+
     def _ensure_disambiguated_names(self):
         if self._label_style is LABEL_STYLE_NONE:
             self = self._set_label_style(LABEL_STYLE_DISAMBIGUATE_ONLY)
@@ -4558,7 +4598,7 @@ class TextualSelect(SelectBase):
         ]
         self.positional = positional
 
-    @SelectBase._memoized_property
+    @HasMemoized.memoized_attribute
     def selected_columns(self):
         """A :class:`.ColumnCollection` representing the columns that
         this SELECT statement or similar construct returns in its result set.
index 1fcc2d0233448bc7543700b6d7ca45daff457d8a..9ac6cda978025e689358753e5c09f84956fb3b95 100644 (file)
@@ -7,6 +7,7 @@ from .visitors import ExtendedInternalTraversal
 from .visitors import InternalTraversal
 from .. import util
 from ..inspection import inspect
+from ..util import HasMemoized
 
 SKIP_TRAVERSE = util.symbol("skip_traverse")
 COMPARE_FAILED = False
@@ -26,7 +27,7 @@ def compare(obj1, obj2, **kw):
     return strategy.compare(obj1, obj2, **kw)
 
 
-class HasCacheKey(object):
+class HasCacheKey(HasMemoized):
     _cache_key_traversal = NO_CACHE
 
     __slots__ = ()
@@ -105,6 +106,14 @@ class HasCacheKey(object):
                         attrname,
                         obj._gen_cache_key(anon_map, bindparams),
                     )
+                elif meth is InternalTraversal.dp_annotations_key:
+                    # obj is here is the _annotations dict.   however,
+                    # we want to use the memoized cache key version of it.
+                    # for Columns, this should be long lived.   For select()
+                    # statements, not so much, but they usually won't have
+                    # annotations.
+                    if obj:
+                        result += self._annotations_cache_key
                 elif meth is InternalTraversal.dp_clauseelement_list:
                     if obj:
                         result += (
@@ -130,6 +139,7 @@ class HasCacheKey(object):
 
         return result
 
+    @HasMemoized.memoized_instancemethod
     def _generate_cache_key(self):
         """return a cache key.
 
@@ -161,6 +171,7 @@ class HasCacheKey(object):
         will return None, indicating no cache key is available.
 
         """
+
         bindparams = []
 
         _anon_map = anon_map()
@@ -178,6 +189,36 @@ class CacheKey(namedtuple("CacheKey", ["key", "bindparams"])):
     def __eq__(self, other):
         return self.key == other.key
 
+    def __str__(self):
+        stack = [self.key]
+
+        output = []
+        sentinel = object()
+        indent = -1
+        while stack:
+            elem = stack.pop(0)
+            if elem is sentinel:
+                output.append((" " * (indent * 2)) + "),")
+                indent -= 1
+            elif isinstance(elem, tuple):
+                if not elem:
+                    output.append((" " * ((indent + 1) * 2)) + "()")
+                else:
+                    indent += 1
+                    stack = list(elem) + [sentinel] + stack
+                    output.append((" " * (indent * 2)) + "(")
+            else:
+                if isinstance(elem, HasCacheKey):
+                    repr_ = "<%s object at %s>" % (
+                        type(elem).__name__,
+                        hex(id(elem)),
+                    )
+                else:
+                    repr_ = repr(elem)
+                output.append((" " * (indent * 2)) + "  " + repr_ + ", ")
+
+        return "CacheKey(key=%s)" % ("\n".join(output),)
+
 
 def _clone(element, **kw):
     return element._clone()
@@ -189,6 +230,8 @@ class _CacheKey(ExtendedInternalTraversal):
 
     visit_has_cache_key = visit_clauseelement = CALL_GEN_CACHE_KEY
     visit_clauseelement_list = InternalTraversal.dp_clauseelement_list
+    visit_annotations_key = InternalTraversal.dp_annotations_key
+
     visit_string = (
         visit_boolean
     ) = visit_operator = visit_plain_obj = CACHE_IN_PLACE
@@ -690,8 +733,8 @@ class TraversalComparatorStrategy(InternalTraversal, util.MemoizedSlots):
                 fillvalue=(None, None),
             ):
                 if not compare_annotations and (
-                    (left_attrname == "_annotations_cache_key")
-                    or (right_attrname == "_annotations_cache_key")
+                    (left_attrname == "_annotations")
+                    or (right_attrname == "_annotations")
                 ):
                     continue
 
@@ -827,6 +870,17 @@ class TraversalComparatorStrategy(InternalTraversal, util.MemoizedSlots):
     ):
         return left == right
 
+    def visit_annotations_key(
+        self, left_parent, left, right_parent, right, **kw
+    ):
+        if left and right:
+            return (
+                left_parent._annotations_cache_key
+                == right_parent._annotations_cache_key
+            )
+        else:
+            return left == right
+
     def visit_plain_obj(self, left_parent, left, right_parent, right, **kw):
         return left == right
 
index 38189ec9d5b217ef97e0503a44bae693706efe5e..e3929fac7f0ba897384a57978456684de76fb945 100644 (file)
@@ -1414,6 +1414,17 @@ class Variant(TypeDecorator):
         self.impl = base
         self.mapping = mapping
 
+    @util.memoized_property
+    def _static_cache_key(self):
+        # TODO: needs tests in test/sql/test_compare.py
+        return (self.__class__,) + (
+            self.impl._static_cache_key,
+            tuple(
+                (key, self.mapping[key]._static_cache_key)
+                for key in sorted(self.mapping)
+            ),
+        )
+
     def coerce_compared_value(self, operator, value):
         result = self.impl.coerce_compared_value(operator, value)
         if result is self.impl:
index 8d185ce7d116b7c78dd48ce927bb4bf8b47e3212..fae68da98739878922d56e76e061db85c8438d6f 100644 (file)
@@ -584,7 +584,6 @@ def splice_joins(left, right, stop_on=None):
         (right, prevright) = stack.pop()
         if isinstance(right, Join) and right is not stop_on:
             right = right._clone()
-            right._reset_exported()
             right.onclause = adapter.traverse(right.onclause)
             stack.append((right.left, right))
         else:
index 4c1aab62fd20f892e11c29b21c63602226684a17..5504bf3d8cc9d4e1886ea307e43ed6b0d4ec3dd4 100644 (file)
@@ -331,6 +331,17 @@ class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
 
     """
 
+    dp_annotations_key = symbol("AK")
+    """Visit the _annotations_cache_key element.
+
+    This is a dictionary of additional information about a ClauseElement
+    that modifies its role.  It should be included when comparing or caching
+    objects, however generating this key is relatively expensive.   Visitors
+    should check the "_annotations" dict for non-None first before creating
+    this key.
+
+    """
+
     dp_plain_obj = symbol("PO")
     """Visit a plain python object.
 
index c0e290cb0e65f403582623addc35ab0d378da17c..695985a9105ab5f50e0da4515b9fa2b08e34853a 100644 (file)
@@ -119,7 +119,7 @@ from .langhelpers import get_callable_argspec  # noqa
 from .langhelpers import get_cls_kwargs  # noqa
 from .langhelpers import get_func_kwargs  # noqa
 from .langhelpers import getargspec_init  # noqa
-from .langhelpers import group_expirable_memoized_property  # noqa
+from .langhelpers import HasMemoized  # noqa
 from .langhelpers import hybridmethod  # noqa
 from .langhelpers import hybridproperty  # noqa
 from .langhelpers import iterate_attributes  # noqa
@@ -134,8 +134,8 @@ from .langhelpers import NoneType  # noqa
 from .langhelpers import only_once  # noqa
 from .langhelpers import PluginLoader  # noqa
 from .langhelpers import portable_instancemethod  # noqa
-from .langhelpers import preloaded  # noqa
 from .langhelpers import preload_module  # noqa
+from .langhelpers import preloaded  # noqa
 from .langhelpers import quoted_token_parser  # noqa
 from .langhelpers import safe_reraise  # noqa
 from .langhelpers import set_creation_order  # noqa
index 474463981544493aceddc9681f2b59249a1191c9..7e990602882bb0269c43da2db9561a6bda405736 100644 (file)
@@ -924,27 +924,56 @@ def memoized_instancemethod(fn):
     return update_wrapper(oneshot, fn)
 
 
-class group_expirable_memoized_property(object):
-    """A family of @memoized_properties that can be expired in tandem."""
-
-    def __init__(self, attributes=()):
-        self.attributes = []
-        if attributes:
-            self.attributes.extend(attributes)
-
-    def expire_instance(self, instance):
-        """Expire all memoized properties for *instance*."""
-        stash = instance.__dict__
-        for attribute in self.attributes:
-            stash.pop(attribute, None)
-
-    def __call__(self, fn):
-        self.attributes.append(fn.__name__)
-        return memoized_property(fn)
-
-    def method(self, fn):
-        self.attributes.append(fn.__name__)
-        return memoized_instancemethod(fn)
+class HasMemoized(object):
+    """A class that maintains the names of memoized elements in a
+    collection for easy cache clearing, generative, etc.
+
+    """
+
+    _memoized_keys = frozenset()
+
+    def _reset_memoizations(self):
+        for elem in self._memoized_keys:
+            self.__dict__.pop(elem, None)
+
+    def _assert_no_memoizations(self):
+        for elem in self._memoized_keys:
+            assert elem not in self.__dict__
+
+    class memoized_attribute(object):
+        """A read-only @property that is only evaluated once."""
+
+        def __init__(self, fget, doc=None):
+            self.fget = fget
+            self.__doc__ = doc or fget.__doc__
+            self.__name__ = fget.__name__
+
+        def __get__(self, obj, cls):
+            if obj is None:
+                return self
+            obj.__dict__[self.__name__] = result = self.fget(obj)
+            obj._memoized_keys |= {self.__name__}
+            return result
+
+    @classmethod
+    def memoized_instancemethod(cls, fn):
+        """Decorate a method memoize its return value.
+
+        """
+
+        def oneshot(self, *args, **kw):
+            result = fn(self, *args, **kw)
+
+            def memo(*a, **kw):
+                return result
+
+            memo.__name__ = fn.__name__
+            memo.__doc__ = fn.__doc__
+            self.__dict__[fn.__name__] = memo
+            self._memoized_keys |= {fn.__name__}
+            return result
+
+        return update_wrapper(oneshot, fn)
 
 
 class MemoizedSlots(object):
index 8ea59ac3be29d7cc09f8371ac30d01eaefc0d677..ad4db4545e689bbee8a3086db81b2cc4c711f96d 100644 (file)
@@ -1276,8 +1276,9 @@ class CycleTest(_fixtures.FixtureTest):
 
         s = Session()
 
-        # cycles here are due to ClauseElement._cloned_set
-        @assert_cycles(3)
+        # cycles here are due to ClauseElement._cloned_set, others
+        # as of cache key
+        @assert_cycles(4)
         def go():
             s.query(User).join(User.addresses).all()
 
@@ -1291,8 +1292,9 @@ class CycleTest(_fixtures.FixtureTest):
         def generate():
             s.query(User).options(joinedload(User.addresses)).all()
 
-        # cycles here are due to ClauseElement._cloned_set and Load.context
-        @assert_cycles(28)
+        # cycles here are due to ClauseElement._cloned_set and Load.context,
+        # others as of cache key
+        @assert_cycles(29)
         def go():
             generate()
 
@@ -1310,8 +1312,9 @@ class CycleTest(_fixtures.FixtureTest):
     def test_plain_join_select(self):
         users, addresses = self.tables("users", "addresses")
 
-        # cycles here are due to ClauseElement._cloned_set
-        @assert_cycles(6)
+        # cycles here are due to ClauseElement._cloned_set, others
+        # as of cache key
+        @assert_cycles(7)
         def go():
             s = select([users]).select_from(users.join(addresses))
             state = s._compile_state_factory(s, None)
index 8fd9e742bcc50cf9f6e5634a6ac9d04340167be4..32ab23f5e85cebf5baab2bf9f94b084559dc4f2b 100644 (file)
@@ -106,7 +106,17 @@ class CacheKeyTest(fixtures.TestBase):
         ]
 
     @profiling.function_call_count(variance=0.15, warmup=2)
-    def test_statement_one(self, stmt_fixture_one):
+    def test_statement_key_is_cached(self, stmt_fixture_one):
+        current_key = None
+        for stmt in stmt_fixture_one:
+            key = stmt._generate_cache_key()
+            if current_key:
+                eq_(key, current_key)
+            else:
+                current_key = key
+
+    @profiling.function_call_count(variance=0.15, warmup=0)
+    def test_statement_key_is_not_cached(self, stmt_fixture_one):
         current_key = None
         for stmt in stmt_fixture_one:
             key = stmt._generate_cache_key()
index faf0ec5332aa523d87df6dd17af7a6490cec2940..9906339c2b7c87d86413ce4c0b592666dc3a6af1 100644 (file)
@@ -1036,6 +1036,36 @@ class LazyLoaderTest(testing.AssertsCompiledSQL, BakedTest):
         mapper(Dingaling, self.tables.dingalings)
         return User, Address, Dingaling
 
+    def _o2m_threelevel_fixture(self, lazy="select", **kw):
+        Order = self.classes.Order
+        User = self.classes.User
+        Address = self.classes.Address
+        Dingaling = self.classes.Dingaling
+
+        mapper(
+            Order, self.tables.orders, properties={"user": relationship(User)}
+        )
+
+        mapper(
+            User,
+            self.tables.users,
+            properties={
+                "addresses": relationship(
+                    Address,
+                    order_by=self.tables.addresses.c.id,
+                    lazy=lazy,
+                    **kw
+                )
+            },
+        )
+        mapper(
+            Address,
+            self.tables.addresses,
+            properties={"dingalings": relationship(Dingaling, lazy=lazy)},
+        )
+        mapper(Dingaling, self.tables.dingalings)
+        return Order, User, Address, Dingaling
+
     def _m2o_fixture(self):
         User = self.classes.User
         Address = self.classes.Address
@@ -1048,6 +1078,61 @@ class LazyLoaderTest(testing.AssertsCompiledSQL, BakedTest):
         )
         return User, Address
 
+    def test_same_lazyload_multiple_paths(self):
+        """this is an initial test that requires the presence of
+        the ResultMetaData._adapt_to_context method.
+
+        Both queries emit a lazyload against User.addresses that then
+        includes a subqueryload against Address.dingalings.
+
+        The subqueryload produces an adapted query that has anonymized
+        columns.  for these to be locatable in the result, the columns in
+        the adapted query have to match up to the result map, which is not
+        the case if the statement is pulled from the cache.
+
+        The query is done in two different ways so that one lazyload will not
+        have a state.load_path on User, and the other one will. this means
+        there will be two different baked queries that both produce the same
+        Core SELECT statement. Using logical cache keys in Core, rather than
+        based on identity, means two different BakedContext objects will use
+        the same Core context.   For this reason, at result fetch time the
+        ResultMetaData._adapt_to_context step is required so that the ORM can
+        still locate the columns.
+
+        This will all be done differently with direct ORM results, however this
+        same issue that ResultMetaData has to be adapted to the live statement
+        will exist indepndently of baked queries.  More statement-level caching
+        tests need to be added which test with complex statements that include
+        anonymous columns.
+
+        """
+        Order, User, Address, Dingaling = self._o2m_threelevel_fixture()
+
+        sess = Session()
+        from sqlalchemy.orm import joinedload
+
+        o1 = (
+            sess.query(Order)
+            .options(
+                joinedload(Order.user)
+                .lazyload(User.addresses)
+                .subqueryload(Address.dingalings)
+            )
+            .filter(Order.id == 2)
+            .first()
+        )
+        assert o1.user.addresses[0].dingalings
+
+        sess.expire_all()
+
+        u1 = (
+            sess.query(User)
+            .options(lazyload(User.addresses).subqueryload(Address.dingalings))
+            .filter(User.id == 9)
+            .first()
+        )
+        assert u1.addresses[0].dingalings
+
     def test_no_cache_for_event(self, modify_query_fixture):
 
         m1 = modify_query_fixture(False)
index bceab386b26197d1c646b1e8614e31c2ac40afe1..9133d63011fe2290e3ca43903cfa261cc87f21c2 100644 (file)
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert
 
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_cextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_nocextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_cextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_nocextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_cextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_nocextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_cextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_cextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_cextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 64
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_select
 
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_cextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_nocextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_cextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_nocextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_cextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_nocextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_cextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_cextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_cextensions 174
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 174
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_cextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_nocextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_cextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_nocextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_cextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_nocextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_cextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_cextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_cextensions 170
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 170
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_select_labels
 
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mssql_pyodbc_dbapiunicode_cextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mssql_pyodbc_dbapiunicode_nocextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_dbapiunicode_cextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_dbapiunicode_nocextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_pymysql_dbapiunicode_cextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_pymysql_dbapiunicode_nocextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_dbapiunicode_cextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_dbapiunicode_cextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_dbapiunicode_cextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 182
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_cextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_nocextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_cextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_nocextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_cextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_nocextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_cextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_cextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_cextensions 197
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 197
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mssql_pyodbc_dbapiunicode_cextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mssql_pyodbc_dbapiunicode_nocextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_dbapiunicode_cextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_dbapiunicode_nocextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_pymysql_dbapiunicode_cextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_pymysql_dbapiunicode_nocextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_dbapiunicode_cextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_dbapiunicode_cextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_dbapiunicode_cextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 173
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_cextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_nocextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_cextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_nocextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_cextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_nocextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_cextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_cextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_cextensions 188
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 188
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_update
 
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_cextensions 70
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_nocextensions 70
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 72
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_cextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 64
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_cextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_cextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_cextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_cextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_cextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_cextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 67
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause
 
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mssql_pyodbc_dbapiunicode_cextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mssql_pyodbc_dbapiunicode_nocextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_dbapiunicode_cextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_dbapiunicode_nocextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_pymysql_dbapiunicode_cextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_pymysql_dbapiunicode_nocextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_cextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_dbapiunicode_cextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_dbapiunicode_cextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 154
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_cextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_nocextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_cextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_nocextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_cextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_nocextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_cextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_cextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_cextensions 162
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 162
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mssql_pyodbc_dbapiunicode_cextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mssql_pyodbc_dbapiunicode_nocextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_dbapiunicode_cextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_dbapiunicode_nocextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_pymysql_dbapiunicode_cextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_pymysql_dbapiunicode_nocextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_cextensions 150
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 150
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_dbapiunicode_cextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_dbapiunicode_cextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_cextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_nocextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_cextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_nocextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_cextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_nocextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_cextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_cextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_cextensions 154
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 154
+
+# TEST: test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_cached
+
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_cached 2.7_sqlite_pysqlite_dbapiunicode_cextensions 302
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_cached 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 302
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_cached 3.7_sqlite_pysqlite_dbapiunicode_cextensions 303
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_cached 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 303
+
+# TEST: test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_not_cached
+
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_not_cached 2.7_sqlite_pysqlite_dbapiunicode_cextensions 3702
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_not_cached 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 3702
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_not_cached 3.7_sqlite_pysqlite_dbapiunicode_cextensions 4292
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_not_cached 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 4292
 
 # TEST: test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one
 
-test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 2.7_sqlite_pysqlite_dbapiunicode_cextensions 4002
-test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 4002
-test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 3.7_sqlite_pysqlite_dbapiunicode_cextensions 4703
-test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 4703
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 2.7_sqlite_pysqlite_dbapiunicode_cextensions 302
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 302
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 3.7_sqlite_pysqlite_dbapiunicode_cextensions 303
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 303
 
 # TEST: test.aaa_profiling.test_misc.EnumTest.test_create_enum_from_pep_435_w_expensive_members
 
@@ -162,66 +176,66 @@ test.aaa_profiling.test_misc.EnumTest.test_create_enum_from_pep_435_w_expensive_
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 48505
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 64205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 50405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 64205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 47405
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 63105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 49305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 63105
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 48005
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 63705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 49905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 63705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 62605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48805
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 62605
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45805
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 58205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 47205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 57805
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 58705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 47705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 57605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 46605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 57205
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_cextensions 39705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 45805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_cextensions 42205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 48605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_cextensions 38605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 44705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_cextensions 41105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 47505
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45805
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 58205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 47205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 57805
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 58705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 47705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 57605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 46605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 57205
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 26705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 30505
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 28805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 32705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 25905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 29705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 28105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 32005
 
 # TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 26105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 29905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 28205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 32105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 25305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 29105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 27505
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 31405
 
 # TEST: test.aaa_profiling.test_orm.AttributeOverheadTest.test_attribute_set
 
@@ -253,38 +267,38 @@ test.aaa_profiling.test_orm.BranchedOptionTest.test_generate_path_cache_key_unbo
 
 # TEST: test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching
 
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 45
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 58
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 46
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 59
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_key_bound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 59
 
 # TEST: test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching
 
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 388
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 388
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 394
-test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 394
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_cextensions 389
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 389
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_cextensions 395
+test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 395
 
 # TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline
 
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_cextensions 17142
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 38148
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.7_sqlite_pysqlite_dbapiunicode_cextensions 17177
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 38186
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_cextensions 17117
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 38123
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.7_sqlite_pysqlite_dbapiunicode_cextensions 17147
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 38156
 
 # TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols
 
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 23248
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 32254
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.7_sqlite_pysqlite_dbapiunicode_cextensions 23290
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 32299
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 23244
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 32250
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.7_sqlite_pysqlite_dbapiunicode_cextensions 23287
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 32296
 
 # TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased
 
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 9903
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 9903
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.7_sqlite_pysqlite_dbapiunicode_cextensions 10354
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 10354
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 9303
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 9303
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.7_sqlite_pysqlite_dbapiunicode_cextensions 9754
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 9754
 
 # TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain
 
@@ -295,31 +309,31 @@ test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 3.7_sqlite_pysql
 
 # TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d
 
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_cextensions 95538
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 95738
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_cextensions 103474
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 103674
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_cextensions 92688
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 92888
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_cextensions 100789
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 100789
 
 # TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased
 
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 93138
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 93338
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_cextensions 101339
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 101539
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 90738
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 90938
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_cextensions 99139
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 99139
 
 # TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query
 
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_cextensions 457606
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 457611
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_cextensions 488272
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 488272
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_cextensions 443111
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 443111
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_cextensions 473672
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 473672
 
 # TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results
 
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 429097
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 459697
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.7_sqlite_pysqlite_dbapiunicode_cextensions 434107
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 465007
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 446748
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 475548
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.7_sqlite_pysqlite_dbapiunicode_cextensions 452057
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 482357
 
 # TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity
 
@@ -330,24 +344,24 @@ test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_
 
 # TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity
 
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_cextensions 89508
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 93210
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.7_sqlite_pysqlite_dbapiunicode_cextensions 91785
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 95789
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_cextensions 90536
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 94288
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.7_sqlite_pysqlite_dbapiunicode_cextensions 93114
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 97118
 
 # TEST: test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks
 
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_cextensions 18394
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 18860
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.7_sqlite_pysqlite_dbapiunicode_cextensions 19072
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 19626
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_cextensions 18487
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 18959
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.7_sqlite_pysqlite_dbapiunicode_cextensions 19168
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 19720
 
 # TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_load
 
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1014
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1048
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiunicode_cextensions 1054
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 1092
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1007
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1041
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiunicode_cextensions 1047
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 1085
 
 # TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_no_load
 
@@ -358,24 +372,24 @@ test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.7_sqlite_pysqlite_dba
 
 # TEST: test.aaa_profiling.test_orm.QueryTest.test_query_cols
 
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 5694
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6974
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_sqlite_pysqlite_dbapiunicode_cextensions 5922
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 7212
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 5584
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6864
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_sqlite_pysqlite_dbapiunicode_cextensions 5792
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 7082
 
 # TEST: test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results
 
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 172195
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 193499
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.7_sqlite_pysqlite_dbapiunicode_cextensions 177018
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 196826
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 172456
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 194360
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.7_sqlite_pysqlite_dbapiunicode_cextensions 177281
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 197689
 
 # TEST: test.aaa_profiling.test_orm.SessionTest.test_expire_lots
 
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1162
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1161
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.7_sqlite_pysqlite_dbapiunicode_cextensions 1261
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 1273
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1123
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1151
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.7_sqlite_pysqlite_dbapiunicode_cextensions 1257
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 1269
 
 # TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect
 
@@ -474,122 +488,122 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7
 
 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mssql_pyodbc_dbapiunicode_cextensions 275
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6277
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 317
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6339
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122273
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 128275
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 384
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36426
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 288
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6310
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 256
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6278
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mssql_pyodbc_dbapiunicode_cextensions 260
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6264
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 293
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6297
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_pymysql_dbapiunicode_cextensions 88048
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_pymysql_dbapiunicode_nocextensions 94052
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_oracle_cx_oracle_dbapiunicode_cextensions 349
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6353
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_postgresql_psycopg2_dbapiunicode_cextensions 284
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6288
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_sqlite_pysqlite_dbapiunicode_cextensions 250
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6254
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mssql_pyodbc_dbapiunicode_cextensions 274
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6276
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 316
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6338
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122272
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 128274
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 383
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36425
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 287
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6309
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 255
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6277
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mssql_pyodbc_dbapiunicode_cextensions 259
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6263
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 292
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6296
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_pymysql_dbapiunicode_cextensions 88047
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_pymysql_dbapiunicode_nocextensions 94051
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_oracle_cx_oracle_dbapiunicode_cextensions 348
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6352
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_postgresql_psycopg2_dbapiunicode_cextensions 283
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6287
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_sqlite_pysqlite_dbapiunicode_cextensions 249
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6253
 
 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_cextensions 275
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6277
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 317
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6339
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122273
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 128275
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 384
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36426
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 288
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6310
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 256
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6278
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mssql_pyodbc_dbapiunicode_cextensions 260
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6264
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 293
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6297
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_pymysql_dbapiunicode_cextensions 88048
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_pymysql_dbapiunicode_nocextensions 94052
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_oracle_cx_oracle_dbapiunicode_cextensions 349
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6353
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_postgresql_psycopg2_dbapiunicode_cextensions 284
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6288
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_sqlite_pysqlite_dbapiunicode_cextensions 250
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6254
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_cextensions 274
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6276
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 316
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6338
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122272
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 128274
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 383
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36425
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 287
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6309
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 255
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6277
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mssql_pyodbc_dbapiunicode_cextensions 259
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6263
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 292
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6296
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_pymysql_dbapiunicode_cextensions 88047
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_pymysql_dbapiunicode_nocextensions 94051
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_oracle_cx_oracle_dbapiunicode_cextensions 348
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6352
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_postgresql_psycopg2_dbapiunicode_cextensions 283
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6287
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_sqlite_pysqlite_dbapiunicode_cextensions 249
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6253
 
 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_string
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_cextensions 517
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6519
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 522
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6524
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122488
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 128490
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 548
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36570
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 492
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6494
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 446
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6448
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mssql_pyodbc_dbapiunicode_cextensions 518
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6522
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 523
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6527
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_pymysql_dbapiunicode_cextensions 88278
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_pymysql_dbapiunicode_nocextensions 94282
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_oracle_cx_oracle_dbapiunicode_cextensions 548
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6552
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_postgresql_psycopg2_dbapiunicode_cextensions 513
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6517
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_sqlite_pysqlite_dbapiunicode_cextensions 465
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6469
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_cextensions 524
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6526
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 529
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6531
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122495
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 128497
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 555
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36577
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 499
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6501
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 453
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6455
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mssql_pyodbc_dbapiunicode_cextensions 528
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6532
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 533
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6537
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_pymysql_dbapiunicode_cextensions 88288
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_pymysql_dbapiunicode_nocextensions 94292
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_oracle_cx_oracle_dbapiunicode_cextensions 558
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6562
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_postgresql_psycopg2_dbapiunicode_cextensions 523
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6527
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_sqlite_pysqlite_dbapiunicode_cextensions 475
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6479
 
 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_unicode
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_cextensions 517
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6519
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 522
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6524
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122488
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 128490
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 548
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36570
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 492
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6494
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 446
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6448
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mssql_pyodbc_dbapiunicode_cextensions 518
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6522
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 523
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6527
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_pymysql_dbapiunicode_cextensions 88278
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_pymysql_dbapiunicode_nocextensions 94282
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_oracle_cx_oracle_dbapiunicode_cextensions 548
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6552
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_postgresql_psycopg2_dbapiunicode_cextensions 513
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6517
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_sqlite_pysqlite_dbapiunicode_cextensions 465
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6469
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_cextensions 524
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6526
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 529
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6531
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122495
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 128497
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 555
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36577
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 499
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6501
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 453
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6455
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mssql_pyodbc_dbapiunicode_cextensions 528
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6532
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 533
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6537
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_pymysql_dbapiunicode_cextensions 88288
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_pymysql_dbapiunicode_nocextensions 94292
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_oracle_cx_oracle_dbapiunicode_cextensions 558
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6562
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_postgresql_psycopg2_dbapiunicode_cextensions 523
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6527
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_sqlite_pysqlite_dbapiunicode_cextensions 475
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6479
 
 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation
 
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_cextensions 6462,328,4212,12379,1308,2185,2749
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6500,328,4276,12943,1430,2201,2937
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_cextensions 6307,311,4188,12346,1299,2142,2798
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6384,311,4268,12954,1430,2164,2992
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_cextensions 6454,327,4284,12623,1310,2206,2716
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6509,327,4348,13185,1433,2222,2900
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_cextensions 6244,310,4276,12644,1310,2163,2774
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6321,310,4356,13252,1441,2185,2968
 
 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation
 
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_cextensions 7085,440,6993,18134,1321,2795
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 7262,450,7225,20437,1433,2853
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_cextensions 7124,432,7213,18746,1313,2876
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 7272,440,7469,21112,1434,2944
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_cextensions 7054,442,6906,17933,1323,2823
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 7230,452,7138,20236,1436,2882
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_cextensions 7093,434,7126,18551,1324,2910
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 7241,442,7382,20917,1445,2978
index ab612053ebb170544930079b9f0560c6e47f40e2..2800f8248c823ca653226b62cd4b8cb62b6517fc 100644 (file)
@@ -61,6 +61,7 @@ from sqlalchemy.sql.visitors import InternalTraversal
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import is_false
+from sqlalchemy.testing import is_not_
 from sqlalchemy.testing import is_true
 from sqlalchemy.testing import ne_
 from sqlalchemy.testing.util import random_choices
@@ -174,6 +175,14 @@ class CoreFixtures(object):
             table_a.c.a._annotate(
                 {"orm": True, "parententity": MyEntity("b", select([table_a]))}
             ),
+            table_a.c.a._annotate(
+                {
+                    "orm": True,
+                    "parententity": MyEntity(
+                        "b", select([table_a]).where(table_a.c.a == 5)
+                    ),
+                }
+            ),
         ),
         lambda: (
             table_a,
@@ -759,6 +768,43 @@ class CacheKeyTest(CacheKeyFixture, CoreFixtures, fixtures.TestBase):
         f1._copy_internals()
         f2._copy_internals()
 
+    def test_generative_cache_key_regen(self):
+        t1 = table("t1", column("a"), column("b"))
+
+        s1 = select([t1])
+
+        ck1 = s1._generate_cache_key()
+
+        s2 = s1.where(t1.c.a == 5)
+
+        ck2 = s2._generate_cache_key()
+
+        ne_(ck1, ck2)
+        is_not_(ck1, None)
+        is_not_(ck2, None)
+
+    def test_generative_cache_key_regen_w_del(self):
+        t1 = table("t1", column("a"), column("b"))
+
+        s1 = select([t1])
+
+        ck1 = s1._generate_cache_key()
+
+        s2 = s1.where(t1.c.a == 5)
+
+        del s1
+
+        # there is now a good chance that id(s3) == id(s1), make sure
+        # cache key is regenerated
+
+        s3 = s2.order_by(t1.c.b)
+
+        ck3 = s3._generate_cache_key()
+
+        ne_(ck1, ck3)
+        is_not_(ck1, None)
+        is_not_(ck3, None)
+
 
 class CompareAndCopyTest(CoreFixtures, fixtures.TestBase):
     @classmethod
index 151ecb1d2527df53343bd4e855a261d122b6d341..e44deed90d5fa47570420cd189b71131e33d149f 100644 (file)
@@ -3344,6 +3344,116 @@ class BindParameterTest(AssertsCompiledSQL, fixtures.TestBase):
             _group_number=2,
         )
 
+    @testing.combinations(
+        (
+            select([table1]).where(table1.c.myid == 5),
+            select([table1]).where(table1.c.myid == 10),
+            {"myid_1": 5},
+            {"myid_1": 10},
+            None,
+            None,
+        ),
+        (
+            select([table1]).where(
+                table1.c.myid
+                == bindparam(None, unique=True, callable_=lambda: 5)
+            ),
+            select([table1]).where(
+                table1.c.myid
+                == bindparam(None, unique=True, callable_=lambda: 10)
+            ),
+            {"param_1": 5},
+            {"param_1": 10},
+            None,
+            None,
+        ),
+        (
+            table1.update()
+            .where(table1.c.myid == 5)
+            .values(name="n1", description="d1"),
+            table1.update()
+            .where(table1.c.myid == 10)
+            .values(name="n2", description="d2"),
+            {"description": "d1", "myid_1": 5, "name": "n1"},
+            {"description": "d2", "myid_1": 10, "name": "n2"},
+            None,
+            None,
+        ),
+        (
+            table1.update().where(table1.c.myid == 5),
+            table1.update().where(table1.c.myid == 10),
+            {"description": "d1", "myid_1": 5, "name": "n1"},
+            {"description": "d2", "myid_1": 10, "name": "n2"},
+            {"description": "d1", "name": "n1"},
+            {"description": "d2", "name": "n2"},
+        ),
+        (
+            table1.update().where(
+                table1.c.myid
+                == bindparam(None, unique=True, callable_=lambda: 5)
+            ),
+            table1.update().where(
+                table1.c.myid
+                == bindparam(None, unique=True, callable_=lambda: 10)
+            ),
+            {"description": "d1", "param_1": 5, "name": "n1"},
+            {"description": "d2", "param_1": 10, "name": "n2"},
+            {"description": "d1", "name": "n1"},
+            {"description": "d2", "name": "n2"},
+        ),
+        (
+            union(
+                select([table1]).where(table1.c.myid == 5),
+                select([table1]).where(table1.c.myid == 12),
+            ),
+            union(
+                select([table1]).where(table1.c.myid == 5),
+                select([table1]).where(table1.c.myid == 15),
+            ),
+            {"myid_1": 5, "myid_2": 12},
+            {"myid_1": 5, "myid_2": 15},
+            None,
+            None,
+        ),
+    )
+    def test_construct_params_combine_extracted(
+        self, stmt1, stmt2, param1, param2, extparam1, extparam2
+    ):
+
+        if extparam1:
+            keys = list(extparam1)
+        else:
+            keys = []
+
+        s1_cache_key = stmt1._generate_cache_key()
+        s1_compiled = stmt1.compile(cache_key=s1_cache_key, column_keys=keys)
+
+        s2_cache_key = stmt2._generate_cache_key()
+
+        eq_(s1_compiled.construct_params(params=extparam1), param1)
+        eq_(
+            s1_compiled.construct_params(
+                params=extparam1, extracted_parameters=s1_cache_key[1]
+            ),
+            param1,
+        )
+
+        eq_(
+            s1_compiled.construct_params(
+                params=extparam2, extracted_parameters=s2_cache_key[1]
+            ),
+            param2,
+        )
+
+        s1_compiled_no_cache_key = stmt1.compile()
+        assert_raises_message(
+            exc.CompileError,
+            "This compiled object has no original cache key; can't pass "
+            "extracted_parameters to construct_params",
+            s1_compiled_no_cache_key.construct_params,
+            extracted_parameters=s1_cache_key[1],
+        )
+
     def test_tuple_expanding_in_no_values(self):
         expr = tuple_(table1.c.myid, table1.c.name).in_(
             [(1, "foo"), (5, "bar")]
@@ -5021,7 +5131,7 @@ class ResultMapTest(fixtures.TestBase):
         stmt = select([t.c.x, t.c.y, l1, t.c.y, l2, t.c.x, l3])
 
         # so the statement has 7 inner columns...
-        eq_(len(list(stmt.inner_columns)), 7)
+        eq_(len(list(stmt.selected_columns)), 7)
 
         # 7 are exposed as of 1.4, no more deduping
         eq_(len(stmt.subquery().c), 7)
index ac423931fdea092b25052f38f81fbeb5806cba30..37fb752fe1c1bb7e6d6282d9c605e96e22366469 100644 (file)
@@ -172,7 +172,7 @@ class TraversalTest(fixtures.TestBase, AssertsExecutionResults):
 
         vis = Vis()
         s2 = vis.traverse(s1)
-        eq_(list(s2.inner_columns)[0].anon_label, c1.anon_label)
+        eq_(list(s2.selected_columns)[0].anon_label, c1.anon_label)
 
     def test_change_in_place(self):
         struct = B(
index fd3f557803d39091c789acceeb708d039f4a7968..5a6e6252b4c92bd23d6510f17ca6a8e2ae327cff 100644 (file)
@@ -468,9 +468,12 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
             "my_func(:my_func_1, :my_func_2, NULL, :my_func_3)",
         )
 
+        f1 = func.my_func(1, 2, None, 3)
+        f1._generate_cache_key()
+
         # test pickling
         self.assert_compile(
-            util.pickle.loads(util.pickle.dumps(func.my_func(1, 2, None, 3))),
+            util.pickle.loads(util.pickle.dumps(f1)),
             "my_func(:my_func_1, :my_func_2, NULL, :my_func_3)",
         )
 
index f08248440b6e5430eebeea3651e33436f478b3d0..253ad7b38e21c01e76690c71ef8f7ba16e73daf3 100644 (file)
@@ -29,9 +29,11 @@ from sqlalchemy.engine import default
 from sqlalchemy.engine import result as _result
 from sqlalchemy.engine import Row
 from sqlalchemy.ext.compiler import compiles
+from sqlalchemy.sql import ColumnElement
 from sqlalchemy.sql import expression
 from sqlalchemy.sql.selectable import TextualSelect
 from sqlalchemy.sql.sqltypes import NULLTYPE
+from sqlalchemy.sql.util import ClauseAdapter
 from sqlalchemy.testing import assert_raises
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.testing import assertions
@@ -1391,16 +1393,20 @@ class KeyTargetingTest(fixtures.TablesTest):
 
     @classmethod
     def insert_data(cls):
-        cls.tables.keyed1.insert().execute(dict(b="a1", q="c1"))
-        cls.tables.keyed2.insert().execute(dict(a="a2", b="b2"))
-        cls.tables.keyed3.insert().execute(dict(a="a3", d="d3"))
-        cls.tables.keyed4.insert().execute(dict(b="b4", q="q4"))
-        cls.tables.content.insert().execute(type="t1")
-
-        if testing.requires.schemas.enabled:
-            cls.tables[
-                "%s.wschema" % testing.config.test_schema
-            ].insert().execute(dict(b="a1", q="c1"))
+        with testing.db.begin() as conn:
+            conn.execute(cls.tables.keyed1.insert(), dict(b="a1", q="c1"))
+            conn.execute(cls.tables.keyed2.insert(), dict(a="a2", b="b2"))
+            conn.execute(cls.tables.keyed3.insert(), dict(a="a3", d="d3"))
+            conn.execute(cls.tables.keyed4.insert(), dict(b="b4", q="q4"))
+            conn.execute(cls.tables.content.insert(), dict(type="t1"))
+
+            if testing.requires.schemas.enabled:
+                conn.execute(
+                    cls.tables[
+                        "%s.wschema" % testing.config.test_schema
+                    ].insert(),
+                    dict(b="a1", q="c1"),
+                )
 
     @testing.requires.schemas
     def test_keyed_accessor_wschema(self):
@@ -1712,6 +1718,87 @@ class KeyTargetingTest(fixtures.TablesTest):
         in_(stmt.selected_columns.keyed2_a, row._mapping)
         in_(stmt.selected_columns.keyed2_b, row._mapping)
 
+    def _adapt_result_columns_fixture_one(self):
+        keyed1 = self.tables.keyed1
+        stmt = (
+            select([keyed1.c.b, keyed1.c.q.label("foo")])
+            .apply_labels()
+            .subquery()
+        )
+
+        return select([stmt.c.keyed1_b, stmt.c.foo])
+
+    def _adapt_result_columns_fixture_two(self):
+        return text("select a AS keyed2_a, b AS keyed2_b from keyed2").columns(
+            keyed2_a=CHAR, keyed2_b=CHAR
+        )
+
+    def _adapt_result_columns_fixture_three(self):
+        keyed1 = self.tables.keyed1
+        stmt = select([keyed1.c.b, keyed1.c.q.label("foo")]).subquery()
+
+        return select([stmt.c.b, stmt.c.foo])
+
+    def _adapt_result_columns_fixture_four(self):
+        keyed1 = self.tables.keyed1
+
+        stmt1 = select([keyed1]).apply_labels()
+
+        a1 = keyed1.alias()
+        stmt2 = ClauseAdapter(a1).traverse(stmt1)
+
+        return stmt2
+
+    @testing.combinations(
+        _adapt_result_columns_fixture_one,
+        _adapt_result_columns_fixture_two,
+        _adapt_result_columns_fixture_three,
+        _adapt_result_columns_fixture_four,
+        argnames="stmt_fn",
+    )
+    def test_adapt_result_columns(self, connection, stmt_fn):
+        """test adaptation of a CursorResultMetadata to another one.
+
+
+        This copies the _keymap from one to the other in terms of the
+        selected columns of a target selectable.
+
+        This is used by the statement caching process to re-use the
+        CursorResultMetadata from the cached statement against the same
+        statement sent separately.
+
+        """
+
+        stmt1 = stmt_fn(self)
+        stmt2 = stmt_fn(self)
+
+        eq_(stmt1._generate_cache_key(), stmt2._generate_cache_key())
+
+        column_linkage = dict(
+            zip(stmt1.selected_columns, stmt2.selected_columns)
+        )
+
+        result = connection.execute(stmt1)
+
+        mock_context = Mock(
+            compiled=result.context.compiled, invoked_statement=stmt2
+        )
+        existing_metadata = result._metadata
+        adapted_metadata = existing_metadata._adapt_to_context(mock_context)
+
+        eq_(existing_metadata.keys, adapted_metadata.keys)
+
+        for k in existing_metadata._keymap:
+            if isinstance(k, ColumnElement) and k in column_linkage:
+                other_k = column_linkage[k]
+            else:
+                other_k = k
+
+            is_(
+                existing_metadata._keymap[k], adapted_metadata._keymap[other_k]
+            )
+        return stmt1, existing_metadata, stmt2, adapted_metadata
+
 
 class PositionalTextTest(fixtures.TablesTest):
     run_inserts = "once"
index 58e7ee6a175945a8717e8b2e033ddc6c4f33f012..8fb1f29510e4be9aeecc3156e1e8763fa3dfa9b9 100644 (file)
@@ -1973,10 +1973,10 @@ class ReduceTest(fixtures.TestBase, AssertsExecutionResults):
         )
         s1 = select([t1, t2])
         s2 = s1.reduce_columns(only_synonyms=False)
-        eq_(set(s2.inner_columns), set([t1.c.x, t1.c.y, t2.c.q]))
+        eq_(set(s2.selected_columns), set([t1.c.x, t1.c.y, t2.c.q]))
 
         s2 = s1.reduce_columns()
-        eq_(set(s2.inner_columns), set([t1.c.x, t1.c.y, t2.c.z, t2.c.q]))
+        eq_(set(s2.selected_columns), set([t1.c.x, t1.c.y, t2.c.z, t2.c.q]))
 
     def test_reduce_only_synonym_fk(self):
         m = MetaData()
@@ -2019,7 +2019,7 @@ class ReduceTest(fixtures.TestBase, AssertsExecutionResults):
         s1 = select([t1]).subquery()
         s2 = select([t1, s1]).where(t1.c.x == s1.c.x).where(s1.c.y == t1.c.z)
         eq_(
-            set(s2.reduce_columns().inner_columns),
+            set(s2.reduce_columns().selected_columns),
             set([t1.c.x, t1.c.y, t1.c.z, s1.c.y, s1.c.z]),
         )
 
@@ -2027,7 +2027,7 @@ class ReduceTest(fixtures.TestBase, AssertsExecutionResults):
         s1 = select([t1]).subquery()
         s2 = select([s1, t1]).where(t1.c.x == s1.c.x).where(s1.c.y == t1.c.z)
         eq_(
-            set(s2.reduce_columns().inner_columns),
+            set(s2.reduce_columns().selected_columns),
             set([s1.c.x, t1.c.y, t1.c.z, s1.c.y, s1.c.z]),
         )