diff options
-rw-r--r-- | doc/build/core/tutorial.rst | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/cextension/utils.c | 27 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/base.py | 20 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 187 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/util.py | 73 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/assertsql.py | 6 | ||||
-rw-r--r-- | test/aaa_profiling/test_resultset.py | 4 | ||||
-rw-r--r-- | test/dialect/postgresql/test_query.py | 15 | ||||
-rw-r--r-- | test/engine/test_deprecations.py | 90 | ||||
-rw-r--r-- | test/engine/test_execute.py | 121 | ||||
-rw-r--r-- | test/engine/test_processors.py | 34 | ||||
-rw-r--r-- | test/profiles.txt | 29 |
12 files changed, 477 insertions, 137 deletions
diff --git a/doc/build/core/tutorial.rst b/doc/build/core/tutorial.rst index 3f4d37d0e..8d27dd21d 100644 --- a/doc/build/core/tutorial.rst +++ b/doc/build/core/tutorial.rst @@ -323,7 +323,7 @@ and use it in the "normal" way: .. sourcecode:: pycon+sql >>> ins = users.insert() - >>> conn.execute(ins, id=2, name='wendy', fullname='Wendy Williams') + >>> conn.execute(ins, {"id": 2, "name":"wendy", "fullname": "Wendy Williams"}) {opensql}INSERT INTO users (id, name, fullname) VALUES (?, ?, ?) [...] (2, 'wendy', 'Wendy Williams') COMMIT @@ -972,7 +972,7 @@ unchanged. Below, we create a :func:`_expression.text` object and execute it: ... "AND users.name BETWEEN :x AND :y " ... "AND (addresses.email_address LIKE :e1 " ... "OR addresses.email_address LIKE :e2)") - >>> conn.execute(s, x='m', y='z', e1='%@aol.com', e2='%@msn.com').fetchall() + >>> conn.execute(s, {"x":"m", "y":"z", "e1":"%@aol.com", "e2":"%@msn.com"}).fetchall() {opensql}SELECT users.fullname || ', ' || addresses.email_address AS title FROM users, addresses WHERE users.id = addresses.user_id AND users.name BETWEEN ? AND ? AND @@ -1127,7 +1127,7 @@ need to refer to any pre-established :class:`_schema.Table` metadata: ... "OR addresses.email_address LIKE :y)") ... ) ... ).select_from(text('users, addresses')) - >>> conn.execute(s, x='%@aol.com', y='%@msn.com').fetchall() + >>> conn.execute(s, {"x": "%@aol.com", "y": "%@msn.com"}).fetchall() {opensql}SELECT users.fullname || ', ' || addresses.email_address AS title FROM users, addresses WHERE users.id = addresses.user_id AND users.name BETWEEN 'm' AND 'z' @@ -1180,7 +1180,7 @@ be quoted: ... ) ... ).select_from(table('users')).select_from(table('addresses')) - >>> conn.execute(s, x='%@aol.com', y='%@msn.com').fetchall() + >>> conn.execute(s, {"x":"%@aol.com", "y":"%@msn.com"}).fetchall() {opensql}SELECT users.fullname || ? || addresses.email_address AS anon_1 FROM users, addresses WHERE users.id = addresses.user_id diff --git a/lib/sqlalchemy/cextension/utils.c b/lib/sqlalchemy/cextension/utils.c index ab8b39335..c612094dc 100644 --- a/lib/sqlalchemy/cextension/utils.c +++ b/lib/sqlalchemy/cextension/utils.c @@ -26,12 +26,13 @@ distill_params(PyObject *self, PyObject *args) // TODO: pass the Connection in so that there can be a standard // method for warning on parameter format - PyObject *multiparams, *params; + PyObject *connection, *multiparams, *params; PyObject *enclosing_list, *double_enclosing_list; PyObject *zero_element, *zero_element_item; + PyObject *tmp; Py_ssize_t multiparam_size, zero_element_length; - if (!PyArg_UnpackTuple(args, "_distill_params", 2, 2, &multiparams, ¶ms)) { + if (!PyArg_UnpackTuple(args, "_distill_params", 3, 3, &connection, &multiparams, ¶ms)) { return NULL; } @@ -47,8 +48,12 @@ distill_params(PyObject *self, PyObject *args) if (multiparam_size == 0) { if (params != Py_None && PyMapping_Size(params) != 0) { - // TODO: this is keyword parameters, emit parameter format - // deprecation warning + + tmp = PyObject_CallMethod(connection, "_warn_for_legacy_exec_format", ""); + if (tmp == NULL) { + return NULL; + } + enclosing_list = PyList_New(1); if (enclosing_list == NULL) { return NULL; @@ -102,6 +107,7 @@ distill_params(PyObject *self, PyObject *args) * execute(stmt, ("value", "value")) */ Py_XDECREF(zero_element_item); + enclosing_list = PyList_New(1); if (enclosing_list == NULL) { return NULL; @@ -131,6 +137,11 @@ distill_params(PyObject *self, PyObject *args) } return enclosing_list; } else { + tmp = PyObject_CallMethod(connection, "_warn_for_legacy_exec_format", ""); + if (tmp == NULL) { + return NULL; + } + enclosing_list = PyList_New(1); if (enclosing_list == NULL) { return NULL; @@ -157,8 +168,12 @@ distill_params(PyObject *self, PyObject *args) } } else { - // TODO: this is multiple positional params, emit parameter format - // deprecation warning + + tmp = PyObject_CallMethod(connection, "_warn_for_legacy_exec_format", ""); + if (tmp == NULL) { + return NULL; + } + zero_element = PyTuple_GetItem(multiparams, 0); if (PyObject_HasAttrString(zero_element, "__iter__") && !PyObject_HasAttrString(zero_element, "strip") diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 2db079799..c56cccd8d 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -2903,7 +2903,11 @@ class PGDialect(default.DefaultDialect): "JOIN pg_namespace n ON n.oid = c.relnamespace " "WHERE n.nspname = :schema AND c.relkind in ('r', 'p')" ).columns(relname=sqltypes.Unicode), - schema=schema if schema is not None else self.default_schema_name, + dict( + schema=schema + if schema is not None + else self.default_schema_name + ), ) return [name for name, in result] @@ -3018,7 +3022,7 @@ class PGDialect(default.DefaultDialect): .bindparams(sql.bindparam("table_oid", type_=sqltypes.Integer)) .columns(attname=sqltypes.Unicode, default=sqltypes.Unicode) ) - c = connection.execute(s, table_oid=table_oid) + c = connection.execute(s, dict(table_oid=table_oid)) rows = c.fetchall() # dictionary with (name, ) if default search path or (schema, name) @@ -3260,7 +3264,7 @@ class PGDialect(default.DefaultDialect): ORDER BY k.ord """ t = sql.text(PK_SQL).columns(attname=sqltypes.Unicode) - c = connection.execute(t, table_oid=table_oid) + c = connection.execute(t, dict(table_oid=table_oid)) cols = [r[0] for r in c.fetchall()] PK_CONS_SQL = """ @@ -3270,7 +3274,7 @@ class PGDialect(default.DefaultDialect): ORDER BY 1 """ t = sql.text(PK_CONS_SQL).columns(conname=sqltypes.Unicode) - c = connection.execute(t, table_oid=table_oid) + c = connection.execute(t, dict(table_oid=table_oid)) name = c.scalar() return {"constrained_columns": cols, "name": name} @@ -3318,7 +3322,7 @@ class PGDialect(default.DefaultDialect): t = sql.text(FK_SQL).columns( conname=sqltypes.Unicode, condef=sqltypes.Unicode ) - c = connection.execute(t, table=table_oid) + c = connection.execute(t, dict(table=table_oid)) fkeys = [] for conname, condef, conschema in c.fetchall(): m = re.search(FK_REGEX, condef).groups() @@ -3490,7 +3494,7 @@ class PGDialect(default.DefaultDialect): t = sql.text(IDX_SQL).columns( relname=sqltypes.Unicode, attname=sqltypes.Unicode ) - c = connection.execute(t, table_oid=table_oid) + c = connection.execute(t, dict(table_oid=table_oid)) indexes = defaultdict(lambda: defaultdict(dict)) @@ -3632,7 +3636,7 @@ class PGDialect(default.DefaultDialect): """ t = sql.text(UNIQUE_SQL).columns(col_name=sqltypes.Unicode) - c = connection.execute(t, table_oid=table_oid) + c = connection.execute(t, dict(table_oid=table_oid)) uniques = defaultdict(lambda: defaultdict(dict)) for row in c.fetchall(): @@ -3683,7 +3687,7 @@ class PGDialect(default.DefaultDialect): cons.contype = 'c' """ - c = connection.execute(sql.text(CHECK_SQL), table_oid=table_oid) + c = connection.execute(sql.text(CHECK_SQL), dict(table_oid=table_oid)) ret = [] for name, src in c: diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 34bf720b7..0eaa1fae1 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -935,6 +935,17 @@ class Connection(Connectable): if not self.in_transaction(): self._rollback_impl() + def _warn_for_legacy_exec_format(self): + util.warn_deprecated_20( + "The connection.execute() method in " + "SQLAlchemy 2.0 will accept parameters as a single " + "dictionary or a " + "single sequence of dictionaries only. " + "Parameters passed as keyword arguments, tuples or positionally " + "oriened dictionaries and/or tuples " + "will no longer be accepted." + ) + def close(self): """Close this :class:`_engine.Connection`. @@ -1073,14 +1084,13 @@ class Connection(Connectable): "or the Connection.exec_driver_sql() method to invoke a " "driver-level SQL string." ) - distilled_parameters = _distill_params(multiparams, params) return self._exec_driver_sql( object_, multiparams, params, - distilled_parameters, _EMPTY_EXECUTION_OPTS, + future=False, ) try: @@ -1113,11 +1123,16 @@ class Connection(Connectable): execution_options ) + distilled_parameters = _distill_params(self, multiparams, params) + if self._has_events or self.engine._has_events: - for fn in self.dispatch.before_execute: - default, multiparams, params = fn( - self, default, multiparams, params, execution_options - ) + ( + distilled_params, + event_multiparams, + event_params, + ) = self._invoke_before_exec_event( + default, distilled_parameters, execution_options + ) try: conn = self._dbapi_connection @@ -1139,7 +1154,12 @@ class Connection(Connectable): if self._has_events or self.engine._has_events: self.dispatch.after_execute( - self, default, multiparams, params, execution_options, ret + self, + default, + event_multiparams, + event_params, + execution_options, + ret, ) return ret @@ -1151,11 +1171,16 @@ class Connection(Connectable): self._execution_options, execution_options ) + distilled_parameters = _distill_params(self, multiparams, params) + if self._has_events or self.engine._has_events: - for fn in self.dispatch.before_execute: - ddl, multiparams, params = fn( - self, ddl, multiparams, params, execution_options - ) + ( + distilled_params, + event_multiparams, + event_params, + ) = self._invoke_before_exec_event( + ddl, distilled_parameters, execution_options + ) exec_opts = self._execution_options.merge_with(execution_options) schema_translate_map = exec_opts.get("schema_translate_map", None) @@ -1175,10 +1200,43 @@ class Connection(Connectable): ) if self._has_events or self.engine._has_events: self.dispatch.after_execute( - self, ddl, multiparams, params, execution_options, ret + self, + ddl, + event_multiparams, + event_params, + execution_options, + ret, ) return ret + def _invoke_before_exec_event( + self, elem, distilled_params, execution_options + ): + + if len(distilled_params) == 1: + event_multiparams, event_params = [], distilled_params[0] + else: + event_multiparams, event_params = distilled_params, {} + + for fn in self.dispatch.before_execute: + elem, event_multiparams, event_params = fn( + self, elem, event_multiparams, event_params, execution_options, + ) + + if event_multiparams: + distilled_params = list(event_multiparams) + if event_params: + raise exc.InvalidRequestError( + "Event handler can't return non-empty multiparams " + "and params at the same time" + ) + elif event_params: + distilled_params = [event_params] + else: + distilled_params = [] + + return distilled_params, event_multiparams, event_params + def _execute_clauseelement( self, elem, multiparams, params, execution_options ): @@ -1188,14 +1246,18 @@ class Connection(Connectable): self._execution_options, execution_options ) + distilled_params = _distill_params(self, multiparams, params) + has_events = self._has_events or self.engine._has_events if has_events: - for fn in self.dispatch.before_execute: - elem, multiparams, params = fn( - self, elem, multiparams, params, execution_options - ) + ( + distilled_params, + event_multiparams, + event_params, + ) = self._invoke_before_exec_event( + elem, distilled_params, execution_options + ) - distilled_params = _distill_params(multiparams, params) if distilled_params: # ensure we don't retain a link to the view object for keys() # which links to the values, which we don't want to cache @@ -1237,7 +1299,12 @@ class Connection(Connectable): ) if has_events: self.dispatch.after_execute( - self, elem, multiparams, params, execution_options, ret + self, + elem, + event_multiparams, + event_params, + execution_options, + ret, ) return ret @@ -1257,49 +1324,58 @@ class Connection(Connectable): execution_options = compiled.execution_options.merge_with( self._execution_options, execution_options ) + distilled_parameters = _distill_params(self, multiparams, params) if self._has_events or self.engine._has_events: - for fn in self.dispatch.before_execute: - compiled, multiparams, params = fn( - self, compiled, multiparams, params, execution_options - ) + ( + distilled_params, + event_multiparams, + event_params, + ) = self._invoke_before_exec_event( + compiled, distilled_parameters, execution_options + ) dialect = self.dialect - parameters = _distill_params(multiparams, params) ret = self._execute_context( dialect, dialect.execution_ctx_cls._init_compiled, compiled, - parameters, + distilled_parameters, execution_options, compiled, - parameters, + distilled_parameters, None, None, ) if self._has_events or self.engine._has_events: self.dispatch.after_execute( - self, compiled, multiparams, params, execution_options, ret + self, + compiled, + event_multiparams, + event_params, + execution_options, + ret, ) return ret def _exec_driver_sql( - self, - statement, - multiparams, - params, - distilled_parameters, - execution_options, + self, statement, multiparams, params, execution_options, future ): execution_options = self._execution_options.merge_with( execution_options ) - if self._has_events or self.engine._has_events: - for fn in self.dispatch.before_execute: - statement, multiparams, params = fn( - self, statement, multiparams, params, execution_options + distilled_parameters = _distill_params(self, multiparams, params) + + if not future: + if self._has_events or self.engine._has_events: + ( + distilled_params, + event_multiparams, + event_params, + ) = self._invoke_before_exec_event( + statement, distilled_parameters, execution_options ) dialect = self.dialect @@ -1312,10 +1388,17 @@ class Connection(Connectable): statement, distilled_parameters, ) - if self._has_events or self.engine._has_events: - self.dispatch.after_execute( - self, statement, multiparams, params, execution_options, ret - ) + + if not future: + if self._has_events or self.engine._has_events: + self.dispatch.after_execute( + self, + statement, + event_multiparams, + event_params, + execution_options, + ret, + ) return ret def _execute_20( @@ -1324,9 +1407,7 @@ class Connection(Connectable): parameters=None, execution_options=_EMPTY_EXECUTION_OPTS, ): - multiparams, params, distilled_parameters = _distill_params_20( - parameters - ) + args_10style, kwargs_10style = _distill_params_20(parameters) try: meth = statement._execute_on_connection except AttributeError as err: @@ -1334,7 +1415,7 @@ class Connection(Connectable): exc.ObjectNotExecutableError(statement), replace_context=err ) else: - return meth(self, multiparams, params, execution_options) + return meth(self, args_10style, kwargs_10style, execution_options) def exec_driver_sql( self, statement, parameters=None, execution_options=None @@ -1373,22 +1454,28 @@ class Connection(Connectable): (1, 'v1') ) + .. note:: The :meth:`_engine.Connection.exec_driver_sql` method does + not participate in the + :meth:`_events.ConnectionEvents.before_execute` and + :meth:`_events.ConnectionEvents.after_execute` events. To + intercept calls to :meth:`_engine.Connection.exec_driver_sql`, use + :meth:`_events.ConnectionEvents.before_cursor_execute` and + :meth:`_events.ConnectionEvents.after_cursor_execute`. + .. seealso:: :pep:`249` """ - multiparams, params, distilled_parameters = _distill_params_20( - parameters - ) + args_10style, kwargs_10style = _distill_params_20(parameters) return self._exec_driver_sql( statement, - multiparams, - params, - distilled_parameters, + args_10style, + kwargs_10style, execution_options, + future=True, ) def _execute_context( diff --git a/lib/sqlalchemy/engine/util.py b/lib/sqlalchemy/engine/util.py index fc0260ae2..c1f6bad77 100644 --- a/lib/sqlalchemy/engine/util.py +++ b/lib/sqlalchemy/engine/util.py @@ -30,10 +30,14 @@ def connection_memoize(key): return decorated +_no_tuple = () +_no_kw = util.immutabledict() + + def py_fallback(): # TODO: pass the Connection in so that there can be a standard # method for warning on parameter format - def _distill_params(multiparams, params): # noqa + def _distill_params(connection, multiparams, params): # noqa r"""Given arguments from the calling form \*multiparams, \**params, return a list of bind parameter structures, usually a list of dictionaries. @@ -43,9 +47,12 @@ def py_fallback(): """ + # C version will fail if this assertion is not true. + # assert isinstance(multiparams, tuple) + if not multiparams: if params: - # TODO: parameter format deprecation warning + connection._warn_for_legacy_exec_format() return [params] else: return [] @@ -61,16 +68,22 @@ def py_fallback(): # execute(stmt, [(), (), (), ...]) return zero else: + # this is used by exec_driver_sql only, so a deprecation + # warning would already be coming from passing a plain + # textual statement with positional parameters to + # execute(). # execute(stmt, ("value", "value")) + return [zero] elif hasattr(zero, "keys"): # execute(stmt, {"key":"value"}) return [zero] else: + connection._warn_for_legacy_exec_format() # execute(stmt, "value") return [[zero]] else: - # TODO: parameter format deprecation warning + connection._warn_for_legacy_exec_format() if hasattr(multiparams[0], "__iter__") and not hasattr( multiparams[0], "strip" ): @@ -81,14 +94,55 @@ def py_fallback(): return locals() -_no_tuple = () -_no_kw = util.immutabledict() +def _distill_cursor_params(connection, multiparams, params): + """_distill_params without any warnings. more appropriate for + "cursor" params that can include tuple arguments, lists of tuples, + etc. + + """ + + if not multiparams: + if params: + return [params] + else: + return [] + elif len(multiparams) == 1: + zero = multiparams[0] + if isinstance(zero, (list, tuple)): + if ( + not zero + or hasattr(zero[0], "__iter__") + and not hasattr(zero[0], "strip") + ): + # execute(stmt, [{}, {}, {}, ...]) + # execute(stmt, [(), (), (), ...]) + return zero + else: + # this is used by exec_driver_sql only, so a deprecation + # warning would already be coming from passing a plain + # textual statement with positional parameters to + # execute(). + # execute(stmt, ("value", "value")) + + return [zero] + elif hasattr(zero, "keys"): + # execute(stmt, {"key":"value"}) + return [zero] + else: + # execute(stmt, "value") + return [[zero]] + else: + if hasattr(multiparams[0], "__iter__") and not hasattr( + multiparams[0], "strip" + ): + return multiparams + else: + return [multiparams] def _distill_params_20(params): - # TODO: this has to be in C if params is None: - return _no_tuple, _no_kw, [] + return _no_tuple, _no_kw elif isinstance(params, list): # collections_abc.MutableSequence): # avoid abc.__instancecheck__ if params and not isinstance( @@ -98,15 +152,14 @@ def _distill_params_20(params): "List argument must consist only of tuples or dictionaries" ) - # the tuple is needed atm by the C version of _distill_params... - return tuple(params), _no_kw, params + return (params,), _no_kw elif isinstance( params, (tuple, dict, immutabledict), # avoid abc.__instancecheck__ # (collections_abc.Sequence, collections_abc.Mapping), ): - return _no_tuple, params, [params] + return (params,), _no_kw else: raise exc.ArgumentError("mapping or sequence expected for parameters") diff --git a/lib/sqlalchemy/testing/assertsql.py b/lib/sqlalchemy/testing/assertsql.py index 73b062b96..c86e26ccf 100644 --- a/lib/sqlalchemy/testing/assertsql.py +++ b/lib/sqlalchemy/testing/assertsql.py @@ -13,7 +13,7 @@ from .. import event from .. import util from ..engine import url from ..engine.default import DefaultDialect -from ..engine.util import _distill_params +from ..engine.util import _distill_cursor_params from ..schema import _DDLCompiles @@ -348,7 +348,9 @@ class SQLExecuteObserved(object): def __init__(self, context, clauseelement, multiparams, params): self.context = context self.clauseelement = clauseelement - self.parameters = _distill_params(multiparams, params) + self.parameters = _distill_cursor_params( + context.connection, tuple(multiparams), params + ) self.statements = [] def __repr__(self): diff --git a/test/aaa_profiling/test_resultset.py b/test/aaa_profiling/test_resultset.py index 119a5ee6a..aea160c9e 100644 --- a/test/aaa_profiling/test_resultset.py +++ b/test/aaa_profiling/test_resultset.py @@ -98,7 +98,7 @@ class ResultSetTest(fixtures.TestBase, AssertsExecutionResults): ) as conn: [tuple(row) for row in conn.execute(t2.select()).fetchall()] - @profiling.function_call_count(variance=0.10) + @profiling.function_call_count(variance=0.15) def test_raw_string(self): stmt = "SELECT %s FROM table1" % ( ", ".join("field%d" % fnum for fnum in range(NUM_FIELDS)) @@ -106,7 +106,7 @@ class ResultSetTest(fixtures.TestBase, AssertsExecutionResults): with testing.db.connect() as conn: [tuple(row) for row in conn.exec_driver_sql(stmt).fetchall()] - @profiling.function_call_count(variance=0.10) + @profiling.function_call_count(variance=0.15) def test_raw_unicode(self): stmt = "SELECT %s FROM table2" % ( ", ".join("field%d" % fnum for fnum in range(NUM_FIELDS)) diff --git a/test/dialect/postgresql/test_query.py b/test/dialect/postgresql/test_query.py index 5ab65f9e3..eb96eaabb 100644 --- a/test/dialect/postgresql/test_query.py +++ b/test/dialect/postgresql/test_query.py @@ -609,8 +609,7 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults): (exc.IntegrityError, exc.ProgrammingError), conn.execute, table.insert(), - {"data": "d2"}, - {"data": "d3"}, + [{"data": "d2"}, {"data": "d3"}], ) with expect_warnings( ".*has no Python-side or server-side default.*" @@ -628,14 +627,12 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults): (exc.IntegrityError, exc.ProgrammingError), conn.execute, table.insert(), - {"data": "d2"}, - {"data": "d3"}, + [{"data": "d2"}, {"data": "d3"}], ) conn.execute( table.insert(), - {"id": 31, "data": "d2"}, - {"id": 32, "data": "d3"}, + [{"id": 31, "data": "d2"}, {"id": 32, "data": "d3"}], ) conn.execute(table.insert(inline=True), {"id": 33, "data": "d4"}) eq_( @@ -668,13 +665,11 @@ class InsertTest(fixtures.TestBase, AssertsExecutionResults): (exc.IntegrityError, exc.ProgrammingError), conn.execute, table.insert(), - {"data": "d2"}, - {"data": "d3"}, + [{"data": "d2"}, {"data": "d3"}], ) conn.execute( table.insert(), - {"id": 31, "data": "d2"}, - {"id": 32, "data": "d3"}, + [{"id": 31, "data": "d2"}, {"id": 32, "data": "d3"}], ) conn.execute(table.insert(inline=True), {"id": 33, "data": "d4"}) eq_( diff --git a/test/engine/test_deprecations.py b/test/engine/test_deprecations.py index f09f0f1e1..62bac312b 100644 --- a/test/engine/test_deprecations.py +++ b/test/engine/test_deprecations.py @@ -28,6 +28,7 @@ from sqlalchemy.testing import is_ from sqlalchemy.testing import is_false from sqlalchemy.testing import is_instance_of from sqlalchemy.testing import is_true +from sqlalchemy.testing import mock from sqlalchemy.testing.engines import testing_engine from sqlalchemy.testing.mock import Mock from sqlalchemy.testing.schema import Column @@ -397,6 +398,25 @@ class DeprecatedEngineFeatureTest(fixtures.TablesTest): with _string_deprecation_expect(): testing.db.execute(select1(testing.db)).scalar() + def test_execute_plain_string_events(self): + + m1 = Mock() + select1_str = select1(testing.db) + with _string_deprecation_expect(): + with testing.db.connect() as conn: + event.listen(conn, "before_execute", m1.before_execute) + event.listen(conn, "after_execute", m1.after_execute) + result = conn.execute(select1_str) + eq_( + m1.mock_calls, + [ + mock.call.before_execute(mock.ANY, select1_str, [], {}, {}), + mock.call.after_execute( + mock.ANY, select1_str, [], {}, {}, result + ), + ], + ) + def test_scalar_plain_string(self): with _string_deprecation_expect(): testing.db.scalar(select1(testing.db)) @@ -772,6 +792,76 @@ class RawExecuteTest(fixtures.TablesTest): ] +class DeprecatedExecParamsTest(fixtures.TablesTest): + __backend__ = True + + @classmethod + def define_tables(cls, metadata): + Table( + "users", + metadata, + Column("user_id", INT, primary_key=True, autoincrement=False), + Column("user_name", VARCHAR(20)), + ) + + Table( + "users_autoinc", + metadata, + Column( + "user_id", INT, primary_key=True, test_needs_autoincrement=True + ), + Column("user_name", VARCHAR(20)), + ) + + def test_kwargs(self, connection): + users = self.tables.users + + with testing.expect_deprecated_20( + r"The connection.execute\(\) method in " + "SQLAlchemy 2.0 will accept parameters as a single " + ): + connection.execute( + users.insert(), user_id=5, user_name="some name" + ) + + eq_(connection.execute(select(users)).all(), [(5, "some name")]) + + def test_positional_dicts(self, connection): + users = self.tables.users + + with testing.expect_deprecated_20( + r"The connection.execute\(\) method in " + "SQLAlchemy 2.0 will accept parameters as a single " + ): + connection.execute( + users.insert(), + {"user_id": 5, "user_name": "some name"}, + {"user_id": 6, "user_name": "some other name"}, + ) + + eq_( + connection.execute(select(users).order_by(users.c.user_id)).all(), + [(5, "some name"), (6, "some other name")], + ) + + def test_single_scalar(self, connection): + + users = self.tables.users_autoinc + + with testing.expect_deprecated_20( + r"The connection.execute\(\) method in " + "SQLAlchemy 2.0 will accept parameters as a single " + ): + # TODO: I'm not even sure what this exec format is or how + # it worked if at all + connection.execute(users.insert(), "some name") + + eq_( + connection.execute(select(users).order_by(users.c.user_id)).all(), + [(1, None)], + ) + + class EngineEventsTest(fixtures.TestBase): __requires__ = ("ad_hoc_engines",) __backend__ = True diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index fd42224eb..5b922a97d 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -34,6 +34,7 @@ from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import config from sqlalchemy.testing import engines from sqlalchemy.testing import eq_ +from sqlalchemy.testing import expect_raises_message from sqlalchemy.testing import expect_warnings from sqlalchemy.testing import fixtures from sqlalchemy.testing import is_ @@ -1420,6 +1421,18 @@ class EngineEventsTest(fixtures.TestBase): eq_(canary.be1.call_count, 2) eq_(canary.be2.call_count, 2) + def test_new_exec_driver_sql_no_events(self): + m1 = Mock() + + def select1(db): + return str(select([1]).compile(dialect=db.dialect)) + + with testing.db.connect() as conn: + event.listen(conn, "before_execute", m1.before_execute) + event.listen(conn, "after_execute", m1.after_execute) + conn.exec_driver_sql(select1(testing.db)) + eq_(m1.mock_calls, []) + def test_add_event_after_connect(self): # new feature as of #2978 canary = Mock() @@ -1504,6 +1517,83 @@ class EngineEventsTest(fixtures.TestBase): [call(conn, ctx.cursor, stmt, ctx.parameters[0], ctx, False)], ) + @testing.combinations( + ( + ([{"x": 5, "y": 10}, {"x": 8, "y": 9}],), + {}, + [{"x": 5, "y": 10}, {"x": 8, "y": 9}], + {}, + ), + ((), {"z": 10}, [], {"z": 10}, testing.requires.legacy_engine), + (({"z": 10},), {}, [], {"z": 10}), + ) + def test_modify_parameters_from_event_one( + self, multiparams, params, expected_multiparams, expected_params + ): + # this is testing both the normalization added to parameters + # as of I97cb4d06adfcc6b889f10d01cc7775925cffb116 as well as + # that the return value from the event is taken as the new set + # of parameters. + def before_execute( + conn, clauseelement, multiparams, params, execution_options + ): + eq_(multiparams, expected_multiparams) + eq_(params, expected_params) + return clauseelement, (), {"q": "15"} + + def after_execute( + conn, clauseelement, multiparams, params, result, execution_options + ): + eq_(multiparams, ()) + eq_(params, {"q": "15"}) + + e1 = testing_engine(config.db_url) + event.listen(e1, "before_execute", before_execute, retval=True) + event.listen(e1, "after_execute", after_execute) + + with e1.connect() as conn: + result = conn.execute( + select(bindparam("q", type_=String)), *multiparams, **params + ) + eq_(result.all(), [("15",)]) + + @testing.provide_metadata + def test_modify_parameters_from_event_two(self, connection): + t = Table("t", self.metadata, Column("q", Integer)) + + t.create(connection) + + def before_execute( + conn, clauseelement, multiparams, params, execution_options + ): + return clauseelement, [{"q": 15}, {"q": 19}], {} + + event.listen(connection, "before_execute", before_execute, retval=True) + connection.execute(t.insert(), {"q": 12}) + event.remove(connection, "before_execute", before_execute) + + eq_( + connection.execute(select(t).order_by(t.c.q)).fetchall(), + [(15,), (19,)], + ) + + def test_modify_parameters_from_event_three(self, connection): + def before_execute( + conn, clauseelement, multiparams, params, execution_options + ): + return clauseelement, [{"q": 15}, {"q": 19}], {"q": 7} + + e1 = testing_engine(config.db_url) + event.listen(e1, "before_execute", before_execute, retval=True) + + with expect_raises_message( + tsa.exc.InvalidRequestError, + "Event handler can't return non-empty multiparams " + "and params at the same time", + ): + with e1.connect() as conn: + conn.execute(select(literal("1"))) + def test_argument_format_execute(self): def before_execute( conn, clauseelement, multiparams, params, execution_options @@ -1591,30 +1681,13 @@ class EngineEventsTest(fixtures.TestBase): if ctx: ctx.close() - if engine._is_future: - compiled = [ - ("CREATE TABLE t1", {}, None), - ( - "INSERT INTO t1 (c1, c2)", - {"c2": "some data", "c1": 5}, - None, - ), - ("INSERT INTO t1 (c1, c2)", {"c1": 6}, None), - ("select * from t1", {}, None), - ("DROP TABLE t1", {}, None), - ] - else: - compiled = [ - ("CREATE TABLE t1", {}, None), - ( - "INSERT INTO t1 (c1, c2)", - {}, - ({"c2": "some data", "c1": 5},), - ), - ("INSERT INTO t1 (c1, c2)", {}, ({"c1": 6},)), - ("select * from t1", {}, None), - ("DROP TABLE t1", {}, None), - ] + compiled = [ + ("CREATE TABLE t1", {}, None), + ("INSERT INTO t1 (c1, c2)", {"c2": "some data", "c1": 5}, (),), + ("INSERT INTO t1 (c1, c2)", {"c1": 6}, ()), + ("select * from t1", {}, None), + ("DROP TABLE t1", {}, None), + ] cursor = [ ("CREATE TABLE t1", {}, ()), diff --git a/test/engine/test_processors.py b/test/engine/test_processors.py index 9bfd8d505..3810de06a 100644 --- a/test/engine/test_processors.py +++ b/test/engine/test_processors.py @@ -1,6 +1,7 @@ from sqlalchemy.testing import assert_raises_message from sqlalchemy.testing import eq_ from sqlalchemy.testing import fixtures +from sqlalchemy.testing import mock class _BooleanProcessorTest(fixtures.TestBase): @@ -107,70 +108,77 @@ class CDateProcessorTest(_DateProcessorTest): class _DistillArgsTest(fixtures.TestBase): def test_distill_none(self): - eq_(self.module._distill_params(None, None), []) + eq_(self.module._distill_params(mock.Mock(), None, None), []) def test_distill_no_multi_no_param(self): - eq_(self.module._distill_params((), {}), []) + eq_(self.module._distill_params(mock.Mock(), (), {}), []) def test_distill_dict_multi_none_param(self): eq_( - self.module._distill_params(None, {"foo": "bar"}), [{"foo": "bar"}] + self.module._distill_params(mock.Mock(), None, {"foo": "bar"}), + [{"foo": "bar"}], ) def test_distill_dict_multi_empty_param(self): - eq_(self.module._distill_params((), {"foo": "bar"}), [{"foo": "bar"}]) + eq_( + self.module._distill_params(mock.Mock(), (), {"foo": "bar"}), + [{"foo": "bar"}], + ) def test_distill_single_dict(self): eq_( - self.module._distill_params(({"foo": "bar"},), {}), + self.module._distill_params(mock.Mock(), ({"foo": "bar"},), {}), [{"foo": "bar"}], ) def test_distill_single_list_strings(self): eq_( - self.module._distill_params((["foo", "bar"],), {}), + self.module._distill_params(mock.Mock(), (["foo", "bar"],), {}), [["foo", "bar"]], ) def test_distill_single_list_tuples(self): eq_( self.module._distill_params( - ([("foo", "bar"), ("bat", "hoho")],), {} + mock.Mock(), ([("foo", "bar"), ("bat", "hoho")],), {} ), [("foo", "bar"), ("bat", "hoho")], ) def test_distill_single_list_tuple(self): eq_( - self.module._distill_params(([("foo", "bar")],), {}), + self.module._distill_params(mock.Mock(), ([("foo", "bar")],), {}), [("foo", "bar")], ) def test_distill_multi_list_tuple(self): eq_( self.module._distill_params( - ([("foo", "bar")], [("bar", "bat")]), {} + mock.Mock(), ([("foo", "bar")], [("bar", "bat")]), {} ), ([("foo", "bar")], [("bar", "bat")]), ) def test_distill_multi_strings(self): - eq_(self.module._distill_params(("foo", "bar"), {}), [("foo", "bar")]) + eq_( + self.module._distill_params(mock.Mock(), ("foo", "bar"), {}), + [("foo", "bar")], + ) def test_distill_single_list_dicts(self): eq_( self.module._distill_params( - ([{"foo": "bar"}, {"foo": "hoho"}],), {} + mock.Mock(), ([{"foo": "bar"}, {"foo": "hoho"}],), {} ), [{"foo": "bar"}, {"foo": "hoho"}], ) def test_distill_single_string(self): - eq_(self.module._distill_params(("arg",), {}), [["arg"]]) + eq_(self.module._distill_params(mock.Mock(), ("arg",), {}), [["arg"]]) def test_distill_multi_string_tuple(self): eq_( - self.module._distill_params((("arg", "arg"),), {}), + self.module._distill_params(mock.Mock(), (("arg", "arg"),), {}), [("arg", "arg")], ) diff --git a/test/profiles.txt b/test/profiles.txt index f2aaeb073..19db8b148 100644 --- a/test/profiles.txt +++ b/test/profiles.txt @@ -1,15 +1,15 @@ # /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 @@ -394,6 +394,7 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 51 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 49 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 54 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 53 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 53 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 53 @@ -421,6 +422,7 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 91 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 89 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 91 +test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 92 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 91 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 91 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 91 @@ -448,6 +450,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 16 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 16 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 16 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 17 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 17 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 17 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 17 @@ -475,6 +478,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_ test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 13507 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 1458 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 13460 +test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 1547 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 1509 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 13512 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_legacy x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 1516 @@ -502,6 +506,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_6 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 15512 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 2465 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15467 +test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 2553 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 2517 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 15520 test.aaa_profiling.test_resultset.ResultSetTest.test_fetch_by_key_mappings x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 2524 @@ -529,6 +534,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 23 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 15 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 15 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-0] x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 23 @@ -556,6 +562,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 16 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 16 +test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 23 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 15 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 17 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-1] x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 23 @@ -583,6 +590,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 16 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 16 +test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 23 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 15 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 17 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[False-2] x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 23 @@ -610,6 +618,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_ test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 19 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 17 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 19 +test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 28 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 18 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 20 test.aaa_profiling.test_resultset.ResultSetTest.test_one_or_none[True-1] x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 28 @@ -637,6 +646,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpy test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6302 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 251 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6271 +test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 263 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 256 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 6256 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 288 @@ -647,7 +657,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpy test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_oracle_cx_oracle_dbapiunicode_nocextensions 6345 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_cextensions 278 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_nocextensions 6278 -test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 245 +test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 222 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_nocextensions 6245 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode @@ -664,6 +674,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cp test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6302 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 251 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6271 +test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 263 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 256 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 6256 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 288 @@ -674,7 +685,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cp test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_oracle_cx_oracle_dbapiunicode_nocextensions 6345 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_cextensions 278 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_postgresql_psycopg2_dbapiunicode_nocextensions 6278 -test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 245 +test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_cextensions 222 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode x86_64_linux_cpython_3.8_sqlite_pysqlite_dbapiunicode_nocextensions 6245 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_string @@ -691,6 +702,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6505 test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 458 test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6460 +test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 548 test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 521 test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 6521 test.aaa_profiling.test_resultset.ResultSetTest.test_string x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 528 @@ -718,6 +730,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpytho test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6505 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_cextensions 458 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6460 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_3.8_mariadb_mysqldb_dbapiunicode_cextensions 548 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_cextensions 521 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_3.8_mssql_pyodbc_dbapiunicode_nocextensions 6521 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode x86_64_linux_cpython_3.8_mysql_mysqldb_dbapiunicode_cextensions 528 |