]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Decouple compiler state from DML objects; make cacheable
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 23 Feb 2020 18:37:18 +0000 (13:37 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 6 Mar 2020 16:01:51 +0000 (11:01 -0500)
Targeting select / insert / update / delete, the goal
is to minimize overhead of construction and generative methods
so that only the raw arguments passed are handled.   An interim
stage that converts the raw state into more compiler-ready state
is added, which is analogous to the ORM QueryContext which will
also be rolled in to be a similar concept, as is currently
being prototyped in I19e05b3424b07114cce6c439b05198ac47f7ac10.
the ORM update/delete BulkUD concept is also going to be rolled
onto this idea.   So while the compiler-ready state object,
here called DMLState, looks a little thin, it's the
base of a bigger pattern that will allow for ORM functionality
to embed itself directly into the compiler, execution
context, and result set objects.

This change targets the DML objects, primarily focused on the
values() method which is the most complex process.   The
work done by values() is minimized as much as possible
while still being able to create a cache key.   Additional
computation is then offloaded to a new object ValuesState
that is handled by the compiler.

Architecturally, a big change here is that insert.values()
and update.values() will generate BindParameter objects for
the values now, which are then carefully received by crud.py
so that they generate the expected names.   This is so that
the values() portion of these constructs is cacheable.
for the "multi-values" version of Insert, this is all skipped
and the plan right now is that a multi-values insert is
not worth caching (can always be revisited).

Using the
coercions system in values() also gets us nicer validation
for free, we can remove the NotAClauseElement thing from
schema, and we also now require scalar_subquery() is called
for an insert/update that uses a SELECT as a column value,
1.x deprecation path is added.

The traversal system is then applied to the DML objects
including tests so that they have traversal, cloning, and
cache key support.  cloning is not a use case for DML however
having it present allows better validation of the structure
within the tests.

Special per-dialect DML is explicitly not cacheable at the moment,
more as a proof of concept that third party DML constructs can
exist as gracefully not-cacheable rather than producing an
incomplete cache key.

A few selected performance improvements have been added as well,
simplifying the immutabledict.union() method and adding
a new SQLCompiler function that can generate delimeter-separated
clauses like WHERE and ORDER BY without having to build
a ClauseList object at all.   The use of ClauseList will
be removed from Select in an upcoming commit.  Overall,
ClaustList is unnecessary for internal use and only adds
overhead to statement construction and will likely be removed
as much as possible except for explcit use of conjunctions like
and_() and or_().

Change-Id: I408e0b8be91fddd77cf279da97f55020871f75a9

26 files changed:
doc/build/core/tutorial.rst
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/engine/default.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/schema.py
lib/sqlalchemy/sql/selectable.py
lib/sqlalchemy/sql/traversals.py
lib/sqlalchemy/sql/visitors.py
lib/sqlalchemy/util/_collections.py
test/dialect/mysql/test_compiler.py
test/dialect/postgresql/test_dialect.py
test/orm/test_unitofwork.py
test/orm/test_update_delete.py
test/profiles.txt
test/sql/test_compare.py
test/sql/test_compiler.py
test/sql/test_defaults.py
test/sql/test_deprecations.py
test/sql/test_external_traversal.py
test/sql/test_insert.py
test/sql/test_update.py

index 89316bcb9aeb6834f14c38e2751e1052e88ff240..1ce9a3e0686b340d55223484bb105216136e3387 100644 (file)
@@ -2193,14 +2193,15 @@ Correlated Updates
 ------------------
 
 A correlated update lets you update a table using selection from another
-table, or the same table:
+table, or the same table; the SELECT statement is passed as a scalar
+subquery using :meth:`.Select.scalar_subquery`:
 
 .. sourcecode:: pycon+sql
 
     >>> stmt = select([addresses.c.email_address]).\
     ...             where(addresses.c.user_id == users.c.id).\
     ...             limit(1)
-    >>> conn.execute(users.update().values(fullname=stmt))
+    >>> conn.execute(users.update().values(fullname=stmt.scalar_subquery()))
     {opensql}UPDATE users SET fullname=(SELECT addresses.email_address
         FROM addresses
         WHERE addresses.user_id = users.id
index a3855cc2c0eb86a10d8320ac6c13ee17bcd4c43f..955a0f23b133e37271537216e5391a25555b9dac 100644 (file)
@@ -1450,31 +1450,17 @@ class MSExecutionContext(default.DefaultExecutionContext):
             insert_has_sequence = seq_column is not None
 
             if insert_has_sequence:
+                compile_state = self.compiled.compile_state
                 self._enable_identity_insert = (
                     seq_column.key in self.compiled_parameters[0]
                 ) or (
-                    self.compiled.statement.parameters
+                    compile_state._dict_parameters
                     and (
-                        (
-                            self.compiled.statement._has_multi_parameters
-                            and (
-                                seq_column.key
-                                in self.compiled.statement.parameters[0]
-                                or seq_column
-                                in self.compiled.statement.parameters[0]
-                            )
-                        )
-                        or (
-                            not self.compiled.statement._has_multi_parameters
-                            and (
-                                seq_column.key
-                                in self.compiled.statement.parameters
-                                or seq_column
-                                in self.compiled.statement.parameters
-                            )
-                        )
+                        seq_column.key in compile_state._dict_parameters
+                        or seq_column in compile_state._dict_parameters
                     )
                 )
+
             else:
                 self._enable_identity_insert = False
 
index 8775a88136002a44b41072509d5b462f1e1e0b8a..b151b6e4830191c455b5d65c3f7c69d69821d355 100644 (file)
@@ -1459,10 +1459,12 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
                 "get_current_parameters() can only be invoked in the "
                 "context of a Python side column default function"
             )
+
+        compile_state = self.compiled.compile_state
         if (
             isolate_multiinsert_groups
             and self.isinsert
-            and self.compiled.statement._has_multi_parameters
+            and compile_state._has_multi_parameters
         ):
             if column._is_multiparam_column:
                 index = column.index + 1
@@ -1470,7 +1472,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
             else:
                 d = {column.key: parameters[column.key]}
                 index = 0
-            keys = self.compiled.statement.parameters[0].keys()
+            keys = compile_state._dict_parameters.keys()
             d.update(
                 (key, parameters["%s_m%d" % (key, index)]) for key in keys
             )
index 2d336360f9e00e430d20fe6a042552fc759d39bf..89839ea28da0124d7e45786e367e851793b9c62b 100644 (file)
@@ -16,6 +16,7 @@ import re
 
 from .traversals import HasCacheKey  # noqa
 from .visitors import ClauseVisitor
+from .visitors import InternalTraversal
 from .. import exc
 from .. import util
 
@@ -221,6 +222,10 @@ class DialectKWArgs(object):
 
     """
 
+    _dialect_kwargs_traverse_internals = [
+        ("dialect_options", InternalTraversal.dp_dialect_options)
+    ]
+
     @classmethod
     def argument_for(cls, dialect_name, argument_name, default):
         """Add a new kind of dialect-specific keyword argument for this class.
@@ -386,6 +391,39 @@ class DialectKWArgs(object):
                     construct_arg_dictionary[arg_name] = kwargs[k]
 
 
+class CompileState(object):
+    """Produces additional object state necessary for a statement to be
+    compiled.
+
+    the :class:`.CompileState` class is at the base of classes that assemble
+    state for a particular statement object that is then used by the
+    compiler.   This process is essentially an extension of the process that
+    the SQLCompiler.visit_XYZ() method takes, however there is an emphasis
+    on converting raw user intent into more organized structures rather than
+    producing string output.   The top-level :class:`.CompileState` for the
+    statement being executed is also accessible when the execution context
+    works with invoking the statement and collecting results.
+
+    The production of :class:`.CompileState` is specific to the compiler,  such
+    as within the :meth:`.SQLCompiler.visit_insert`,
+    :meth:`.SQLCompiler.visit_select` etc. methods.  These methods are also
+    responsible for associating the :class:`.CompileState` with the
+    :class:`.SQLCompiler` itself, if the statement is the "toplevel" statement,
+    i.e. the outermost SQL statement that's actually being executed.
+    There can be other :class:`.CompileState` objects that are not the
+    toplevel, such as when a SELECT subquery or CTE-nested
+    INSERT/UPDATE/DELETE is generated.
+
+    .. versionadded:: 1.4
+
+    """
+
+    __slots__ = ("statement",)
+
+    def __init__(self, statement, compiler, **kw):
+        self.statement = statement
+
+
 class Generative(object):
     """Provide a method-chaining pattern in conjunction with the
     @_generative decorator."""
@@ -396,6 +434,12 @@ class Generative(object):
         return s
 
 
+class HasCompileState(Generative):
+    """A class that has a :class:`.CompileState` associated with it."""
+
+    _compile_state_cls = CompileState
+
+
 class Executable(Generative):
     """Mark a ClauseElement as supporting execution.
 
@@ -627,6 +671,9 @@ class ColumnCollection(object):
     def keys(self):
         return [k for (k, col) in self._collection]
 
+    def __bool__(self):
+        return bool(self._collection)
+
     def __len__(self):
         return len(self._collection)
 
index fc841bb4bea7d609b2900de50c0c2638d11394ba..679d9c6e9bd9df8e3cf781946202e5b56e114d96 100644 (file)
@@ -55,7 +55,10 @@ def expect(role, element, **kw):
     # elaborate logic up front if possible
     impl = _impl_lookup[role]
 
-    if not isinstance(element, (elements.ClauseElement, schema.SchemaItem)):
+    if not isinstance(
+        element,
+        (elements.ClauseElement, schema.SchemaItem, schema.FetchedValue),
+    ):
         resolved = impl._resolve_for_clause_element(element, **kw)
     else:
         resolved = element
@@ -194,7 +197,9 @@ class _ColumnCoercions(object):
     def _implicit_coercions(
         self, original_element, resolved, argname=None, **kw
     ):
-        if resolved._is_select_statement:
+        if not resolved.is_clause_element:
+            self._raise_for_expected(original_element, argname, resolved)
+        elif resolved._is_select_statement:
             self._warn_for_scalar_subquery_coercion()
             return resolved.scalar_subquery()
         elif resolved._is_from_clause and isinstance(
@@ -290,14 +295,14 @@ class ExpressionElementImpl(
     _ColumnCoercions, RoleImpl, roles.ExpressionElementRole
 ):
     def _literal_coercion(
-        self, element, name=None, type_=None, argname=None, **kw
+        self, element, name=None, type_=None, argname=None, is_crud=False, **kw
     ):
         if element is None:
             return elements.Null()
         else:
             try:
                 return elements.BindParameter(
-                    name, element, type_, unique=True
+                    name, element, type_, unique=True, _is_crud=is_crud
                 )
             except exc.ArgumentError as err:
                 self._raise_for_expected(element, err=err)
index 424282951a5c1e3267855d6a6445948c1b9b47c3..3ebcf24b03e2a910e0e775566bdac6e0a3390787 100644 (file)
@@ -653,6 +653,20 @@ class SQLCompiler(Compiled):
 
     insert_prefetch = update_prefetch = ()
 
+    compile_state = None
+    """Optional :class:`.CompileState` object that maintains additional
+    state used by the compiler.
+
+    Major executable objects such as :class:`.Insert`, :class:`.Update`,
+    :class:`.Delete`, :class:`.Select` will generate this state when compiled
+    in order to calculate additional information about the object.   For the
+    top level object that is to be executed, the state can be stored here where
+    it can also have applicability towards result set processing.
+
+    .. versionadded:: 1.4
+
+    """
+
     def __init__(
         self,
         dialect,
@@ -1292,6 +1306,13 @@ class SQLCompiler(Compiled):
         else:
             return "0"
 
+    def _generate_delimited_list(self, elements, separator, **kw):
+        return separator.join(
+            s
+            for s in (c._compiler_dispatch(self, **kw) for c in elements)
+            if s
+        )
+
     def visit_clauselist(self, clauselist, **kw):
         sep = clauselist.operator
         if sep is None:
@@ -1299,13 +1320,7 @@ class SQLCompiler(Compiled):
         else:
             sep = OPERATORS[clauselist.operator]
 
-        text = sep.join(
-            s
-            for s in (
-                c._compiler_dispatch(self, **kw) for c in clauselist.clauses
-            )
-            if s
-        )
+        text = self._generate_delimited_list(clauselist.clauses, sep, **kw)
         if clauselist._tuple_values and self.dialect.tuple_in_values:
             text = "VALUES " + text
         return text
@@ -2810,8 +2825,18 @@ class SQLCompiler(Compiled):
         return dialect_hints, table_text
 
     def visit_insert(self, insert_stmt, **kw):
+
+        compile_state = insert_stmt._compile_state_cls(
+            insert_stmt, self, isinsert=True, **kw
+        )
+        insert_stmt = compile_state.statement
+
         toplevel = not self.stack
 
+        if toplevel:
+            self.isinsert = True
+            self.compile_state = compile_state
+
         self.stack.append(
             {
                 "correlate_froms": set(),
@@ -2820,8 +2845,8 @@ class SQLCompiler(Compiled):
             }
         )
 
-        crud_params = crud._setup_crud_params(
-            self, insert_stmt, crud.ISINSERT, **kw
+        crud_params = crud._get_crud_params(
+            self, insert_stmt, compile_state, **kw
         )
 
         if (
@@ -2835,7 +2860,7 @@ class SQLCompiler(Compiled):
                 "inserts." % self.dialect.name
             )
 
-        if insert_stmt._has_multi_parameters:
+        if compile_state._has_multi_parameters:
             if not self.dialect.supports_multivalues_insert:
                 raise exc.CompileError(
                     "The '%s' dialect with current database "
@@ -2888,7 +2913,7 @@ class SQLCompiler(Compiled):
                 text += " %s" % select_text
         elif not crud_params and supports_default_values:
             text += " DEFAULT VALUES"
-        elif insert_stmt._has_multi_parameters:
+        elif compile_state._has_multi_parameters:
             text += " VALUES %s" % (
                 ", ".join(
                     "(%s)" % (", ".join(c[1] for c in crud_param_set))
@@ -2947,9 +2972,16 @@ class SQLCompiler(Compiled):
         )
 
     def visit_update(self, update_stmt, **kw):
+        compile_state = update_stmt._compile_state_cls(
+            update_stmt, self, isupdate=True, **kw
+        )
+        update_stmt = compile_state.statement
+
         toplevel = not self.stack
+        if toplevel:
+            self.isupdate = True
 
-        extra_froms = update_stmt._extra_froms
+        extra_froms = compile_state._extra_froms
         is_multitable = bool(extra_froms)
 
         if is_multitable:
@@ -2981,8 +3013,8 @@ class SQLCompiler(Compiled):
         table_text = self.update_tables_clause(
             update_stmt, update_stmt.table, render_extra_froms, **kw
         )
-        crud_params = crud._setup_crud_params(
-            self, update_stmt, crud.ISUPDATE, **kw
+        crud_params = crud._get_crud_params(
+            self, update_stmt, compile_state, **kw
         )
 
         if update_stmt._hints:
@@ -3022,8 +3054,10 @@ class SQLCompiler(Compiled):
             if extra_from_text:
                 text += " " + extra_from_text
 
-        if update_stmt._whereclause is not None:
-            t = self.process(update_stmt._whereclause, **kw)
+        if update_stmt._where_criteria:
+            t = self._generate_delimited_list(
+                update_stmt._where_criteria, OPERATORS[operators.and_], **kw
+            )
             if t:
                 text += " WHERE " + t
 
@@ -3045,10 +3079,6 @@ class SQLCompiler(Compiled):
 
         return text
 
-    @util.memoized_property
-    def _key_getters_for_crud_column(self):
-        return crud._key_getters_for_crud_column(self, self.statement)
-
     def delete_extra_from_clause(
         self, update_stmt, from_table, extra_froms, from_hints, **kw
     ):
@@ -3069,11 +3099,16 @@ class SQLCompiler(Compiled):
         return from_table._compiler_dispatch(self, asfrom=True, iscrud=True)
 
     def visit_delete(self, delete_stmt, **kw):
-        toplevel = not self.stack
+        compile_state = delete_stmt._compile_state_cls(
+            delete_stmt, self, isdelete=True, **kw
+        )
+        delete_stmt = compile_state.statement
 
-        crud._setup_crud_params(self, delete_stmt, crud.ISDELETE, **kw)
+        toplevel = not self.stack
+        if toplevel:
+            self.isdelete = True
 
-        extra_froms = delete_stmt._extra_froms
+        extra_froms = compile_state._extra_froms
 
         correlate_froms = {delete_stmt.table}.union(extra_froms)
         self.stack.append(
@@ -3122,8 +3157,10 @@ class SQLCompiler(Compiled):
             if extra_from_text:
                 text += " " + extra_from_text
 
-        if delete_stmt._whereclause is not None:
-            t = delete_stmt._whereclause._compiler_dispatch(self, **kw)
+        if delete_stmt._where_criteria:
+            t = self._generate_delimited_list(
+                delete_stmt._where_criteria, OPERATORS[operators.and_], **kw
+            )
             if t:
                 text += " WHERE " + t
 
index e474952ce436c0f22cdefdbcbd890deaf88e3283..2827a5817395a9a8935884409d140551adeb2e3d 100644 (file)
@@ -16,6 +16,7 @@ from . import coercions
 from . import dml
 from . import elements
 from . import roles
+from .elements import ClauseElement
 from .. import exc
 from .. import util
 
@@ -33,45 +34,8 @@ values present.
 """,
 )
 
