]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
expand and further generalize bound parameter translate
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 13 Feb 2021 22:13:51 +0000 (17:13 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 14 Feb 2021 15:16:38 +0000 (10:16 -0500)
Continued with the improvement made as part of :ticket:`5653` to further
support bound parameter names, including those generated against column
names, for names that include colons, parenthesis, and question marks, as
well as improved test support, so that bound parameter names even if they
are auto-derived from column names should have no problem including for
parenthesis in psycopg2's "pyformat" style.

As part of this change, the format used by the asyncpg DBAPI adapter (which
is local to SQLAlchemy's asyncpg diaelct) has been changed from using
"qmark" paramstyle to "format", as there is a standard and internally
supported SQL string escaping style for names that use percent signs with
"format" style (i.e. to double percent signs), as opposed to names that use
question marks with "qmark" style (where an escaping system is not defined
by pep-249 or Python).

Fixes: #5941
Change-Id: Id86f5af81903d7063a8e3505e60df56490f85358

doc/build/changelog/migration_14.rst
doc/build/changelog/unreleased_14/5941.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/postgresql/asyncpg.py
lib/sqlalchemy/dialects/postgresql/psycopg2.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/testing/suite/test_dialect.py
test/profiles.txt

index 41c91050c0545ebc8f691e139e8aa5379bd4ad44..7bd9f9d315759da837b88df8141b5766aecb1f55 100644 (file)
@@ -2577,6 +2577,27 @@ The psycopg2 dialect relies upon many features of psycopg2 released
 in the past few years.  To simplify the dialect, version 2.7, released
 in March, 2017 is now the minimum version required.
 
+.. _change_5941:
+
+psycopg2 dialect no longer has limitations regarding bound parameter names
+--------------------------------------------------------------------------
+
+SQLAlchemy 1.3 was not able to accommodate bound parameter names that included
+percent signs or parenthesis under the psycopg2 dialect, which meant that
+column names which included these characters were also problematic as
+INSERT and other DML statements would generate parameter names that matched
+that of the column, unless the :paramref:`_schema.Column.key` parameter
+were used to provide an alternate name that would be used to generate
+the parameter, or otherwise the parameter style of the dialect had to be
+changed.  As of SQLAlchemy 1.4.0beta3 all naming limitations have been removed
+and parameters are fully escaped in all scenarios.
+
+
+:ticket:`5941`
+
+:ticket:`5653`
+
+
 .. _change_5401:
 
 psycopg2 dialect features "execute_values" with RETURNING for INSERT statements by default
diff --git a/doc/build/changelog/unreleased_14/5941.rst b/doc/build/changelog/unreleased_14/5941.rst
new file mode 100644 (file)
index 0000000..5f5f116
--- /dev/null
@@ -0,0 +1,22 @@
+.. change::
+    :tags: bug, engine, postgresql
+    :tickets: 5941
+
+    Continued with the improvement made as part of :ticket:`5653` to further
+    support bound parameter names, including those generated against column
+    names, for names that include colons, parenthesis, and question marks, as
+    well as improved test support, so that bound parameter names even if they
+    are auto-derived from column names should have no problem including for
+    parenthesis in psycopg2's "pyformat" style.
+
+    As part of this change, the format used by the asyncpg DBAPI adapter (which
+    is local to SQLAlchemy's asyncpg diaelct) has been changed from using
+    "qmark" paramstyle to "format", as there is a standard and internally
+    supported SQL string escaping style for names that use percent signs with
+    "format" style (i.e. to double percent signs), as opposed to names that use
+    question marks with "qmark" style (where an escaping system is not defined
+    by pep-249 or Python).
+
+    .. seealso::
+
+      :ref:`change_5941`
\ No newline at end of file
index 424ed0d5070d07f782859b6dd46a58a49aa9f6d7..7ef5e441cbf0eeb69985de25dff4ec1fc255de7b 100644 (file)
@@ -100,7 +100,6 @@ To disable the prepared statement cache, use a value of zero::
 
 import collections
 import decimal
-import itertools
 import json as _py_json
 import re
 import time
@@ -357,12 +356,12 @@ class AsyncAdapt_asyncpg_cursor:
     def _handle_exception(self, error):
         self._adapt_connection._handle_exception(error)
 
-    def _parameters(self):
+    def _parameter_placeholders(self, params):
         if not self._inputsizes:
-            return ("$%d" % idx for idx in itertools.count(1))
+            return tuple("$%d" % idx for idx, _ in enumerate(params, 1))
         else:
 
-            return (
+            return tuple(
                 "$%d::%s" % (idx, typ) if typ else "$%d" % idx
                 for idx, typ in enumerate(
                     (_pg_types.get(typ) for typ in self._inputsizes), 1
@@ -374,11 +373,10 @@ class AsyncAdapt_asyncpg_cursor:
         if not self._adapt_connection._started:
             await self._adapt_connection._start_transaction()
 
-        params = self._parameters()
-
-        # TODO: would be nice to support the dollar numeric thing
-        # directly, this is much easier for now
-        operation = re.sub(r"\?", lambda m: next(params), operation)
+        if parameters is not None:
+            operation = operation % self._parameter_placeholders(parameters)
+        else:
+            parameters = ()
 
         try:
             prepared_stmt, attributes = await self._adapt_connection._prepare(
@@ -409,7 +407,7 @@ class AsyncAdapt_asyncpg_cursor:
         except Exception as error:
             self._handle_exception(error)
 
-    def execute(self, operation, parameters=()):
+    def execute(self, operation, parameters=None):
         try:
             self._adapt_connection.await_(
                 self._prepare_and_execute(operation, parameters)
@@ -429,8 +427,10 @@ class AsyncAdapt_asyncpg_cursor:
         if not adapt_connection._started:
             adapt_connection.await_(adapt_connection._start_transaction())
 
-        params = self._parameters()
-        operation = re.sub(r"\?", lambda m: next(params), operation)
+        operation = operation % self._parameter_placeholders(
+            seq_of_parameters[0]
+        )
+
         try:
             return adapt_connection.await_(
                 self._connection.executemany(operation, seq_of_parameters)
@@ -706,7 +706,7 @@ class AsyncAdaptFallback_asyncpg_connection(AsyncAdapt_asyncpg_connection):
 class AsyncAdapt_asyncpg_dbapi:
     def __init__(self, asyncpg):
         self.asyncpg = asyncpg
-        self.paramstyle = "qmark"
+        self.paramstyle = "format"
 
     def connect(self, *arg, **kw):
         async_fallback = kw.pop("async_fallback", False)
@@ -837,7 +837,7 @@ class PGDialect_asyncpg(PGDialect):
 
     supports_unicode_binds = True
 
-    default_paramstyle = "qmark"
+    default_paramstyle = "format"
     supports_sane_multi_rowcount = False
     execution_ctx_cls = PGExecutionContext_asyncpg
     statement_compiler = PGCompiler_asyncpg
index b1d27c6faafeaab732210307e9a402c7229ad6fd..a52eacd8bc4829226f1938e5c646e0e7be9c4fd0 100644 (file)
@@ -323,57 +323,6 @@ on the way in and coerce from bytes on the way back, using the value of the
 SQLAlchemy's own unicode encode/decode functionality is steadily becoming
 obsolete as most DBAPIs now support unicode fully.
 
-Bound Parameter Styles
-----------------------
-
-The default parameter style for the psycopg2 dialect is "pyformat", where
-SQL is rendered using ``%(paramname)s`` style.   This format has the limitation
-that it does not accommodate the unusual case of parameter names that
-actually contain percent or parenthesis symbols; as SQLAlchemy in many cases
-generates bound parameter names based on the name of a column, the presence
-of these characters in a column name can lead to problems.
-
-There are two solutions to the issue of a :class:`_schema.Column`
-that contains
-one of these characters in its name.  One is to specify the
-:paramref:`.schema.Column.key` for columns that have such names::
-
-    measurement = Table('measurement', metadata,
-        Column('Size (meters)', Integer, key='size_meters')
-    )
-
-Above, an INSERT statement such as ``measurement.insert()`` will use
-``size_meters`` as the parameter name, and a SQL expression such as
-``measurement.c.size_meters > 10`` will derive the bound parameter name
-from the ``size_meters`` key as well.
-
-.. versionchanged:: 1.0.0 - SQL expressions will use
-   :attr:`_schema.Column.key`
-   as the source of naming when anonymous bound parameters are created
-   in SQL expressions; previously, this behavior only applied to
-   :meth:`_schema.Table.insert` and :meth:`_schema.Table.update`
-   parameter names.
-
-The other solution is to use a positional format; psycopg2 allows use of the
-"format" paramstyle, which can be passed to
-:paramref:`_sa.create_engine.paramstyle`::
-
-    engine = create_engine(
-        'postgresql://scott:tiger@localhost:5432/test', paramstyle='format')
-
-With the above engine, instead of a statement like::
-
-    INSERT INTO measurement ("Size (meters)") VALUES (%(Size (meters))s)
-    {'Size (meters)': 1}
-
-we instead see::
-
-    INSERT INTO measurement ("Size (meters)") VALUES (%s)
-    (1, )
-
-Where above, the dictionary style is converted into a tuple with positional
-style.
-
 
 Transactions
 ------------
@@ -648,14 +597,7 @@ class PGExecutionContext_psycopg2(PGExecutionContext):
 
 
 class PGCompiler_psycopg2(PGCompiler):
-    def bindparam_string(self, name, **kw):
-        if "%" in name and not kw.get("post_compile", False):
-            # psycopg2 will not allow a percent sign in a
-            # pyformat parameter name even if it is doubled
-            kw["escaped_from"] = name
-            name = name.replace("%", "P")
-
-        return PGCompiler.bindparam_string(self, name, **kw)
+    pass
 
 
 class PGIdentifierPreparer_psycopg2(PGIdentifierPreparer):
index f22e8614bc6d4dd5259b9213f04b75a909a634a4..763b4cabbd7cbfd404ef40870300d56c590652bb 100644 (file)
@@ -166,6 +166,11 @@ BIND_TEMPLATES = {
     "named": ":%(name)s",
 }
 
+BIND_TRANSLATE = {
+    "pyformat": re.compile(r"[%\(\)]"),
+    "named": re.compile(r"[\:]"),
+}
+_BIND_TRANSLATE_CHARS = {"%": "P", "(": "A", ")": "Z", ":": "C"}
 
 OPERATORS = {
     # binary
@@ -765,6 +770,7 @@ class SQLCompiler(Compiled):
             self.positiontup = []
             self._numeric_binds = dialect.paramstyle == "numeric"
         self.bindtemplate = BIND_TEMPLATES[dialect.paramstyle]
+        self._bind_translate = BIND_TRANSLATE.get(dialect.paramstyle, None)
 
         self.ctes = None
 
@@ -2417,11 +2423,23 @@ class SQLCompiler(Compiled):
         escaped_from=None,
         **kw
     ):
+
         if self.positional:
             if positional_names is not None:
                 positional_names.append(name)
             else:
                 self.positiontup.append(name)
+        elif not post_compile and not escaped_from:
+            tr_reg = self._bind_translate
+            if tr_reg.search(name):
+                # i'd rather use translate() here but I can't get it to work
+                # in all cases under Python 2, not worth it right now
+                new_name = tr_reg.sub(
+                    lambda m: _BIND_TRANSLATE_CHARS[m.group(0)],
+                    name,
+                )
+                escaped_from = name
+                name = new_name
 
         if escaped_from:
             if not self.escaped_bind_names:
index 8709aca9d35ca5eea5dc045bdcc39592ee39dadd..a236b10769b6313633883cdca99cf9185dc3c962 100644 (file)
@@ -1,5 +1,6 @@
 #! coding: utf-8
 
+from . import testing
 from .. import assert_raises
 from .. import config
 from .. import engines
@@ -11,6 +12,7 @@ from ..config import requirements
 from ..provision import set_default_schema_on_connection
 from ..schema import Column
 from ..schema import Table
+from ... import bindparam
 from ... import event
 from ... import exc
 from ... import Integer
@@ -308,3 +310,50 @@ class FutureWeCanSetDefaultSchemaWEventsTest(
     fixtures.FutureEngineMixin, WeCanSetDefaultSchemaWEventsTest
 ):
     pass
+
+
+class DifficultParametersTest(fixtures.TestBase):
+    __backend__ = True
+
+    @testing.combinations(
+        ("boring",),
+        ("per cent",),
+        ("per % cent",),
+        ("%percent",),
+        ("par(ens)",),
+        ("percent%(ens)yah",),
+        ("col:ons",),
+        ("more :: %colons%",),
+        ("/slashes/",),
+        ("more/slashes",),
+        ("q?marks",),
+        ("1param",),
+        ("1col:on",),
+        argnames="name",
+    )
+    def test_round_trip(self, name, connection, metadata):
+        t = Table(
+            "t",
+            metadata,
+            Column("id", Integer, primary_key=True),
+            Column(name, String(50), nullable=False),
+        )
+
+        # table is created
+        t.create(connection)
+
+        # automatic param generated by insert
+        connection.execute(t.insert().values({"id": 1, name: "some name"}))
+
+        # automatic param generated by criteria, plus selecting the column
+        stmt = select(t.c[name]).where(t.c[name] == "some name")
+
+        eq_(connection.scalar(stmt), "some name")
+
+        # use the name in a param explicitly
+        stmt = select(t.c[name]).where(t.c[name] == bindparam(name))
+
+        row = connection.execute(stmt, {name: "some name"}).first()
+
+        # name works as the key from cursor.description
+        eq_(row._mapping[name], "some name")
index 2a43da63aad23653034b90e90fa35a5ff28c2fa4..a5f06b5c9f93b996680aa433bb9b322882458c15 100644 (file)
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert
 
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_mysqldb_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_mysqldb_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_pymysql_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_pymysql_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mssql_pyodbc_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mssql_pyodbc_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_mysqldb_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_mysqldb_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_pymysql_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_pymysql_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_cextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_nocextensions 62
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 64
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_pymysql_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_pymysql_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_pymysql_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_pymysql_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_oracle_cx_oracle_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_oracle_cx_oracle_dbapiunicode_nocextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_nocextensions 69
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_mysqldb_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_mysqldb_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_pymysql_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mariadb_pymysql_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mssql_pyodbc_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mssql_pyodbc_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_mysqldb_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_mysqldb_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_pymysql_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_mysql_pymysql_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_cextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_nocextensions 66
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 68
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_nocextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_pymysql_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mariadb_pymysql_dbapiunicode_nocextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_nocextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_pymysql_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_mysql_pymysql_dbapiunicode_nocextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_oracle_cx_oracle_dbapiunicode_cextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_oracle_cx_oracle_dbapiunicode_nocextensions 71
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_nocextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 73
+test.aaa_profiling.test_compiler.CompileTest.test_insert x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_nocextensions 73
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_select
 
@@ -112,8 +112,8 @@ test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.
 test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_mysql_mysqldb_dbapiunicode_nocextensions 69
 test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_mysql_pymysql_dbapiunicode_cextensions 69
 test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_mysql_pymysql_dbapiunicode_nocextensions 69
-test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_cextensions 67
-test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_nocextensions 67
+test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_cextensions 79
+test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_oracle_cx_oracle_dbapiunicode_nocextensions 79
 test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_cextensions 69
 test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 69
 test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 69
@@ -137,14 +137,14 @@ test.aaa_profiling.test_compiler.CompileTest.test_update x86_64_linux_cpython_3.
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause
 
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 159
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_cextensions 165
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_nocextensions 165
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 165
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_nocextensions 165
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_cextensions 169
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 169
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 169
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 169
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_cextensions 175
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_nocextensions 175
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 175
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_nocextensions 175
 
 # TEST: test.aaa_profiling.test_misc.CacheKeyTest.test_statement_key_is_cached