-ISINSERT = util.symbol("ISINSERT")
-ISUPDATE = util.symbol("ISUPDATE")
-ISDELETE = util.symbol("ISDELETE")
 
-
-def _setup_crud_params(compiler, stmt, local_stmt_type, **kw):
-    restore_isinsert = compiler.isinsert
-    restore_isupdate = compiler.isupdate
-    restore_isdelete = compiler.isdelete
-
-    should_restore = (
-        (restore_isinsert or restore_isupdate or restore_isdelete)
-        or len(compiler.stack) > 1
-        or "visiting_cte" in kw
-    )
-
-    if local_stmt_type is ISINSERT:
-        compiler.isupdate = False
-        compiler.isinsert = True
-    elif local_stmt_type is ISUPDATE:
-        compiler.isupdate = True
-        compiler.isinsert = False
-    elif local_stmt_type is ISDELETE:
-        if not should_restore:
-            compiler.isdelete = True
-    else:
-        assert False, "ISINSERT, ISUPDATE, or ISDELETE expected"
-
-    try:
-        if local_stmt_type in (ISINSERT, ISUPDATE):
-            return _get_crud_params(compiler, stmt, **kw)
-    finally:
-        if should_restore:
-            compiler.isinsert = restore_isinsert
-            compiler.isupdate = restore_isupdate
-            compiler.isdelete = restore_isdelete
-
-
-def _get_crud_params(compiler, stmt, **kw):
+def _get_crud_params(compiler, stmt, compile_state, **kw):
     """create a set of tuples representing column/string pairs for use
     in an INSERT or UPDATE statement.
 
@@ -87,27 +51,29 @@ def _get_crud_params(compiler, stmt, **kw):
     compiler.update_prefetch = []
     compiler.returning = []
 
+    # getters - these are normally just column.key,
+    # but in the case of mysql multi-table update, the rules for
+    # .key must conditionally take tablename into account
+    (
+        _column_as_key,
+        _getattr_col_key,
+        _col_bind_name,
+    ) = getters = _key_getters_for_crud_column(compiler, stmt, compile_state)
+
+    compiler._key_getters_for_crud_column = getters
+
     # no parameters in the statement, no parameters in the
     # compiled params - return binds for all columns
-    if compiler.column_keys is None and stmt.parameters is None:
+    if compiler.column_keys is None and compile_state._no_parameters:
         return [
             (c, _create_bind_param(compiler, c, None, required=True))
             for c in stmt.table.columns
         ]
 
-    if stmt._has_multi_parameters:
-        stmt_parameters = stmt.parameters[0]
+    if compile_state._has_multi_parameters:
+        stmt_parameters = compile_state._multi_parameters[0]
     else:
-        stmt_parameters = stmt.parameters
-
-    # getters - these are normally just column.key,
-    # but in the case of mysql multi-table update, the rules for
-    # .key must conditionally take tablename into account
-    (
-        _column_as_key,
-        _getattr_col_key,
-        _col_bind_name,
-    ) = _key_getters_for_crud_column(compiler, stmt)
+        stmt_parameters = compile_state._dict_parameters
 
     # if we have statement parameters - set defaults in the
     # compiled params
@@ -132,10 +98,15 @@ def _get_crud_params(compiler, stmt, **kw):
 
     # special logic that only occurs for multi-table UPDATE
     # statements
-    if compiler.isupdate and stmt._extra_froms and stmt_parameters:
+    if (
+        compile_state.isupdate
+        and compile_state._extra_froms
+        and stmt_parameters
+    ):
         _get_multitable_params(
             compiler,
             stmt,
+            compile_state,
             stmt_parameters,
             check_columns,
             _col_bind_name,
@@ -144,10 +115,11 @@ def _get_crud_params(compiler, stmt, **kw):
             kw,
         )
 
-    if compiler.isinsert and stmt.select_names:
+    if compile_state.isinsert and stmt._select_names:
         _scan_insert_from_select_cols(
             compiler,
             stmt,
+            compile_state,
             parameters,
             _getattr_col_key,
             _column_as_key,
@@ -160,6 +132,7 @@ def _get_crud_params(compiler, stmt, **kw):
         _scan_cols(
             compiler,
             stmt,
+            compile_state,
             parameters,
             _getattr_col_key,
             _column_as_key,
@@ -181,8 +154,10 @@ def _get_crud_params(compiler, stmt, **kw):
                 % (", ".join("%s" % (c,) for c in check))
             )
 
-    if stmt._has_multi_parameters:
-        values = _extend_values_for_multiparams(compiler, stmt, values, kw)
+    if compile_state._has_multi_parameters:
+        values = _extend_values_for_multiparams(
+            compiler, stmt, compile_state, values, kw
+        )
 
     return values
 
@@ -201,15 +176,46 @@ def _create_bind_param(
     return bindparam
 
 
-def _key_getters_for_crud_column(compiler, stmt):
-    if compiler.isupdate and stmt._extra_froms:
+def _handle_values_anonymous_param(compiler, col, value, name, **kw):
+    # the insert() and update() constructs as of 1.4 will now produce anonymous
+    # bindparam() objects in the values() collections up front when given plain
+    # literal values.  This is so that cache key behaviors, which need to
+    # produce bound parameters in deterministic order without invoking any
+    # compilation here, can be applied to these constructs when they include
+    # values() (but not yet multi-values, which are not included in caching
+    # right now).
+    #
+    # in order to produce the desired "crud" style name for these parameters,
+    # which will also be targetable in engine/default.py through the usual
+    # conventions, apply our desired name to these unique parameters by
+    # populating the compiler truncated names cache with the desired name,
+    # rather than having
+    # compiler.visit_bindparam()->compiler._truncated_identifier make up a
+    # name.  Saves on call counts also.
+    if value.unique and isinstance(value.key, elements._truncated_label):
+        compiler.truncated_names[("bindparam", value.key)] = name
+
+    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
+
+    return value._compiler_dispatch(compiler, **kw)
+
+
+def _key_getters_for_crud_column(compiler, stmt, compile_state):
+    if compile_state.isupdate and compile_state._extra_froms:
         # when extra tables are present, refer to the columns
         # in those extra tables as table-qualified, including in
         # dictionaries and when rendering bind param names.
         # the "main" table of the statement remains unqualified,
         # allowing the most compatibility with a non-multi-table
         # statement.
-        _et = set(stmt._extra_froms)
+        _et = set(compile_state._extra_froms)
 
         c_key_role = functools.partial(
             coercions.expect_as_key, roles.DMLColumnRole
@@ -246,6 +252,7 @@ def _key_getters_for_crud_column(compiler, stmt):
 def _scan_insert_from_select_cols(
     compiler,
     stmt,
+    compile_state,
     parameters,
     _getattr_col_key,
     _column_as_key,
@@ -260,9 +267,9 @@ def _scan_insert_from_select_cols(
         implicit_returning,
         implicit_return_defaults,
         postfetch_lastrowid,
-    ) = _get_returning_modifiers(compiler, stmt)
+    ) = _get_returning_modifiers(compiler, stmt, compile_state)
 
-    cols = [stmt.table.c[_column_as_key(name)] for name in stmt.select_names]
+    cols = [stmt.table.c[_column_as_key(name)] for name in stmt._select_names]
 
     compiler._insert_from_select = stmt.select
 
@@ -294,6 +301,7 @@ def _scan_insert_from_select_cols(
 def _scan_cols(
     compiler,
     stmt,
+    compile_state,
     parameters,
     _getattr_col_key,
     _column_as_key,
@@ -308,11 +316,11 @@ def _scan_cols(
         implicit_returning,
         implicit_return_defaults,
         postfetch_lastrowid,
-    ) = _get_returning_modifiers(compiler, stmt)
+    ) = _get_returning_modifiers(compiler, stmt, compile_state)
 
-    if stmt._parameter_ordering:
+    if compile_state._parameter_ordering:
         parameter_ordering = [
-            _column_as_key(key) for key in stmt._parameter_ordering
+            _column_as_key(key) for key in compile_state._parameter_ordering
         ]
         ordered_keys = set(parameter_ordering)
         cols = [stmt.table.c[key] for key in parameter_ordering] + [
@@ -329,6 +337,7 @@ def _scan_cols(
             _append_param_parameter(
                 compiler,
                 stmt,
+                compile_state,
                 c,
                 col_key,
                 parameters,
@@ -339,7 +348,7 @@ def _scan_cols(
                 kw,
             )
 
-        elif compiler.isinsert:
+        elif compile_state.isinsert:
             if (
                 c.primary_key
                 and need_pks
@@ -377,7 +386,7 @@ def _scan_cols(
             ):
                 _warn_pk_with_no_anticipated_value(c)
 
-        elif compiler.isupdate:
+        elif compile_state.isupdate:
             _append_param_update(
                 compiler, stmt, c, implicit_return_defaults, values, kw
             )
@@ -386,6 +395,7 @@ def _scan_cols(
 def _append_param_parameter(
     compiler,
     stmt,
+    compile_state,
     c,
     col_key,
     parameters,
@@ -395,7 +405,9 @@ def _append_param_parameter(
     values,
     kw,
 ):
+
     value = parameters.pop(col_key)
+
     if coercions._is_literal(value):
         value = _create_bind_param(
             compiler,
@@ -403,15 +415,21 @@ def _append_param_parameter(
             value,
             required=value is REQUIRED,
             name=_col_bind_name(c)
-            if not stmt._has_multi_parameters
+            if not compile_state._has_multi_parameters
+            else "%s_m0" % _col_bind_name(c),
+            **kw
+        )
+    elif value._is_bind_parameter:
+        value = _handle_values_anonymous_param(
+            compiler,
+            c,
+            value,
+            name=_col_bind_name(c)
+            if not compile_state._has_multi_parameters
             else "%s_m0" % _col_bind_name(c),
             **kw
         )
     else:
-        if isinstance(value, elements.BindParameter) and value.type._isnull:
-            value = value._clone()
-            value.type = c.type
-
         if c.primary_key and implicit_returning:
             compiler.returning.append(c)
             value = compiler.process(value.self_group(), **kw)
@@ -644,6 +662,7 @@ def _append_param_update(
 def _get_multitable_params(
     compiler,
     stmt,
+    compile_state,
     stmt_parameters,
     check_columns,
     _col_bind_name,
@@ -656,7 +675,7 @@ def _get_multitable_params(
         for c, param in stmt_parameters.items()
     )
     affected_tables = set()
-    for t in stmt._extra_froms:
+    for t in compile_state._extra_froms:
         for c in t.c:
             if c in normalized_params:
                 affected_tables.add(t)
@@ -669,6 +688,11 @@ def _get_multitable_params(
                         value,
                         required=value is REQUIRED,
                         name=_col_bind_name(c),
+                        **kw  # TODO: no test coverage for literal binds here
+                    )
+                elif value._is_bind_parameter:
+                    value = _handle_values_anonymous_param(
+                        compiler, c, value, name=_col_bind_name(c), **kw
                     )
                 else:
                     compiler.postfetch.append(c)
@@ -704,11 +728,11 @@ def _get_multitable_params(
                 compiler.postfetch.append(c)
 
 
-def _extend_values_for_multiparams(compiler, stmt, values, kw):
+def _extend_values_for_multiparams(compiler, stmt, compile_state, values, kw):
     values_0 = values
     values = [values]
 
-    for i, row in enumerate(stmt.parameters[1:]):
+    for i, row in enumerate(compile_state._multi_parameters[1:]):
         extension = []
         for (col, param) in values_0:
             if col in row or col.key in row:
@@ -757,12 +781,13 @@ def _get_stmt_parameters_params(
             values.append((k, v))
 
 
-def _get_returning_modifiers(compiler, stmt):
+def _get_returning_modifiers(compiler, stmt, compile_state):
+
     need_pks = (
-        compiler.isinsert
+        compile_state.isinsert
         and not compiler.inline
         and not stmt._returning
-        and not stmt._has_multi_parameters
+        and not compile_state._has_multi_parameters
     )
 
     implicit_returning = (
@@ -771,9 +796,9 @@ def _get_returning_modifiers(compiler, stmt):
         and stmt.table.implicit_returning
     )
 
-    if compiler.isinsert:
+    if compile_state.isinsert:
         implicit_return_defaults = implicit_returning and stmt._return_defaults
-    elif compiler.isupdate:
+    elif compile_state.isupdate:
         implicit_return_defaults = (
             compiler.dialect.implicit_returning
             and stmt.table.implicit_returning
index 097c513b4f2e40ecf4343d49c4b2b07534f88d41..171a2cc2ceb2746d2c9cb3f7ff28e6ee85e0394f 100644 (file)
 Provide :class:`.Insert`, :class:`.Update` and :class:`.Delete`.
 
 """
-
+from sqlalchemy.types import NullType
 from . import coercions
 from . import roles
 from .base import _from_objects
 from .base import _generative
+from .base import CompileState
 from .base import DialectKWArgs
 from .base import Executable
-from .elements import and_
+from .base import HasCompileState
 from .elements import ClauseElement
 from .elements import Null
 from .selectable import HasCTE
 from .selectable import HasPrefixes
+from .visitors import InternalTraversal
 from .. import exc
 from .. import util
+from ..util import collections_abc
+
+
+class DMLState(CompileState):
+    _no_parameters = True
+    _dict_parameters = None
+    _multi_parameters = None
+    _parameter_ordering = None
+    _has_multi_parameters = False
+    isupdate = False
+    isdelete = False
+    isinsert = False
+
+    def __init__(
+        self,
+        statement,
+        compiler,
+        isinsert=False,
+        isupdate=False,
+        isdelete=False,
+        **kw
+    ):
+        self.statement = statement
+
+        if isupdate:
+            self.isupdate = True
+            self._preserve_parameter_order = (
+                statement._preserve_parameter_order
+            )
+            if statement._ordered_values is not None:
+                self._process_ordered_values(statement)
+            elif statement._values is not None:
+                self._process_values(statement)
+            elif statement._multi_values:
+                self._process_multi_values(statement)
+            self._extra_froms = self._make_extra_froms(statement)
+        elif isinsert:
+            self.isinsert = True
+            if statement._select_names:
+                self._process_select_values(statement)
+            if statement._values is not None:
+                self._process_values(statement)
+            if statement._multi_values:
+                self._process_multi_values(statement)
+        elif isdelete:
+            self.isdelete = True
+            self._extra_froms = self._make_extra_froms(statement)
+        else:
+            assert False, "one of isinsert, isupdate, or isdelete must be set"
+
+    def _make_extra_froms(self, statement):
+        froms = []
+        seen = {statement.table}
+
+        for crit in statement._where_criteria:
+            for item in _from_objects(crit):
+                if not seen.intersection(item._cloned_set):
+                    froms.append(item)
+                seen.update(item._cloned_set)
+
+        return froms
+
+    def _process_multi_values(self, statement):
+        if not statement._supports_multi_parameters:
+            raise exc.InvalidRequestError(
+                "%s construct does not support "
+                "multiple parameter sets." % statement.__visit_name__.upper()
+            )
+
+        for parameters in statement._multi_values:
+            multi_parameters = [
+                {
+                    c.key: value
+                    for c, value in zip(statement.table.c, parameter_set)
+                }
+                if isinstance(parameter_set, collections_abc.Sequence)
+                else parameter_set
+                for parameter_set in parameters
+            ]
+
+            if self._no_parameters:
+                self._no_parameters = False
+                self._has_multi_parameters = True
+                self._multi_parameters = multi_parameters
+                self._dict_parameters = self._multi_parameters[0]
+            elif not self._has_multi_parameters:
+                self._cant_mix_formats_error()
+            else:
+                self._multi_parameters.extend(multi_parameters)
+
+    def _process_values(self, statement):
+        if self._no_parameters:
+            self._has_multi_parameters = False
+            self._dict_parameters = statement._values
+            self._no_parameters = False
+        elif self._has_multi_parameters:
+            self._cant_mix_formats_error()
+
+    def _process_ordered_values(self, statement):
+        parameters = statement._ordered_values
+
+        if self._no_parameters:
+            self._no_parameters = False
+            self._dict_parameters = dict(parameters)
+            self._parameter_ordering = [key for key, value in parameters]
+        elif self._has_multi_parameters:
+            self._cant_mix_formats_error()
+        else:
+            raise exc.InvalidRequestError(
+                "Can only invoke ordered_values() once, and not mixed "
+                "with any other values() call"
+            )
+
+    def _process_select_values(self, statement):
+        parameters = {
+            coercions.expect(roles.DMLColumnRole, name, as_key=True): Null()
+            for name in statement._select_names
+        }
+
+        if self._no_parameters:
+            self._no_parameters = False
+            self._dict_parameters = parameters
+        else:
+            # this condition normally not reachable as the Insert
+            # does not allow this construction to occur
+            assert False, "This statement already has parameters"
+
+    def _cant_mix_formats_error(self):
+        raise exc.InvalidRequestError(
+            "Can't mix single and multiple VALUES "
+            "formats in one INSERT statement; one style appends to a "
+            "list while the other replaces values, so the intent is "
+            "ambiguous."
+        )
 
 
 class UpdateBase(
     roles.DMLRole,
     HasCTE,
+    HasCompileState,
     DialectKWArgs,
     HasPrefixes,
     Executable,
@@ -42,10 +179,10 @@ class UpdateBase(
         {"autocommit": True}
     )
     _hints = util.immutabledict()
-    _parameter_ordering = None
-    _prefixes = ()
     named_with_column = False
 
+    _compile_state_cls = DMLState
+
     @classmethod
     def _constructor_20_deprecations(cls, fn_name, clsname, names):
 
@@ -112,43 +249,6 @@ class UpdateBase(
             col._make_proxy(fromclause) for col in self._returning
         )
 
-    def _process_colparams(self, parameters, preserve_parameter_order=False):
-        def process_single(p):
-            if isinstance(p, (list, tuple)):
-                return dict((c.key, pval) for c, pval in zip(self.table.c, p))
-            else:
-                return p
-
-        if (
-            preserve_parameter_order or self._preserve_parameter_order
-        ) and parameters is not None:
-            if not isinstance(parameters, list) or (
-                parameters and not isinstance(parameters[0], tuple)
-            ):
-                raise ValueError(
-                    "When preserve_parameter_order is True, "
-                    "values() only accepts a list of 2-tuples"
-                )
-            self._parameter_ordering = [key for key, value in parameters]
-
-            return dict(parameters), False
-
-        if (
-            isinstance(parameters, (list, tuple))
-            and parameters
-            and isinstance(parameters[0], (list, tuple, dict))
-        ):
-
-            if not self._supports_multi_parameters:
-                raise exc.InvalidRequestError(
-                    "This construct does not support "
-                    "multiple parameter sets."
-                )
-
-            return [process_single(p) for p in parameters], True
-        else:
-            return process_single(parameters), False
-
     def params(self, *arg, **kw):
         """Set the parameters for the statement.
 
@@ -163,6 +263,29 @@ class UpdateBase(
             " stmt.values(**parameters)."
         )
 
+    @_generative
+    def with_dialect_options(self, **opt):
+        """Add dialect options to this INSERT/UPDATE/DELETE object.
+
+        e.g.::
+
+            upd = table.update().dialect_options(mysql_limit=10)
+
+        .. versionadded: 1.4 - this method supersedes the dialect options
+           associated with the constructor.
+
+
+        """
+        self._validate_dialect_kwargs(opt)
+
+    def _validate_dialect_kwargs_deprecated(self, dialect_kw):
+        util.warn_deprecated_20(
+            "Passing dialect keyword arguments directly to the "
+            "constructor is deprecated and will be removed in SQLAlchemy "
+            "2.0.  Please use the ``with_dialect_options()`` method."
+        )
+        self._validate_dialect_kwargs(dialect_kw)
+
     def bind(self):
         """Return a 'bind' linked to this :class:`.UpdateBase`
         or a :class:`.Table` associated with it.
@@ -266,9 +389,6 @@ class UpdateBase(
 
         self._hints = self._hints.union({(selectable, dialect_name): text})
 
-    def _copy_internals(self, **kw):
-        raise NotImplementedError()
-
 
 class ValuesBase(UpdateBase):
     """Supplies support for :meth:`.ValuesBase.values` to
@@ -277,16 +397,21 @@ class ValuesBase(UpdateBase):
     __visit_name__ = "values_base"
 
     _supports_multi_parameters = False
-    _has_multi_parameters = False
     _preserve_parameter_order = False
     select = None
     _post_values_clause = None
 
+    _values = None
+    _multi_values = ()
+    _ordered_values = None
+    _select_names = None
+
+    _returning = ()
+
     def __init__(self, table, values, prefixes):
         self.table = coercions.expect(roles.FromClauseRole, table)
-        self.parameters, self._has_multi_parameters = self._process_colparams(
-            values
-        )
+        if values is not None:
+            self.values.non_generative(self, values)
         if prefixes:
             self._setup_prefixes(prefixes)
 
@@ -416,59 +541,96 @@ class ValuesBase(UpdateBase):
             :func:`~.expression.update` - produce an ``UPDATE`` statement
 
         """
-        if self.select is not None:
+        if self._select_names:
             raise exc.InvalidRequestError(
                 "This construct already inserts from a SELECT"
             )
-        if self._has_multi_parameters and kwargs:
-            raise exc.InvalidRequestError(
-                "This construct already has multiple parameter sets."
+        elif self._ordered_values:
+            raise exc.ArgumentError(
+                "This statement already has ordered values present"
             )
 
         if args:
-            if len(args) > 1:
+            # positional case.  this is currently expensive.   we don't
+            # yet have positional-only args so we have to check the length.
+            # then we need to check multiparams vs. single dictionary.
+            # since the parameter format is needed in order to determine
+            # a cache key, we need to determine this up front.
+            arg = args[0]
+
+            if kwargs:
+                raise exc.ArgumentError(
+                    "Can't pass positional and kwargs to values() "
+                    "simultaneously"
+                )
+            elif len(args) > 1:
                 raise exc.ArgumentError(
                     "Only a single dictionary/tuple or list of "
                     "dictionaries/tuples is accepted positionally."
                 )
-            v = args[0]
-        else:
-            v = {}
 
-        if self.parameters is None:
-            (
-                self.parameters,
-                self._has_multi_parameters,
-            ) = self._process_colparams(v)
-        else:
-            if self._has_multi_parameters:
-                self.parameters = list(self.parameters)
-                p, self._has_multi_parameters = self._process_colparams(v)
-                if not self._has_multi_parameters:
-                    raise exc.ArgumentError(
-                        "Can't mix single-values and multiple values "
-                        "formats in one statement"
-                    )
+            elif not self._preserve_parameter_order and isinstance(
+                arg, collections_abc.Sequence
+            ):
 
-                self.parameters.extend(p)
-            else:
-                self.parameters = self.parameters.copy()
-                p, self._has_multi_parameters = self._process_colparams(v)
-                if self._has_multi_parameters:
-                    raise exc.ArgumentError(
-                        "Can't mix single-values and multiple values "
-                        "formats in one statement"
-                    )
-                self.parameters.update(p)
+                if arg and isinstance(arg[0], (list, dict, tuple)):
+                    self._multi_values += (arg,)
+                    return
 
-        if kwargs:
-            if self._has_multi_parameters:
+                # tuple values
+                arg = {c.key: value for c, value in zip(self.table.c, arg)}
+            elif self._preserve_parameter_order and not isinstance(
+                arg, collections_abc.Sequence
+            ):
+                raise ValueError(
+                    "When preserve_parameter_order is True, "
+                    "values() only accepts a list of 2-tuples"
+                )
+
+        else:
+            # kwarg path.  this is the most common path for non-multi-params
+            # so this is fairly quick.
+            arg = kwargs
+            if args:
                 raise exc.ArgumentError(
-                    "Can't pass kwargs and multiple parameter sets "
-                    "simultaneously"
+                    "Only a single dictionary/tuple or list of "
+                    "dictionaries/tuples is accepted positionally."
                 )
+
+        # for top level values(), convert literals to anonymous bound
+        # parameters at statement construction time, so that these values can
+        # participate in the cache key process like any other ClauseElement.
+        # crud.py now intercepts bound parameters with unique=True from here
+        # and ensures they get the "crud"-style name when rendered.
+
+        if self._preserve_parameter_order:
+            arg = [
+                (
+                    k,
+                    coercions.expect(
+                        roles.ExpressionElementRole,
+                        v,
+                        type_=NullType(),
+                        is_crud=True,
+                    ),
+                )
+                for k, v in arg
+            ]
+            self._ordered_values = arg
+        else:
+            arg = {
+                k: coercions.expect(
+                    roles.ExpressionElementRole,
+                    v,
+                    type_=NullType(),
+                    is_crud=True,
+                )
+                for k, v in arg.items()
+            }
+            if self._values:
+                self._values = self._values.union(arg)
             else:
-                self.parameters.update(kwargs)
+                self._values = util.immutabledict(arg)
 
     @_generative
     def return_defaults(self, *cols):
@@ -555,6 +717,25 @@ class Insert(ValuesBase):
 
     _supports_multi_parameters = True
 
+    select = None
+    include_insert_from_select_defaults = False
+
+    _traverse_internals = (
+        [
+            ("table", InternalTraversal.dp_clauseelement),
+            ("_inline", InternalTraversal.dp_boolean),
+            ("_select_names", InternalTraversal.dp_string_list),
+            ("_values", InternalTraversal.dp_dml_values),
+            ("_multi_values", InternalTraversal.dp_dml_multi_values),
+            ("select", InternalTraversal.dp_clauseelement),
+            ("_post_values_clause", InternalTraversal.dp_clauseelement),
+            ("_returning", InternalTraversal.dp_clauseelement_list),
+            ("_hints", InternalTraversal.dp_table_hint_list),
+        ]
+        + HasPrefixes._has_prefixes_traverse_internals
+        + DialectKWArgs._dialect_kwargs_traverse_internals
+    )
+
     @ValuesBase._constructor_20_deprecations(
         "insert",
         "Insert",
@@ -626,18 +807,13 @@ class Insert(ValuesBase):
         """
         super(Insert, self).__init__(table, values, prefixes)
         self._bind = bind
-        self.select = self.select_names = None
-        self.include_insert_from_select_defaults = False
         self._inline = inline
-        self._returning = returning
-        self._validate_dialect_kwargs(dialect_kw)
-        self._return_defaults = return_defaults
+        if returning:
+            self._returning = returning
+        if dialect_kw:
+            self._validate_dialect_kwargs_deprecated(dialect_kw)
 
-    def get_children(self, **kwargs):
-        if self.select is not None:
-            return (self.select,)
-        else:
-            return ()
+        self._return_defaults = return_defaults
 
     @_generative
     def inline(self):
@@ -702,25 +878,34 @@ class Insert(ValuesBase):
            :attr:`.ResultProxy.inserted_primary_key` accessor does not apply.
 
         """
-        if self.parameters:
+
+        if self._values:
             raise exc.InvalidRequestError(
                 "This construct already inserts value expressions"
             )
 
-        self.parameters, self._has_multi_parameters = self._process_colparams(
-            {
-                coercions.expect(roles.DMLColumnRole, n, as_key=True): Null()
-                for n in names
-            }
-        )
-
-        self.select_names = names
+        self._select_names = names
         self._inline = True
         self.include_insert_from_select_defaults = include_defaults
         self.select = coercions.expect(roles.DMLSelectRole, select)
 
 
-class Update(ValuesBase):
+class DMLWhereBase(object):
+    _where_criteria = ()
+
+    @_generative
+    def where(self, whereclause):
+        """return a new construct with the given expression added to
+        its WHERE clause, joined to the existing clause via AND, if any.
+
+        """
+
+        self._where_criteria += (
+            coercions.expect(roles.WhereHavingRole, whereclause),
+        )
+
+
+class Update(DMLWhereBase, ValuesBase):
     """Represent an Update construct.
 
     The :class:`.Update` object is created using the :func:`update()`
@@ -730,6 +915,20 @@ class Update(ValuesBase):
 
     __visit_name__ = "update"
 
+    _traverse_internals = (
+        [
+            ("table", InternalTraversal.dp_clauseelement),
+            ("_where_criteria", InternalTraversal.dp_clauseelement_list),
+            ("_inline", InternalTraversal.dp_boolean),
+            ("_ordered_values", InternalTraversal.dp_dml_ordered_values),
+            ("_values", InternalTraversal.dp_dml_values),
+            ("_returning", InternalTraversal.dp_clauseelement_list),
+            ("_hints", InternalTraversal.dp_table_hint_list),
+        ]
+        + HasPrefixes._has_prefixes_traverse_internals
+        + DialectKWArgs._dialect_kwargs_traverse_internals
+    )
+
     @ValuesBase._constructor_20_deprecations(
         "update",
         "Update",
@@ -874,21 +1073,14 @@ class Update(ValuesBase):
         self._bind = bind
         self._returning = returning
         if whereclause is not None:
-            self._whereclause = coercions.expect(
-                roles.WhereHavingRole, whereclause
+            self._where_criteria += (
+                coercions.expect(roles.WhereHavingRole, whereclause),
             )
-        else:
-            self._whereclause = None
         self._inline = inline
-        self._validate_dialect_kwargs(dialect_kw)
+        if dialect_kw:
+            self._validate_dialect_kwargs_deprecated(dialect_kw)
         self._return_defaults = return_defaults
 
-    def get_children(self, **kwargs):
-        if self._whereclause is not None:
-            return (self._whereclause,)
-        else:
-            return ()
-
     @_generative
     def ordered_values(self, *args):
         """Specify the VALUES clause of this UPDATE statement with an explicit
@@ -912,22 +1104,27 @@ class Update(ValuesBase):
            parameter, which will be removed in SQLAlchemy 2.0.
 
         """
-        if self.select is not None:
-            raise exc.InvalidRequestError(
-                "This construct already inserts from a SELECT"
-            )
-
-        if self.parameters is None:
-            (
-                self.parameters,
-                self._has_multi_parameters,
-            ) = self._process_colparams(
-                list(args), preserve_parameter_order=True
-            )
-        else:
+        if self._values:
             raise exc.ArgumentError(
                 "This statement already has values present"
             )
+        elif self._ordered_values:
+            raise exc.ArgumentError(
+                "This statement already has ordered values present"
+            )
+        arg = [
+            (
+                k,
+                coercions.expect(
+                    roles.ExpressionElementRole,
+                    v,
+                    type_=NullType(),
+                    is_crud=True,
+                ),
+            )
+            for k, v in args
+        ]
+        self._ordered_values = arg
 
     @_generative
     def inline(self):
@@ -945,37 +1142,8 @@ class Update(ValuesBase):
         """
         self._inline = True
 
-    @_generative
-    def where(self, whereclause):
-        """return a new update() construct with the given expression added to
-        its WHERE clause, joined to the existing clause via AND, if any.
-
-        """
-        if self._whereclause is not None:
-            self._whereclause = and_(
-                self._whereclause,
-                coercions.expect(roles.WhereHavingRole, whereclause),
-            )
-        else:
-            self._whereclause = coercions.expect(
-                roles.WhereHavingRole, whereclause
-            )
-
-    @property
-    def _extra_froms(self):
-        froms = []
-        seen = {self.table}
-
-        if self._whereclause is not None:
-            for item in _from_objects(self._whereclause):
-                if not seen.intersection(item._cloned_set):
-                    froms.append(item)
-                seen.update(item._cloned_set)
-
-        return froms
-
 
-class Delete(UpdateBase):
+class Delete(DMLWhereBase, UpdateBase):
     """Represent a DELETE construct.
 
     The :class:`.Delete` object is created using the :func:`delete()`
@@ -985,6 +1153,17 @@ class Delete(UpdateBase):
 
     __visit_name__ = "delete"
 
+    _traverse_internals = (
+        [
+            ("table", InternalTraversal.dp_clauseelement),
+            ("_where_criteria", InternalTraversal.dp_clauseelement_list),
+            ("_returning", InternalTraversal.dp_clauseelement_list),
+            ("_hints", InternalTraversal.dp_table_hint_list),
+        ]
+        + HasPrefixes._has_prefixes_traverse_internals
+        + DialectKWArgs._dialect_kwargs_traverse_internals
+    )
+
     @ValuesBase._constructor_20_deprecations(
         "delete",
         "Delete",
@@ -1041,43 +1220,9 @@ class Delete(UpdateBase):
             self._setup_prefixes(prefixes)
 
         if whereclause is not None:
-            self._whereclause = coercions.expect(
-                roles.WhereHavingRole, whereclause
-            )
-        else:
-            self._whereclause = None
-
-        self._validate_dialect_kwargs(dialect_kw)
-
-    def get_children(self, **kwargs):
-        if self._whereclause is not None:
-            return (self._whereclause,)
-        else:
-            return ()
-
-    @_generative
-    def where(self, whereclause):
-        """Add the given WHERE clause to a newly returned delete construct."""
-
-        if self._whereclause is not None:
-            self._whereclause = and_(
-                self._whereclause,
+            self._where_criteria += (
                 coercions.expect(roles.WhereHavingRole, whereclause),
             )
-        else:
-            self._whereclause = coercions.expect(
-                roles.WhereHavingRole, whereclause
-            )
-
-    @property
-    def _extra_froms(self):
-        froms = []
-        seen = {self.table}
 
-        if self._whereclause is not None:
-            for item in _from_objects(self._whereclause):
-                if not seen.intersection(item._cloned_set):
-                    froms.append(item)
-                seen.update(item._cloned_set)
-
-        return froms
+        if dialect_kw:
+            self._validate_dialect_kwargs_deprecated(dialect_kw)
index d0babb1be0b154249c4ee1fba1fbed84b4835f84..47739a37df14ea920daea31f54d1be9431e771b6 100644 (file)
@@ -200,6 +200,7 @@ class ClauseElement(
     _is_from_container = False
     _is_select_container = False
     _is_select_statement = False
+    _is_bind_parameter = False
 
     _order_by_label_element = None
 
@@ -1010,6 +1011,7 @@ class BindParameter(roles.InElementRole, ColumnElement):
 
     _is_crud = False
     _expanding_in_types = ()
+    _is_bind_parameter = True
 
     def __init__(
         self,
@@ -1025,6 +1027,7 @@ class BindParameter(roles.InElementRole, ColumnElement):
         literal_execute=False,
         _compared_to_operator=None,
         _compared_to_type=None,
+        _is_crud=False,
     ):
         r"""Produce a "bound expression".
 
@@ -1303,6 +1306,8 @@ class BindParameter(roles.InElementRole, ColumnElement):
         self.required = required
         self.expanding = expanding
         self.literal_execute = literal_execute
+        if _is_crud:
+            self._is_crud = True
         if type_ is None:
             if _compared_to_type is not None:
                 self.type = _compared_to_type.coerce_compared_value(
@@ -4264,21 +4269,12 @@ class ColumnClause(
         else:
             return other.proxy_set.intersection(self.proxy_set)
 
-    def _get_table(self):
-        return self.__dict__["table"]
-
-    def _set_table(self, table):
-        self._memoized_property.expire_instance(self)
-        self.__dict__["table"] = table
-
     def get_children(self, column_tables=False, **kw):
         if column_tables and self.table is not None:
             return [self.table]
         else:
             return []
 
-    table = property(_get_table, _set_table)
-
     @_memoized_property
     def _from_objects(self):
         t = self.table
index 5445a1bceabfb598598b36fb11ee62fc2ba9c659..4c627c4cc624f64b0581cb199b14ffb026923fba 100644 (file)
@@ -1413,6 +1413,9 @@ class Column(DialectKWArgs, SchemaItem, ColumnClause):
                 "Column must be constructed with a non-blank name or "
                 "assign a non-blank .name before adding to a Table."
             )
+
+        Column._memoized_property.expire_instance(self)
+
         if self.key is None:
             self.key = self.name
 
@@ -2080,24 +2083,7 @@ class ForeignKey(DialectKWArgs, SchemaItem):
             self._set_target_column(_column)
 
 
-class _NotAColumnExpr(object):
-    # the coercions system is not used in crud.py for the values passed in
-    # the insert().values() and update().values() methods, so the usual
-    # pathways to rejecting a coercion in the unlikely case of adding defaut
-    # generator objects to insert() or update() constructs aren't available;
-    # create a quick coercion rejection here that is specific to what crud.py
-    # calls on value objects.
-    def _not_a_column_expr(self):
-        raise exc.InvalidRequestError(
-            "This %s cannot be used directly "
-            "as a column expression." % self.__class__.__name__
-        )
-
-    self_group = lambda self: self._not_a_column_expr()  # noqa
-    _from_objects = property(lambda self: self._not_a_column_expr())
-
-
-class DefaultGenerator(_NotAColumnExpr, SchemaItem):
+class DefaultGenerator(SchemaItem):
     """Base class for column *default* values."""
 
     __visit_name__ = "default_generator"
@@ -2505,7 +2491,7 @@ class Sequence(roles.StatementRole, DefaultGenerator):
 
 
 @inspection._self_inspects
-class FetchedValue(_NotAColumnExpr, SchemaEventTarget):
+class FetchedValue(SchemaEventTarget):
     """A marker for a transparent database-side default.
 
     Use :class:`.FetchedValue` when the database is configured
@@ -2528,6 +2514,7 @@ class FetchedValue(_NotAColumnExpr, SchemaEventTarget):
     is_server_default = True
     reflected = False
     has_argument = False
+    is_clause_element = False
 
     def __init__(self, for_update=False):
         self.for_update = for_update
index b972c13be63c96f010889e72d15fe3010d6a6f82..965ac6e7fed5c2b89610312c28d40cd590a8f811 100644 (file)
@@ -3145,8 +3145,6 @@ class Select(
 
     __visit_name__ = "select"
 
-    _prefixes = ()
-    _suffixes = ()
     _hints = util.immutabledict()
     _statement_hints = ()
     _distinct = False
index 03ff7c4394a3876ece4b1f4443932ea48be55cf1..c29a04ee0966ee6d80cf812d5bab4b7cb3e4df17 100644 (file)
@@ -200,6 +200,9 @@ class _CacheKey(ExtendedInternalTraversal):
             attrname, inspect(obj), parent, anon_map, bindparams
         )
 
+    def visit_string_list(self, attrname, obj, parent, anon_map, bindparams):
+        return tuple(obj)
+
     def visit_multi(self, attrname, obj, parent, anon_map, bindparams):
         return (
             attrname,
@@ -336,6 +339,25 @@ class _CacheKey(ExtendedInternalTraversal):
     def visit_plain_dict(self, attrname, obj, parent, anon_map, bindparams):
         return (attrname, tuple([(key, obj[key]) for key in sorted(obj)]))
 
+    def visit_dialect_options(
+        self, attrname, obj, parent, anon_map, bindparams
+    ):
+        return (
+            attrname,
+            tuple(
+                (
+                    dialect_name,
+                    tuple(
+                        [
+                            (key, obj[dialect_name][key])
+                            for key in sorted(obj[dialect_name])
+                        ]
+                    ),
+                )
+                for dialect_name in sorted(obj)
+            ),
+        )
+
     def visit_string_clauseelement_dict(
         self, attrname, obj, parent, anon_map, bindparams
     ):
@@ -366,9 +388,13 @@ class _CacheKey(ExtendedInternalTraversal):
     def visit_fromclause_canonical_column_collection(
         self, attrname, obj, parent, anon_map, bindparams
     ):
+        # inlining into the internals of ColumnCollection
         return (
             attrname,
-            tuple(col._gen_cache_key(anon_map, bindparams) for col in obj),
+            tuple(
+                col._gen_cache_key(anon_map, bindparams)
+                for k, col in obj._collection
+            ),
         )
 
     def visit_unknown_structure(
@@ -377,6 +403,48 @@ class _CacheKey(ExtendedInternalTraversal):
         anon_map[NO_CACHE] = True
         return ()
 
+    def visit_dml_ordered_values(
+        self, attrname, obj, parent, anon_map, bindparams
+    ):
+        return (
+            attrname,
+            tuple(
+                (
+                    key._gen_cache_key(anon_map, bindparams)
+                    if hasattr(key, "__clause_element__")
+                    else key,
+                    value._gen_cache_key(anon_map, bindparams),
+                )
+                for key, value in obj
+            ),
+        )
+
+    def visit_dml_values(self, attrname, obj, parent, anon_map, bindparams):
+
+        expr_values = {k for k in obj if hasattr(k, "__clause_element__")}
+        if expr_values:
+            # expr values can't be sorted deterministically right now,
+            # so no cache
+            anon_map[NO_CACHE] = True
+            return ()
+
+        str_values = expr_values.symmetric_difference(obj)
+
+        return (
+            attrname,
+            tuple(
+                (k, obj[k]._gen_cache_key(anon_map, bindparams))
+                for k in sorted(str_values)
+            ),
+        )
+
+    def visit_dml_multi_values(
+        self, attrname, obj, parent, anon_map, bindparams
+    ):
+        # multivalues are simply not cacheable right now
+        anon_map[NO_CACHE] = True
+        return ()
+
 
 _cache_key_traversal_visitor = _CacheKey()
 
@@ -404,6 +472,70 @@ class _CopyInternals(InternalTraversal):
             (key, clone(value, **kw)) for key, value in element.items()
         )
 
+    def visit_dml_ordered_values(self, parent, element, clone=_clone, **kw):
+        # sequence of 2-tuples
+        return [
+            (
+                clone(key, **kw)
+                if hasattr(key, "__clause_element__")
+                else key,
+                clone(value, **kw),
+            )
+            for key, value in element
+        ]
+
+    def visit_dml_values(self, parent, element, clone=_clone, **kw):
+        # sequence of dictionaries
+        return [
+            {
+                (
+                    clone(key, **kw)
+                    if hasattr(key, "__clause_element__")
+                    else key
+                ): clone(value, **kw)
+                for key, value in sub_element.items()
+            }
+            for sub_element in element
+        ]
+
+    def visit_dml_multi_values(self, parent, element, clone=_clone, **kw):
+        # sequence of sequences, each sequence contains a list/dict/tuple
+
+        def copy(elem):
+            if isinstance(elem, (list, tuple)):
+                return [
+                    (
+                        clone(key, **kw)
+                        if hasattr(key, "__clause_element__")
+                        else key,
+                        clone(value, **kw)
+                        if hasattr(value, "__clause_element__")
+                        else value,
+                    )
+                    for key, value in elem
+                ]
+            elif isinstance(elem, dict):
+                return {
+                    (
+                        clone(key, **kw)
+                        if hasattr(key, "__clause_element__")
+                        else key
+                    ): (
+                        clone(value, **kw)
+                        if hasattr(value, "__clause_element__")
+                        else value
+                    )
+                    for key, value in elem
+                }
+            else:
+                # TODO: use abc classes
+                assert False
+
+        return [
+            [copy(sub_element) for sub_element in sequence]
+            for sequence in element
+        ]
+
 
 _copy_internals = _CopyInternals()
 
@@ -442,6 +574,25 @@ class _GetChildren(InternalTraversal):
     def visit_clauseelement_unordered_set(self, element, **kw):
         return tuple(element)
 
+    def visit_dml_ordered_values(self, element, **kw):
+        for k, v in element:
+            if hasattr(k, "__clause_element__"):
+                yield k
+            yield v
+
+    def visit_dml_values(self, element, **kw):
+        expr_values = {k for k in element if hasattr(k, "__clause_element__")}
+        str_values = expr_values.symmetric_difference(element)
+
+        for k in sorted(str_values):
+            yield element[k]
+        for k in expr_values:
+            yield k
+            yield element[k]
+
+    def visit_dml_multi_values(self, element, **kw):
+        return ()
+
 
 _get_children = _GetChildren()
 
@@ -644,6 +795,9 @@ class TraversalComparatorStrategy(InternalTraversal, util.MemoizedSlots):
     def visit_string(self, left_parent, left, right_parent, right, **kw):
         return left == right
 
+    def visit_string_list(self, left_parent, left, right_parent, right, **kw):
+        return left == right
+
     def visit_anon_name(self, left_parent, left, right_parent, right, **kw):
         return _resolve_name_for_compare(
             left_parent, left, self.anon_map[0], **kw
@@ -663,6 +817,11 @@ class TraversalComparatorStrategy(InternalTraversal, util.MemoizedSlots):
     def visit_plain_dict(self, left_parent, left, right_parent, right, **kw):
         return left == right
 
+    def visit_dialect_options(
+        self, left_parent, left, right_parent, right, **kw
+    ):
+        return left == right
+
     def visit_plain_obj(self, left_parent, left, right_parent, right, **kw):
         return left == right
 
@@ -713,6 +872,55 @@ class TraversalComparatorStrategy(InternalTraversal, util.MemoizedSlots):
     ):
         raise NotImplementedError()
 
+    def visit_dml_ordered_values(
+        self, left_parent, left, right_parent, right, **kw
+    ):
+        # sequence of tuple pairs
+
+        for (lk, lv), (rk, rv) in util.zip_longest(
+            left, right, fillvalue=(None, None)
+        ):
+            lkce = hasattr(lk, "__clause_element__")
+            rkce = hasattr(rk, "__clause_element__")
+            if lkce != rkce:
+                return COMPARE_FAILED
+            elif lkce and not self.compare_inner(lk, rk, **kw):
+                return COMPARE_FAILED
+            elif not lkce and lk != rk:
+                return COMPARE_FAILED
+            elif not self.compare_inner(lv, rv, **kw):
+                return COMPARE_FAILED
+
+    def visit_dml_values(self, left_parent, left, right_parent, right, **kw):
+        if left is None or right is None or len(left) != len(right):
+            return COMPARE_FAILED
+
+        for lk in left:
+            lv = left[lk]
+
+            if lk not in right:
+                return COMPARE_FAILED
+            rv = right[lk]
+
+            if not self.compare_inner(lv, rv, **kw):
+                return COMPARE_FAILED
+
+    def visit_dml_multi_values(
+        self, left_parent, left, right_parent, right, **kw
+    ):
+        for lseq, rseq in util.zip_longest(left, right, fillvalue=None):
+            if lseq is None or rseq is None:
+                return COMPARE_FAILED
+
+            for ld, rd in util.zip_longest(lseq, rseq, fillvalue=None):
+                if (
+                    self.visit_dml_values(
+                        left_parent, ld, right_parent, rd, **kw
+                    )
+                    is COMPARE_FAILED
+                ):
+                    return COMPARE_FAILED
+
     def compare_clauselist(self, left, right, **kw):
         if left.operator is right.operator:
             if operators.is_associative(left.operator):
@@ -731,11 +939,11 @@ class TraversalComparatorStrategy(InternalTraversal, util.MemoizedSlots):
         if left.operator == right.operator:
             if operators.is_commutative(left.operator):
                 if (
-                    compare(left.left, right.left, **kw)
-                    and compare(left.right, right.right, **kw)
+                    self.compare_inner(left.left, right.left, **kw)
+                    and self.compare_inner(left.right, right.right, **kw)
                 ) or (
-                    compare(left.left, right.right, **kw)
-                    and compare(left.right, right.left, **kw)
+                    self.compare_inner(left.left, right.right, **kw)
+                    and self.compare_inner(left.right, right.left, **kw)
                 ):
                     return ["operator", "negate", "left", "right"]
                 else:
index fda48c65743e6e78a80d163325f591fa4d952706..a049d9bb08950208647cdf24cee103950c2587db 100644 (file)
@@ -269,6 +269,9 @@ class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
 
     """
 
+    dp_string_list = symbol("SL")
+    """Visit a list of strings."""
+
     dp_anon_name = symbol("AN")
     """Visit a potentially "anonymized" string value.
 
@@ -313,6 +316,9 @@ class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
 
     """
 
+    dp_dialect_options = symbol("DO")
+    """visit a dialect options structure."""
+
     dp_string_clauseelement_dict = symbol("CD")
     """Visit a dictionary of string keys to :class:`.ClauseElement`
     objects.
@@ -365,6 +371,21 @@ class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
 
     """
 
+    dp_dml_ordered_values = symbol("DML_OV")
+    """visit the values() ordered tuple list of an :class:`.Update` object."""
+
+    dp_dml_values = symbol("DML_V")
+    """visit the values() dictionary of a :class:`.ValuesBase
+    (e.g. Insert or Update) object.
+
+    """
+
+    dp_dml_multi_values = symbol("DML_MV")
+    """visit the values() multi-valued list of dictionaries of an
+    :class:`.Insert` object.
+
+    """
+
 
 class ExtendedInternalTraversal(InternalTraversal):
     """defines additional symbols that are useful in caching applications.
index 2770cc2397d9cdf5340a3622fe25e3ddb8befd44..b21eb44cfb19970b21cbf9055452151f4b12820c 100644 (file)
@@ -47,17 +47,10 @@ class immutabledict(ImmutableContainer, dict):
         return immutabledict, (dict(self),)
 
     def union(self, d):
-        if not d:
-            return self
-        elif not self:
-            if isinstance(d, immutabledict):
-                return d
-            else:
-                return immutabledict(d)
-        else:
-            d2 = immutabledict(self)
-            dict.update(d2, d)
-            return d2
+        new = dict.__new__(self.__class__)
+        dict.__init__(new, self)
+        dict.update(new, d)
+        return new
 
     def __repr__(self):
         return "immutabledict(%s)" % dict.__repr__(self)
index e74c37d63da69d440bbcafca446eca0de9062712..4e6199c6f2bfb075c9de2239081bf3d12722a070 100644 (file)
@@ -512,15 +512,22 @@ class SQLTest(fixtures.TestBase, AssertsCompiledSQL):
             t.update(values={"col1": 123}), "UPDATE t SET col1=%s"
         )
         self.assert_compile(
-            t.update(values={"col1": 123}, mysql_limit=5),
+            t.update()
+            .values({"col1": 123})
+            .with_dialect_options(mysql_limit=5),
             "UPDATE t SET col1=%s LIMIT 5",
         )
         self.assert_compile(
-            t.update(values={"col1": 123}, mysql_limit=None),
+            t.update()
+            .values({"col1": 123})
+            .with_dialect_options(mysql_limit=None),
             "UPDATE t SET col1=%s",
         )
         self.assert_compile(
-            t.update(t.c.col2 == 456, values={"col1": 123}, mysql_limit=1),
+            t.update()
+            .where(t.c.col2 == 456)
+            .values({"col1": 123})
+            .with_dialect_options(mysql_limit=1),
             "UPDATE t SET col1=%s WHERE t.col2 = %s LIMIT 1",
         )
 
index 107f3a9b24cb23009cc087bacf842700cf0511c6..e36b69802c6f23a6da0acb50ed9561204d7b0eaa 100644 (file)
@@ -421,7 +421,9 @@ class ExecutemanyValuesInsertsTest(ExecuteManyMode, fixtures.TablesTest):
 
         ins = t.insert(inline=True).values(
             id=bindparam("id"),
-            x=select([literal_column("5")]).select_from(self.tables.data),
+            x=select([literal_column("5")])
+            .select_from(self.tables.data)
+            .scalar_subquery(),
             y=bindparam("y"),
             z=bindparam("z"),
         )
@@ -469,7 +471,9 @@ class ExecutemanyValuesInsertsTest(ExecuteManyMode, fixtures.TablesTest):
 
         ins = t.insert(inline=True).values(
             id=bindparam("id"),
-            x=select([literal_column("5")]).select_from(self.tables.data),
+            x=select([literal_column("5")])
+            .select_from(self.tables.data)
+            .scalar_subquery(),
             y=bindparam("y"),
             z=bindparam("z"),
         )
index 235817db9ff039ea586d58edc37ef06e55ea7cee..2813c1e7e08620d2ff536201787ecec1424c1272 100644 (file)
@@ -579,7 +579,7 @@ class ClauseAttributesTest(fixtures.MappedTest):
     def test_insert(self):
         User = self.classes.User
 
-        u = User(name="test", counter=sa.select([5]))
+        u = User(name="test", counter=sa.select([5]).scalar_subquery())
 
         session = create_session()
         session.add(u)
index 3c5c075f5f39150580a2923565c5bcfd927241fd..4384fa67eb8d124785a07673d2a4759ad836af09 100644 (file)
@@ -20,7 +20,6 @@ from sqlalchemy.testing import assert_raises
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing import fixtures
-from sqlalchemy.testing import is_
 from sqlalchemy.testing import mock
 from sqlalchemy.testing.schema import Column
 from sqlalchemy.testing.schema import Table
@@ -680,8 +679,8 @@ class UpdateDeleteTest(fixtures.MappedTest):
         with mock.patch.object(q, "_execute_crud") as exec_:
             q.filter(User.id == 15).update({"name": "foob", "id": 123})
             # Confirm that parameters are a dict instead of tuple or list
-            params_type = type(exec_.mock_calls[0][1][0].parameters)
-            is_(params_type, dict)
+            params = exec_.mock_calls[0][1][0]._values
+            assert isinstance(params, dict)
 
     def test_update_preserve_parameter_order(self):
         User = self.classes.User
@@ -695,7 +694,7 @@ class UpdateDeleteTest(fixtures.MappedTest):
                 update_args={"preserve_parameter_order": True},
             )
             cols = [
-                c.key for c in exec_.mock_calls[0][1][0]._parameter_ordering
+                c.key for c, v in exec_.mock_calls[0][1][0]._ordered_values
             ]
             eq_(["id", "name"], cols)
 
@@ -708,7 +707,7 @@ class UpdateDeleteTest(fixtures.MappedTest):
                 update_args={"preserve_parameter_order": True},
             )
             cols = [
-                c.key for c in exec_.mock_calls[0][1][0]._parameter_ordering
+                c.key for c, v in exec_.mock_calls[0][1][0]._ordered_values
             ]
             eq_(["name", "id"], cols)
 
index a6e4b184507af40d148afdf237a08d1cbd609c52..be9aa2d584d0043b6e760b25fd210cf7df3a0803 100644 (file)
 # /home/classic/dev/sqlalchemy/test/profiles.txt
 # This file is written out on a per-environment basis.
-# For each test in aaa_profiling, the corresponding function and
+# For each test in aaa_profiling, the corresponding function and 
 # environment is located within this file.  If it doesn't exist,
 # the test is skipped.
-# If a callcount does exist, it is compared to what we received.
+# If a callcount does exist, it is compared to what we received. 
 # assertions are raised if the counts do not match.
-#
-# To add a new callcount test, apply the function_call_count
-# decorator and re-run the tests using the --write-profiles
+# 
+# To add a new callcount test, apply the function_call_count 
+# decorator and re-run the tests using the --write-profiles 
 # option - this file will be rewritten including the new count.
-#
+# 
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert
 
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_cextensions 72
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 72
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_cextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mssql_pyodbc_dbapiunicode_nocextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_cextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_dbapiunicode_nocextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_cextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_pymysql_dbapiunicode_nocextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_cextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_cextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_cextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 61
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mssql_pyodbc_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_pymysql_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 66
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_select
 
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_cextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_nocextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_cextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_nocextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_cextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_nocextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_cextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_cextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_cextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 164
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_cextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_nocextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_cextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_nocextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_cextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_nocextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_cextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_cextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_cextensions 177
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 177
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_cextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mssql_pyodbc_dbapiunicode_nocextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_cextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_dbapiunicode_nocextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_cextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_pymysql_dbapiunicode_nocextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_cextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_cextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_cextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 153
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_cextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mssql_pyodbc_dbapiunicode_nocextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_cextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_mysqldb_dbapiunicode_nocextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_cextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_mysql_pymysql_dbapiunicode_nocextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_cextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_cextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_cextensions 166
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 166
 
 # 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 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mssql_pyodbc_dbapiunicode_nocextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_dbapiunicode_cextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_dbapiunicode_nocextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_pymysql_dbapiunicode_cextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_pymysql_dbapiunicode_nocextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_dbapiunicode_cextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_dbapiunicode_cextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_dbapiunicode_cextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 195
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_cextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_nocextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_cextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_nocextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_cextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_nocextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_cextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_cextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_cextensions 208
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 208
+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 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mssql_pyodbc_dbapiunicode_nocextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_cextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_mysqldb_dbapiunicode_nocextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_cextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_mysql_pymysql_dbapiunicode_nocextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_cextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_cextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_cextensions 195
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 195
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_update
 
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_cextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_nocextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_cextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_nocextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_cextensions 77
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_nocextensions 77
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_cextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_cextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_cextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 79
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_cextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_nocextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_cextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_nocextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_cextensions 80
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_nocextensions 80
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_cextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_cextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_cextensions 82
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 82
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mssql_pyodbc_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_pymysql_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_cextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mssql_pyodbc_dbapiunicode_nocextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_cextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_nocextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_cextensions 69
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_pymysql_dbapiunicode_nocextensions 69
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_cextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_cextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_cextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 71
 
 # 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 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mssql_pyodbc_dbapiunicode_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_dbapiunicode_cextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_dbapiunicode_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_pymysql_dbapiunicode_cextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_pymysql_dbapiunicode_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_cextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_dbapiunicode_cextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_dbapiunicode_cextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_cextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_nocextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_cextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_nocextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_cextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_nocextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_cextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_cextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_cextensions 156
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 156
+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 148
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 148
+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 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mssql_pyodbc_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_mysqldb_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_mysql_pymysql_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_cextensions 155
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 155
 
 # 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 4302
+test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 2.7_sqlite_pysqlite_dbapiunicode_cextensions 4702
 test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 4302
 test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 3.7_sqlite_pysqlite_dbapiunicode_cextensions 4903
 test.aaa_profiling.test_misc.CacheKeyTest.test_statement_one 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 4903
@@ -162,64 +162,64 @@ 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 49005
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 64705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 50605
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 64405
+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: 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 48505
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 64205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_cextensions 50105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 63905
+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: 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 47405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48505
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 59105
+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: 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 46805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 47905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58505
+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: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle
 
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_cextensions 40205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 46305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_cextensions 42405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 48805
+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: 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 47405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 48505
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 59105
+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: 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 46805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 59205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_cextensions 47905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 58505
+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: 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 26905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 30705
+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: 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 26305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 30105
+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
 
@@ -267,15 +267,15 @@ test.aaa_profiling.test_orm.BranchedOptionTest.test_query_opts_unbound_branching
 
 # TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline
 
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_cextensions 17182
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 38188
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.7_sqlite_pysqlite_dbapiunicode_cextensions 17209
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 38218
+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: 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 23250
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 32256
+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
 
@@ -295,31 +295,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 99938
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 99938
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_cextensions 107874
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 107874
+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: 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 97538
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 97538
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_cextensions 105739
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 105739
+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: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query
 
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_cextensions 458478
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 458483
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_cextensions 489144
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 489144
+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: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results
 
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 458245
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 488845
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.7_sqlite_pysqlite_dbapiunicode_cextensions 463655
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 493955
+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: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity
 
@@ -330,24 +330,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 90023
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 93725
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.7_sqlite_pysqlite_dbapiunicode_cextensions 91798
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 95802
+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: 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 18540
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 19006
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.7_sqlite_pysqlite_dbapiunicode_cextensions 19167
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 19719
+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: test.aaa_profiling.test_orm.MergeTest.test_merge_load
 
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1027
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1061
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiunicode_cextensions 1062
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 1100
+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: test.aaa_profiling.test_orm.MergeTest.test_merge_no_load
 
@@ -358,24 +358,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 5816
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 7096
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_sqlite_pysqlite_dbapiunicode_cextensions 5994
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 7284
+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: 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 173671
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 195075
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.7_sqlite_pysqlite_dbapiunicode_cextensions 177890
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 197698
+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: test.aaa_profiling.test_orm.SessionTest.test_expire_lots
 
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1158
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1124
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.7_sqlite_pysqlite_dbapiunicode_cextensions 1265
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 1243
+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: test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect
 
@@ -420,176 +420,176 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute
 
 # TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute
 
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mssql_pyodbc_dbapiunicode_cextensions 91
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mssql_pyodbc_dbapiunicode_nocextensions 95
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_dbapiunicode_cextensions 91
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_dbapiunicode_nocextensions 95
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_pymysql_dbapiunicode_cextensions 91
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_pymysql_dbapiunicode_nocextensions 95
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_oracle_cx_oracle_dbapiunicode_cextensions 91
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 95
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_dbapiunicode_cextensions 91
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 95
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_dbapiunicode_cextensions 91
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 95
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mssql_pyodbc_dbapiunicode_cextensions 93
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mssql_pyodbc_dbapiunicode_nocextensions 97
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_mysqldb_dbapiunicode_cextensions 93
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_mysqldb_dbapiunicode_nocextensions 97
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_pymysql_dbapiunicode_cextensions 93
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_pymysql_dbapiunicode_nocextensions 97
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_oracle_cx_oracle_dbapiunicode_cextensions 93
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 97
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_postgresql_psycopg2_dbapiunicode_cextensions 93
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 97
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_sqlite_pysqlite_dbapiunicode_cextensions 93
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mssql_pyodbc_dbapiunicode_cextensions 93
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mssql_pyodbc_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_dbapiunicode_cextensions 93
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_mysqldb_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_pymysql_dbapiunicode_cextensions 93
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_mysql_pymysql_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_oracle_cx_oracle_dbapiunicode_cextensions 93
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_dbapiunicode_cextensions 93
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_dbapiunicode_cextensions 93
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 97
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mssql_pyodbc_dbapiunicode_cextensions 95
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mssql_pyodbc_dbapiunicode_nocextensions 99
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_mysqldb_dbapiunicode_cextensions 95
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_mysqldb_dbapiunicode_nocextensions 99
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_pymysql_dbapiunicode_cextensions 95
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_mysql_pymysql_dbapiunicode_nocextensions 99
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_oracle_cx_oracle_dbapiunicode_cextensions 95
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 99
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_postgresql_psycopg2_dbapiunicode_cextensions 95
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 99
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_sqlite_pysqlite_dbapiunicode_cextensions 95
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 99
 
 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mssql_pyodbc_dbapiunicode_cextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mssql_pyodbc_dbapiunicode_nocextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_dbapiunicode_cextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_dbapiunicode_nocextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_pymysql_dbapiunicode_cextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_pymysql_dbapiunicode_nocextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_oracle_cx_oracle_dbapiunicode_cextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_dbapiunicode_cextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_dbapiunicode_cextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mssql_pyodbc_dbapiunicode_cextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mssql_pyodbc_dbapiunicode_nocextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_mysqldb_dbapiunicode_cextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_mysqldb_dbapiunicode_nocextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_pymysql_dbapiunicode_cextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_pymysql_dbapiunicode_nocextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_oracle_cx_oracle_dbapiunicode_cextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_postgresql_psycopg2_dbapiunicode_cextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_sqlite_pysqlite_dbapiunicode_cextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mssql_pyodbc_dbapiunicode_cextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mssql_pyodbc_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_dbapiunicode_cextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_pymysql_dbapiunicode_cextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_pymysql_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_oracle_cx_oracle_dbapiunicode_cextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_dbapiunicode_cextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_dbapiunicode_cextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mssql_pyodbc_dbapiunicode_cextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mssql_pyodbc_dbapiunicode_nocextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_mysqldb_dbapiunicode_cextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_mysqldb_dbapiunicode_nocextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_pymysql_dbapiunicode_cextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_mysql_pymysql_dbapiunicode_nocextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_oracle_cx_oracle_dbapiunicode_cextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_postgresql_psycopg2_dbapiunicode_cextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_sqlite_pysqlite_dbapiunicode_cextensions 17
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 17
 
 # 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 272
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6274
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 314
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6336
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122270
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 128272
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 381
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36423
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 285
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6307
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 253
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6275
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mssql_pyodbc_dbapiunicode_cextensions 257
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6261
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 290
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6294
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_pymysql_dbapiunicode_cextensions 88045
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_mysql_pymysql_dbapiunicode_nocextensions 94049
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_oracle_cx_oracle_dbapiunicode_cextensions 346
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6350
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_postgresql_psycopg2_dbapiunicode_cextensions 281
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6285
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_sqlite_pysqlite_dbapiunicode_cextensions 247
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6251
+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: test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_cextensions 272
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6274
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 314
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6336
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122270
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 128272
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 381
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36423
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 285
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6307
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 253
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6275
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mssql_pyodbc_dbapiunicode_cextensions 257
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6261
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 290
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6294
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_pymysql_dbapiunicode_cextensions 88045
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_mysql_pymysql_dbapiunicode_nocextensions 94049
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_oracle_cx_oracle_dbapiunicode_cextensions 346
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6350
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_postgresql_psycopg2_dbapiunicode_cextensions 281
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6285
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_sqlite_pysqlite_dbapiunicode_cextensions 247
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6251
+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: test.aaa_profiling.test_resultset.ResultSetTest.test_string
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_cextensions 534
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6536
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 539
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6541
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122505
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 128507
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 565
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36587
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 509
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6511
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 463
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6465
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mssql_pyodbc_dbapiunicode_cextensions 533
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6537
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 538
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6542
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_pymysql_dbapiunicode_cextensions 88294
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_pymysql_dbapiunicode_nocextensions 94298
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_oracle_cx_oracle_dbapiunicode_cextensions 564
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6568
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_postgresql_psycopg2_dbapiunicode_cextensions 529
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6533
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_sqlite_pysqlite_dbapiunicode_cextensions 480
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6484
+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: test.aaa_profiling.test_resultset.ResultSetTest.test_unicode
 
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_cextensions 534
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6536
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 539
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 6541
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122505
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 128507
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 565
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 36587
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 509
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6511
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 463
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6465
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mssql_pyodbc_dbapiunicode_cextensions 533
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mssql_pyodbc_dbapiunicode_nocextensions 6537
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 538
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_nocextensions 6542
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_pymysql_dbapiunicode_cextensions 88294
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_pymysql_dbapiunicode_nocextensions 94298
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_oracle_cx_oracle_dbapiunicode_cextensions 564
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_oracle_cx_oracle_dbapiunicode_nocextensions 6568
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_postgresql_psycopg2_dbapiunicode_cextensions 529
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6533
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_sqlite_pysqlite_dbapiunicode_cextensions 480
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 6484
+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: test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation
 
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_cextensions 6685,336,4367,12924,1322,2274,2821
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6740,336,4431,13486,1445,2290,3005
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_cextensions 6474,318,4335,12868,1309,2223,2872
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 6551,318,4415,13476,1440,2245,3066
+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: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation
 
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_cextensions 7162,444,7247,18830,1335,2890
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 7338,454,7479,21133,1448,2949
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_cextensions 7199,435,7459,19425,1323,2974
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_invocation 3.7_postgresql_psycopg2_dbapiunicode_nocextensions 7347,443,7715,21791,1444,3042
+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
index fb6c515e472061fdb8a3824c4e9042f8316b1bdb..18524409493fbe2d42695024b07729a9c81132af 100644 (file)
@@ -25,9 +25,12 @@ from sqlalchemy import tuple_
 from sqlalchemy import union
 from sqlalchemy import union_all
 from sqlalchemy import util
+from sqlalchemy.dialects import mysql
+from sqlalchemy.dialects import postgresql
 from sqlalchemy.schema import Sequence
 from sqlalchemy.sql import bindparam
 from sqlalchemy.sql import ColumnElement
+from sqlalchemy.sql import dml
 from sqlalchemy.sql import False_
 from sqlalchemy.sql import func
 from sqlalchemy.sql import operators
@@ -57,7 +60,6 @@ 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
@@ -96,6 +98,11 @@ class MyEntity(HasCacheKey):
     ]
 
 
+dml.Insert.argument_for("sqlite", "foo", None)
+dml.Update.argument_for("sqlite", "foo", None)
+dml.Delete.argument_for("sqlite", "foo", None)
+
+
 class CoreFixtures(object):
     # lambdas which return a tuple of ColumnElement objects.
     # must return at least two objects that should compare differently.
@@ -328,6 +335,64 @@ class CoreFixtures(object):
                 func.bernoulli(1), name="bar", seed=func.random()
             ),
         ),
+        lambda: (
+            table_a.insert(),
+            table_a.insert().values({})._annotate({"nocache": True}),
+            table_b.insert(),
+            table_b.insert().with_dialect_options(sqlite_foo="some value"),
+            table_b.insert().from_select(["a", "b"], select([table_a])),
+            table_b.insert().from_select(
+                ["a", "b"], select([table_a]).where(table_a.c.a > 5)
+            ),
+            table_b.insert().from_select(["a", "b"], select([table_b])),
+            table_b.insert().from_select(["c", "d"], select([table_a])),
+            table_b.insert().returning(table_b.c.a),
+            table_b.insert().returning(table_b.c.a, table_b.c.b),
+            table_b.insert().inline(),
+            table_b.insert().prefix_with("foo"),
+            table_b.insert().with_hint("RUNFAST"),
+            table_b.insert().values(a=5, b=10),
+            table_b.insert().values(a=5),
+            table_b.insert()
+            .values({table_b.c.a: 5, "b": 10})
+            ._annotate({"nocache": True}),
+            table_b.insert().values(a=7, b=10),
+            table_b.insert().values(a=5, b=10).inline(),
+            table_b.insert()
+            .values([{"a": 5, "b": 10}, {"a": 8, "b": 12}])
+            ._annotate({"nocache": True}),
+        ),
+        lambda: (
+            table_b.update(),
+            table_b.update().where(table_b.c.a == 5),
+            table_b.update().where(table_b.c.b == 5),
+            table_b.update()
+            .where(table_b.c.b == 5)
+            .with_dialect_options(mysql_limit=10),
+            table_b.update()
+            .where(table_b.c.b == 5)
+            .with_dialect_options(mysql_limit=10, sqlite_foo="some value"),
+            table_b.update().where(table_b.c.a == 5).values(a=5, b=10),
+            table_b.update().where(table_b.c.a == 5).values(a=5, b=10, c=12),
+            table_b.update()
+            .where(table_b.c.b == 5)
+            .values(a=5, b=10)
+            ._annotate({"nocache": True}),
+            table_b.update().values(a=5, b=10),
+            table_b.update()
+            .values({"a": 5, table_b.c.b: 10})
+            ._annotate({"nocache": True}),
+            table_b.update().values(a=7, b=10),
+            table_b.update().ordered_values(("a", 5), ("b", 10)),
+            table_b.update().ordered_values(("b", 10), ("a", 5)),
+            table_b.update().ordered_values((table_b.c.a, 5), ("b", 10)),
+        ),
+        lambda: (
+            table_b.delete(),
+            table_b.delete().with_dialect_options(sqlite_foo="some value"),
+            table_b.delete().where(table_b.c.a == 5),
+            table_b.delete().where(table_b.c.b == 5),
+        ),
         lambda: (
             select([table_a.c.a]),
             select([table_a.c.a]).prefix_with("foo"),
@@ -490,8 +555,12 @@ class CacheKeyFixture(object):
             if a == b:
                 a_key = case_a[a]._generate_cache_key()
                 b_key = case_b[b]._generate_cache_key()
-                is_not_(a_key, None)
-                is_not_(b_key, None)
+
+                if a_key is None:
+                    assert case_a[a]._annotations.get("nocache")
+
+                    assert b_key is None
+                    continue
 
                 eq_(a_key.key, b_key.key)
                 eq_(hash(a_key), hash(b_key))
@@ -506,6 +575,13 @@ class CacheKeyFixture(object):
                 a_key = case_a[a]._generate_cache_key()
                 b_key = case_b[b]._generate_cache_key()
 
+                if a_key is None or b_key is None:
+                    if a_key is None:
+                        assert case_a[a]._annotations.get("nocache")
+                    if b_key is None:
+                        assert case_b[b]._annotations.get("nocache")
+                    continue
+
                 if a_key.key == b_key.key:
                     for a_param, b_param in zip(
                         a_key.bindparams, b_key.bindparams
@@ -562,7 +638,18 @@ class CacheKeyFixture(object):
 
 
 class CacheKeyTest(CacheKeyFixture, CoreFixtures, fixtures.TestBase):
-    @testing.combinations(table_a.update(), table_a.insert(), table_a.delete())
+    # we are slightly breaking the policy of not having external dialect
+    # stuff in here, but use pg/mysql as test cases to ensure that these
+    # objects don't report an inaccurate cache key, which is dependent
+    # on the base insert sending out _post_values_clause and the caching
+    # system properly recognizing these constructs as not cacheable
+
+    @testing.combinations(
+        postgresql.insert(table_a).on_conflict_do_update(
+            index_elements=[table_a.c.a], set_={"name": "foo"}
+        ),
+        mysql.insert(table_a).on_duplicate_key_update(updated_once=None),
+    )
     def test_dml_not_cached_yet(self, dml_stmt):
         eq_(dml_stmt._generate_cache_key(), None)
 
index 5030f9df847af0da964e3aaaf4c42cd32800ae9f..6b1f443e29111564cb2901a9db40fa700e5ebda9 100644 (file)
@@ -4818,7 +4818,11 @@ class ResultMapTest(fixtures.TestBase):
         Table("t1", m, astring)
         t2 = Table("t2", m, aint)
 
-        stmt = t2.insert().values(a=select([astring])).returning(aint)
+        stmt = (
+            t2.insert()
+            .values(a=select([astring]).scalar_subquery())
+            .returning(aint)
+        )
         comp = stmt.compile(dialect=postgresql.dialect())
         eq_(
             comp._create_result_map(),
index 831f2a680c06f2a7d11cd82edb1a29ead31d9a11..957fa890a939d0fab4781a8d4353148462cddf95 100644 (file)
@@ -658,16 +658,18 @@ class DefaultTest(fixtures.TestBase):
                 [const],
             )
             assert_raises_message(
-                sa.exc.InvalidRequestError,
-                "cannot be used directly as a column expression.",
-                str,
-                t.insert().values(col4=const),
+                sa.exc.ArgumentError,
+                "SQL expression element expected, got %s"
+                % const.__class__.__name__,
+                t.insert().values,
+                col4=const,
             )
             assert_raises_message(
-                sa.exc.InvalidRequestError,
-                "cannot be used directly as a column expression.",
-                str,
-                t.update().values(col4=const),
+                sa.exc.ArgumentError,
+                "SQL expression element expected, got %s"
+                % const.__class__.__name__,
+                t.update().values,
+                col4=const,
             )
 
     def test_missing_many_param(self):
index 06fe22fed28de73fa419eac91e3d06d18d8bf854..5b7b3bd1fa5b4d2c782df8af812db8707f9b43f3 100644 (file)
@@ -22,6 +22,7 @@ from sqlalchemy import String
 from sqlalchemy import table
 from sqlalchemy import testing
 from sqlalchemy import text
+from sqlalchemy import update
 from sqlalchemy import util
 from sqlalchemy import VARCHAR
 from sqlalchemy.engine import default
@@ -1823,3 +1824,75 @@ class DMLTest(fixtures.TestBase, AssertsCompiledSQL):
             "col3=:col3",
             inline_flag=True,
         )
+
+    def test_update_dialect_kwargs(self):
+        t = table("foo", column("bar"))
+
+        with testing.expect_deprecated_20("Passing dialect keyword arguments"):
+            stmt = t.update(mysql_limit=10)
+
+        self.assert_compile(
+            stmt, "UPDATE foo SET bar=%s LIMIT 10", dialect="mysql"
+        )
+
+    @testing.fixture()
+    def update_from_fixture(self):
+        metadata = MetaData()
+
+        mytable = Table(
+            "mytable",
+            metadata,
+            Column("myid", Integer),
+            Column("name", String(30)),
+            Column("description", String(50)),
+        )
+        myothertable = Table(
+            "myothertable",
+            metadata,
+            Column("otherid", Integer),
+            Column("othername", String(30)),
+        )
+        return mytable, myothertable
+
+    def test_correlated_update_two(self, update_from_fixture):
+        table1, t2 = update_from_fixture
+
+        mt = table1.alias()
+        with testing.expect_deprecated(
+            "coercing SELECT object to scalar subquery in a column-expression "
+            "context is deprecated"
+        ):
+            u = update(
+                table1,
+                values={
+                    table1.c.name: select(
+                        [mt.c.name], mt.c.myid == table1.c.myid
+                    )
+                },
+            )
+        self.assert_compile(
+            u,
+            "UPDATE mytable SET name=(SELECT mytable_1.name FROM "
+            "mytable AS mytable_1 WHERE "
+            "mytable_1.myid = mytable.myid)",
+        )
+
+    def test_correlated_update_three(self, update_from_fixture):
+        table1, table2 = update_from_fixture
+
+        # test against a regular constructed subquery
+        s = select([table2], table2.c.otherid == table1.c.myid)
+        with testing.expect_deprecated(
+            "coercing SELECT object to scalar subquery in a column-expression "
+            "context is deprecated"
+        ):
+            u = update(
+                table1, table1.c.name == "jack", values={table1.c.name: s}
+            )
+        self.assert_compile(
+            u,
+            "UPDATE mytable SET name=(SELECT myothertable.otherid, "
+            "myothertable.othername FROM myothertable WHERE "
+            "myothertable.otherid = mytable.myid) "
+            "WHERE mytable.name = :name_1",
+        )
index 84d99d886ccf63d515d768c858710e6a8b4ba0a4..2a82c2cc1ed49b5c87385506ce7cf327b75771ab 100644 (file)
@@ -7,6 +7,7 @@ from sqlalchemy import extract
 from sqlalchemy import ForeignKey
 from sqlalchemy import func
 from sqlalchemy import Integer
+from sqlalchemy import literal
 from sqlalchemy import literal_column
 from sqlalchemy import MetaData
 from sqlalchemy import select
@@ -37,7 +38,6 @@ from sqlalchemy.testing import fixtures
 from sqlalchemy.testing import is_
 from sqlalchemy.testing import is_not_
 
-
 A = B = t1 = t2 = t3 = table1 = table2 = table3 = table4 = None
 
 
@@ -1961,29 +1961,53 @@ class ValuesBaseTest(fixtures.TestBase, AssertsCompiledSQL):
 
     def test_add_kwarg(self):
         i = t1.insert()
-        eq_(i.parameters, None)
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+        eq_(compile_state._dict_parameters, None)
         i = i.values(col1=5)
-        eq_(i.parameters, {"col1": 5})
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+        self._compare_param_dict(compile_state._dict_parameters, {"col1": 5})
         i = i.values(col2=7)
-        eq_(i.parameters, {"col1": 5, "col2": 7})
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+        self._compare_param_dict(
+            compile_state._dict_parameters, {"col1": 5, "col2": 7}
+        )
 
     def test_via_tuple_single(self):
         i = t1.insert()
-        eq_(i.parameters, None)
+
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+        eq_(compile_state._dict_parameters, None)
+
         i = i.values((5, 6, 7))
-        eq_(i.parameters, {"col1": 5, "col2": 6, "col3": 7})
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+
+        self._compare_param_dict(
+            compile_state._dict_parameters, {"col1": 5, "col2": 6, "col3": 7},
+        )
 
     def test_kw_and_dict_simultaneously_single(self):
         i = t1.insert()
-        i = i.values({"col1": 5}, col2=7)
-        eq_(i.parameters, {"col1": 5, "col2": 7})
+        assert_raises_message(
+            exc.ArgumentError,
+            r"Can't pass positional and kwargs to values\(\) simultaneously",
+            i.values,
+            {"col1": 5},
+            col2=7,
+        )
 
     def test_via_tuple_multi(self):
         i = t1.insert()
-        eq_(i.parameters, None)
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+        eq_(compile_state._dict_parameters, None)
+
         i = i.values([(5, 6, 7), (8, 9, 10)])
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
         eq_(
-            i.parameters,
+            compile_state._dict_parameters, {"col1": 5, "col2": 6, "col3": 7},
+        )
+        eq_(compile_state._has_multi_parameters, True)
+        eq_(
+            compile_state._multi_parameters,
             [
                 {"col1": 5, "col2": 6, "col3": 7},
                 {"col1": 8, "col2": 9, "col3": 10},
@@ -1992,58 +2016,92 @@ class ValuesBaseTest(fixtures.TestBase, AssertsCompiledSQL):
 
     def test_inline_values_single(self):
         i = t1.insert(values={"col1": 5})
-        eq_(i.parameters, {"col1": 5})
-        is_(i._has_multi_parameters, False)
+
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+
+        self._compare_param_dict(compile_state._dict_parameters, {"col1": 5})
+        is_(compile_state._has_multi_parameters, False)
 
     def test_inline_values_multi(self):
         i = t1.insert(values=[{"col1": 5}, {"col1": 6}])
-        eq_(i.parameters, [{"col1": 5}, {"col1": 6}])
-        is_(i._has_multi_parameters, True)
+
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+
+        # multiparams are not converted to bound parameters
+        eq_(compile_state._dict_parameters, {"col1": 5})
+
+        # multiparams are not converted to bound parameters
+        eq_(compile_state._multi_parameters, [{"col1": 5}, {"col1": 6}])
+        is_(compile_state._has_multi_parameters, True)
+
+    def _compare_param_dict(self, a, b):
+        if list(a) != list(b):
+            return False
+
+        from sqlalchemy.types import NullType
+
+        for a_k, a_i in a.items():
+            b_i = b[a_k]
+
+            # compare BindParameter on the left to
+            # literal value on the right
+            assert a_i.compare(literal(b_i, type_=NullType()))
 
     def test_add_dictionary(self):
         i = t1.insert()
-        eq_(i.parameters, None)
+
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+
+        eq_(compile_state._dict_parameters, None)
         i = i.values({"col1": 5})
-        eq_(i.parameters, {"col1": 5})
-        is_(i._has_multi_parameters, False)
+
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+
+        self._compare_param_dict(compile_state._dict_parameters, {"col1": 5})
+        is_(compile_state._has_multi_parameters, False)
 
         i = i.values({"col1": 6})
         # note replaces
-        eq_(i.parameters, {"col1": 6})
-        is_(i._has_multi_parameters, False)
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+
+        self._compare_param_dict(compile_state._dict_parameters, {"col1": 6})
+        is_(compile_state._has_multi_parameters, False)
 
         i = i.values({"col2": 7})
-        eq_(i.parameters, {"col1": 6, "col2": 7})
-        is_(i._has_multi_parameters, False)
+        compile_state = i._compile_state_cls(i, None, isinsert=True)
+        self._compare_param_dict(
+            compile_state._dict_parameters, {"col1": 6, "col2": 7}
+        )
+        is_(compile_state._has_multi_parameters, False)
 
     def test_add_kwarg_disallowed_multi(self):
         i = t1.insert()
         i = i.values([{"col1": 5}, {"col1": 7}])
+        i = i.values(col2=7)
         assert_raises_message(
             exc.InvalidRequestError,
-            "This construct already has multiple parameter sets.",
-            i.values,
-            col2=7,
+            "Can't mix single and multiple VALUES formats",
+            i.compile,
         )
 
     def test_cant_mix_single_multi_formats_dict_to_list(self):
         i = t1.insert().values(col1=5)
+        i = i.values([{"col1": 6}])
         assert_raises_message(
-            exc.ArgumentError,
-            "Can't mix single-values and multiple values "
-            "formats in one statement",
-            i.values,
-            [{"col1": 6}],
+            exc.InvalidRequestError,
+            "Can't mix single and multiple VALUES "
+            "formats in one INSERT statement",
+            i.compile,
         )
 
     def test_cant_mix_single_multi_formats_list_to_dict(self):
         i = t1.insert().values([{"col1": 6}])
+        i = i.values({"col1": 5})
         assert_raises_message(
-            exc.ArgumentError,
-            "Can't mix single-values and multiple values "
-            "formats in one statement",
-            i.values,
-            {"col1": 5},
+            exc.InvalidRequestError,
+            "Can't mix single and multiple VALUES "
+            "formats in one INSERT statement",
+            i.compile,
         )
 
     def test_erroneous_multi_args_dicts(self):
@@ -2072,7 +2130,7 @@ class ValuesBaseTest(fixtures.TestBase, AssertsCompiledSQL):
         i = t1.insert()
         assert_raises_message(
             exc.ArgumentError,
-            "Can't pass kwargs and multiple parameter sets simultaneously",
+            r"Can't pass positional and kwargs to values\(\) simultaneously",
             i.values,
             [{"col1": 5}],
             col2=7,
@@ -2080,17 +2138,18 @@ class ValuesBaseTest(fixtures.TestBase, AssertsCompiledSQL):
 
     def test_update_no_support_multi_values(self):
         u = t1.update()
+        u = u.values([{"col1": 5}, {"col1": 7}])
         assert_raises_message(
             exc.InvalidRequestError,
-            "This construct does not support multiple parameter sets.",
-            u.values,
-            [{"col1": 5}, {"col1": 7}],
+            "UPDATE construct does not support multiple parameter sets.",
+            u.compile,
         )
 
     def test_update_no_support_multi_constructor(self):
+        stmt = t1.update(values=[{"col1": 5}, {"col1": 7}])
+
         assert_raises_message(
             exc.InvalidRequestError,
-            "This construct does not support multiple parameter sets.",
-            t1.update,
-            values=[{"col1": 5}, {"col1": 7}],
+            "UPDATE construct does not support multiple parameter sets.",
+            stmt.compile,
         )
index 7508ee6c71e591561bd8d7d0e760d1d45de15b52..8a067b65aadbbcb6050af335f2a739b83579672d 100644 (file)
@@ -13,6 +13,7 @@ from sqlalchemy import Sequence
 from sqlalchemy import String
 from sqlalchemy import Table
 from sqlalchemy import table
+from sqlalchemy import testing
 from sqlalchemy import text
 from sqlalchemy.dialects import mysql
 from sqlalchemy.dialects import postgresql
@@ -256,7 +257,7 @@ class InsertTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             "INSERT INTO mytable (myid, name) VALUES (:userid, :username)",
         )
 
-    def test_insert_values(self):
+    def test_insert_values_multiple(self):
         table1 = self.tables.mytable
 
         values1 = {table1.c.myid: bindparam("userid")}
@@ -961,7 +962,8 @@ class EmptyTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             dialect=dialect,
         )
 
-    def _test_insert_with_empty_collection_values(self, collection):
+    @testing.combinations(([],), ({},), ((),))
+    def test_insert_with_empty_collection_values(self, collection):
         table1 = self.tables.mytable
 
         ins = table1.insert().values(collection)
@@ -977,15 +979,6 @@ class EmptyTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             checkparams={"myid": 3},
         )
 
-    def test_insert_with_empty_list_values(self):
-        self._test_insert_with_empty_collection_values([])
-
-    def test_insert_with_empty_dict_values(self):
-        self._test_insert_with_empty_collection_values({})
-
-    def test_insert_with_empty_tuple_values(self):
-        self._test_insert_with_empty_collection_values(())
-
 
 class MultirowTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
     __dialect__ = "default"
@@ -1123,6 +1116,62 @@ class MultirowTest(_InsertTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             dialect=dialect,
         )
 
+    def test_mix_single_and_multi_single_first(self):
+        table1 = self.tables.mytable
+
+        stmt = table1.insert().values(myid=1, name="d1")
+        stmt = stmt.values(
+            [{"myid": 2, "name": "d2"}, {"myid": 3, "name": "d3"}]
+        )
+
+        assert_raises_message(
+            exc.InvalidRequestError,
+            "Can't mix single and multiple VALUES formats in one "
+            "INSERT statement",
+            stmt.compile,
+        )
+
+    def test_mix_single_and_multi_multi_first(self):
+        table1 = self.tables.mytable
+
+        stmt = table1.insert().values(
+            [{"myid": 2, "name": "d2"}, {"myid": 3, "name": "d3"}]
+        )
+
+        stmt = stmt.values(myid=1, name="d1")
+
+        assert_raises_message(
+            exc.InvalidRequestError,
+            "Can't mix single and multiple VALUES formats in one "
+            "INSERT statement",
+            stmt.compile,
+        )
+
+    def test_multi_multi(self):
+        table1 = self.tables.mytable
+
+        stmt = table1.insert().values([{"myid": 1, "name": "d1"}])
+
+        stmt = stmt.values(
+            [{"myid": 2, "name": "d2"}, {"myid": 3, "name": "d3"}]
+        )
+
+        self.assert_compile(
+            stmt,
+            "INSERT INTO mytable (myid, name) VALUES (%(myid_m0)s, "
+            "%(name_m0)s), (%(myid_m1)s, %(name_m1)s), (%(myid_m2)s, "
+            "%(name_m2)s)",
+            checkparams={
+                "myid_m0": 1,
+                "name_m0": "d1",
+                "myid_m1": 2,
+                "name_m1": "d2",
+                "myid_m2": 3,
+                "name_m2": "d3",
+            },
+            dialect=postgresql.dialect(),
+        )
+
     def test_inline_default(self):
         metadata = MetaData()
         table = Table(
index 78eecdf21c2357818844c7e1c3c7b09e3bda7ba4..22c4b17434836467a2c0e6201cc7c00dc4ec3dfd 100644 (file)
@@ -148,7 +148,9 @@ class UpdateTest(_UpdateFromTestBase, fixtures.TablesTest, AssertsCompiledSQL):
         u = update(
             table1,
             values={
-                table1.c.name: select([mt.c.name], mt.c.myid == table1.c.myid)
+                table1.c.name: select(
+                    [mt.c.name], mt.c.myid == table1.c.myid
+                ).scalar_subquery()
             },
         )
         self.assert_compile(
@@ -163,7 +165,9 @@ class UpdateTest(_UpdateFromTestBase, fixtures.TablesTest, AssertsCompiledSQL):
         table2 = self.tables.myothertable
 
         # test against a regular constructed subquery
-        s = select([table2], table2.c.otherid == table1.c.myid)
+        s = select(
+            [table2], table2.c.otherid == table1.c.myid
+        ).scalar_subquery()
         u = update(table1, table1.c.name == "jack", values={table1.c.name: s})
         self.assert_compile(
             u,
@@ -630,6 +634,45 @@ class UpdateTest(_UpdateFromTestBase, fixtures.TablesTest, AssertsCompiledSQL):
             "mytable.name = :param_2 || mytable.name || :param_3",
         )
 
+    def test_update_ordered_parameters_multiple(self):
+        table1 = self.tables.mytable
+
+        stmt = update(table1)
+
+        stmt = stmt.ordered_values(("name", "somename"))
+
+        assert_raises_message(
+            exc.ArgumentError,
+            "This statement already has ordered values present",
+            stmt.ordered_values,
+            ("myid", 10),
+        )
+
+    def test_update_ordered_then_nonordered(self):
+        table1 = self.tables.mytable
+
+        stmt = table1.update().ordered_values(("myid", 1), ("name", "d1"))
+
+        assert_raises_message(
+            exc.ArgumentError,
+            "This statement already has ordered values present",
+            stmt.values,
+            {"myid": 2, "name": "d2"},
+        )
+
+    def test_update_no_multiple_parameters_allowed(self):
+        table1 = self.tables.mytable
+
+        stmt = table1.update().values(
+            [{"myid": 1, "name": "n1"}, {"myid": 2, "name": "n2"}]
+        )
+
+        assert_raises_message(
+            exc.InvalidRequestError,
+            "UPDATE construct does not support multiple parameter sets.",
+            stmt.compile,
+        )
+
     def test_update_ordered_parameters_fire_onupdate(self):
         table = self.tables.update_w_default