diff options
-rw-r--r-- | CHANGES | 8 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 72 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/default.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/event.py | 171 | ||||
-rw-r--r-- | lib/sqlalchemy/events.py | 229 | ||||
-rw-r--r-- | test/base/test_events.py | 226 | ||||
-rw-r--r-- | test/engine/test_execute.py | 30 | ||||
-rw-r--r-- | test/lib/profiles.txt | 200 | ||||
-rw-r--r-- | test/sql/test_cte.py | 1 |
9 files changed, 699 insertions, 240 deletions
@@ -303,6 +303,14 @@ underneath "0.7.xx". Connection objects, not just Engine objects. [ticket:2511] + - [feature] The before_cursor_execute event + fires off for so-called "_cursor_execute" + events, which are usually special-case + executions of primary-key bound sequences + and default-generation SQL + phrases that invoke separately when RETURNING + is not used with INSERT. [ticket:2459] + - [bug] Fixed bug whereby if a database restart affected multiple connections, each connection would individually invoke a new diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 83ed551af..bf3131994 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -63,7 +63,10 @@ class Connection(Connectable): self.__invalid = False if _dispatch: self.dispatch = _dispatch + elif engine._has_events: + self.dispatch = self.dispatch._join(engine.dispatch) self._has_events = _has_events or engine._has_events + self._echo = self.engine._should_log_info() if _execution_options: self._execution_options =\ @@ -429,7 +432,6 @@ class Connection(Connectable): if self._has_events: self.dispatch.begin(self) - self.engine.dispatch.begin(self) try: self.engine.dialect.do_begin(self.connection) @@ -440,7 +442,6 @@ class Connection(Connectable): def _rollback_impl(self): if self._has_events: self.dispatch.rollback(self) - self.engine.dispatch.rollback(self) if self._still_open_and_connection_is_valid: if self._echo: @@ -457,7 +458,6 @@ class Connection(Connectable): def _commit_impl(self): if self._has_events: self.dispatch.commit(self) - self.engine.dispatch.commit(self) if self._echo: self.engine.logger.info("COMMIT") @@ -471,7 +471,6 @@ class Connection(Connectable): def _savepoint_impl(self, name=None): if self._has_events: self.dispatch.savepoint(self, name) - self.engine.dispatch.savepoint(self, name) if name is None: self.__savepoint_seq += 1 @@ -483,7 +482,6 @@ class Connection(Connectable): def _rollback_to_savepoint_impl(self, name, context): if self._has_events: self.dispatch.rollback_savepoint(self, name, context) - self.engine.dispatch.rollback_savepoint(self, name, context) if self._still_open_and_connection_is_valid: self.engine.dialect.do_rollback_to_savepoint(self, name) @@ -492,7 +490,6 @@ class Connection(Connectable): def _release_savepoint_impl(self, name, context): if self._has_events: self.dispatch.release_savepoint(self, name, context) - self.engine.dispatch.release_savepoint(self, name, context) if self._still_open_and_connection_is_valid: self.engine.dialect.do_release_savepoint(self, name) @@ -501,7 +498,6 @@ class Connection(Connectable): def _begin_twophase_impl(self, xid): if self._has_events: self.dispatch.begin_twophase(self, xid) - self.engine.dispatch.begin_twophase(self, xid) if self._still_open_and_connection_is_valid: self.engine.dialect.do_begin_twophase(self, xid) @@ -509,7 +505,6 @@ class Connection(Connectable): def _prepare_twophase_impl(self, xid): if self._has_events: self.dispatch.prepare_twophase(self, xid) - self.engine.dispatch.prepare_twophase(self, xid) if self._still_open_and_connection_is_valid: assert isinstance(self.__transaction, TwoPhaseTransaction) @@ -518,7 +513,6 @@ class Connection(Connectable): def _rollback_twophase_impl(self, xid, is_prepared): if self._has_events: self.dispatch.rollback_twophase(self, xid, is_prepared) - self.engine.dispatch.rollback_twophase(self, xid, is_prepared) if self._still_open_and_connection_is_valid: assert isinstance(self.__transaction, TwoPhaseTransaction) @@ -528,7 +522,6 @@ class Connection(Connectable): def _commit_twophase_impl(self, xid, is_prepared): if self._has_events: self.dispatch.commit_twophase(self, xid, is_prepared) - self.engine.dispatch.commit_twophase(self, xid, is_prepared) if self._still_open_and_connection_is_valid: assert isinstance(self.__transaction, TwoPhaseTransaction) @@ -658,10 +651,7 @@ class Connection(Connectable): """Execute a schema.ColumnDefault object.""" if self._has_events: - for fn in chain( - self.dispatch.before_execute, - self.engine.dispatch.before_execute - ): + for fn in self.dispatch.before_execute: default, multiparams, params = \ fn(self, default, multiparams, params) @@ -685,8 +675,6 @@ class Connection(Connectable): if self._has_events: self.dispatch.after_execute(self, default, multiparams, params, ret) - self.engine.dispatch.after_execute(self, - default, multiparams, params, ret) return ret @@ -694,10 +682,7 @@ class Connection(Connectable): """Execute a schema.DDL object.""" if self._has_events: - for fn in chain( - self.dispatch.before_execute, - self.engine.dispatch.before_execute - ): + for fn in self.dispatch.before_execute: ddl, multiparams, params = \ fn(self, ddl, multiparams, params) @@ -712,7 +697,7 @@ class Connection(Connectable): compiled ) if self._has_events: - self.engine.dispatch.after_execute(self, + self.dispatch.after_execute(self, ddl, multiparams, params, ret) return ret @@ -720,10 +705,7 @@ class Connection(Connectable): """Execute a sql.ClauseElement object.""" if self._has_events: - for fn in chain( - self.dispatch.before_execute, - self.engine.dispatch.before_execute - ): + for fn in self.dispatch.before_execute: elem, multiparams, params = \ fn(self, elem, multiparams, params) @@ -759,18 +741,13 @@ class Connection(Connectable): if self._has_events: self.dispatch.after_execute(self, elem, multiparams, params, ret) - self.engine.dispatch.after_execute(self, - elem, multiparams, params, ret) return ret def _execute_compiled(self, compiled, multiparams, params): """Execute a sql.Compiled object.""" if self._has_events: - for fn in chain( - self.dispatch.before_execute, - self.engine.dispatch.before_execute - ): + for fn in self.dispatch.before_execute: compiled, multiparams, params = \ fn(self, compiled, multiparams, params) @@ -786,18 +763,13 @@ class Connection(Connectable): if self._has_events: self.dispatch.after_execute(self, compiled, multiparams, params, ret) - self.engine.dispatch.after_execute(self, - compiled, multiparams, params, ret) return ret def _execute_text(self, statement, multiparams, params): """Execute a string SQL statement.""" if self._has_events: - for fn in chain( - self.dispatch.before_execute, - self.engine.dispatch.before_execute - ): + for fn in self.dispatch.before_execute: statement, multiparams, params = \ fn(self, statement, multiparams, params) @@ -813,8 +785,6 @@ class Connection(Connectable): if self._has_events: self.dispatch.after_execute(self, statement, multiparams, params, ret) - self.engine.dispatch.after_execute(self, - statement, multiparams, params, ret) return ret def _execute_context(self, dialect, constructor, @@ -847,10 +817,7 @@ class Connection(Connectable): parameters = parameters[0] if self._has_events: - for fn in chain( - self.dispatch.before_cursor_execute, - self.engine.dispatch.before_cursor_execute - ): + for fn in self.dispatch.before_cursor_execute: statement, parameters = \ fn(self, cursor, statement, parameters, context, context.executemany) @@ -893,11 +860,6 @@ class Connection(Connectable): parameters, context, context.executemany) - self.engine.dispatch.after_cursor_execute(self, cursor, - statement, - parameters, - context, - context.executemany) if context.compiled: context.post_exec() @@ -929,7 +891,7 @@ class Connection(Connectable): return result - def _cursor_execute(self, cursor, statement, parameters): + def _cursor_execute(self, cursor, statement, parameters, context=None): """Execute a statement + params on the given cursor. Adds appropriate logging and exception handling. @@ -940,6 +902,12 @@ class Connection(Connectable): terminates at _execute_context(). """ + if self._has_events: + for fn in self.dispatch.before_cursor_execute: + statement, parameters = \ + fn(self, cursor, statement, parameters, + context, context.executemany) + if self._echo: self.engine.logger.info(statement) self.engine.logger.info("%r", parameters) @@ -1006,12 +974,6 @@ class Connection(Connectable): parameters, context, e) - self.engine.dispatch.dbapi_error(self, - cursor, - statement, - parameters, - context, - e) context.handle_dbapi_exception(e) is_disconnect = isinstance(e, self.dialect.dbapi.Error) and \ diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 8518cf0da..1c74dab43 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -597,7 +597,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext): else: default_params = {} - conn._cursor_execute(self.cursor, stmt, default_params) + conn._cursor_execute(self.cursor, stmt, default_params, context=self) r = self.cursor.fetchone()[0] if type_ is not None: # apply type post processors to the result diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py index 8e9064cfc..ee4c0ad32 100644 --- a/lib/sqlalchemy/event.py +++ b/lib/sqlalchemy/event.py @@ -7,6 +7,7 @@ """Base event API.""" from . import util, exc +from itertools import chain CANCEL = util.symbol('CANCEL') NO_RETVAL = util.symbol('NO_RETVAL') @@ -37,7 +38,7 @@ def listen(target, identifier, fn, *args, **kw): tgt.dispatch._listen(tgt, identifier, fn, *args, **kw) return raise exc.InvalidRequestError("No such event '%s' for target '%s'" % - (identifier,target)) + (identifier, target)) def listens_for(target, identifier, *args, **kw): """Decorate a function as a listener for the given target + identifier. @@ -69,7 +70,7 @@ def remove(target, identifier, fn): """ for evt_cls in _registrars[identifier]: for tgt in evt_cls._accept_with(target): - tgt.dispatch._remove(identifier, tgt, fn, *args, **kw) + tgt.dispatch._remove(identifier, tgt, fn) return _registrars = util.defaultdict(list) @@ -112,6 +113,30 @@ class _Dispatch(object): def __init__(self, _parent_cls): self._parent_cls = _parent_cls + def _join(self, other): + """Create a 'join' of this :class:`._Dispatch` and another. + + This new dispatcher will dispatch events to both + :class:`._Dispatch` objects. + + Once constructed, the joined dispatch will respond to new events + added to this dispatcher, but may not be aware of events + added to the other dispatcher after creation of the join. This is + currently for performance reasons so that both dispatchers need + not be "evaluated" fully on each call. + + """ + if '_joined_dispatch_cls' not in self.__class__.__dict__: + cls = type( + "Joined%s" % self.__class__.__name__, + (_JoinedDispatcher, self.__class__), {} + ) + for ls in _event_descriptors(self): + setattr(cls, ls.name, _JoinedDispatchDescriptor(ls.name)) + + self.__class__._joined_dispatch_cls = cls + return self._joined_dispatch_cls(self, other) + def __reduce__(self): return _UnpickleDispatch(), (self._parent_cls, ) @@ -276,6 +301,7 @@ class _DispatchDescriptor(object): obj.__dict__[self.__name__] = ret return ret + class _EmptyListener(object): """Serves as a class-level interface to the events served by a _DispatchDescriptor, when there are no @@ -303,8 +329,9 @@ class _EmptyListener(object): and returns it. """ - obj.__dict__[self.name] = result = _ListenerCollection( - self.parent, obj._parent_cls) + result = _ListenerCollection(self.parent, obj._parent_cls) + if obj.__dict__[self.name] is self: + obj.__dict__[self.name] = result return result def _needs_modify(self, *args, **kw): @@ -324,14 +351,47 @@ class _EmptyListener(object): def __iter__(self): return iter(self.parent_listeners) - def __getitem__(self, index): - return (self.parent_listeners)[index] + def __nonzero__(self): + return False + +class _CompoundListener(object): + _exec_once = False + + def exec_once(self, *args, **kw): + """Execute this event, but only if it has not been + executed already for this collection.""" + + if not self._exec_once: + self(*args, **kw) + self._exec_once = True + + # I'm not entirely thrilled about the overhead here, + # but this allows class-level listeners to be added + # at any point. + # + # In the absense of instance-level listeners, + # we stay with the _EmptyListener object when called + # at the instance level. + + def __call__(self, *args, **kw): + """Execute this event.""" + + for fn in self.parent_listeners: + fn(*args, **kw) + for fn in self.listeners: + fn(*args, **kw) + + def __len__(self): + return len(self.parent_listeners) + len(self.listeners) + + def __iter__(self): + return chain(self.parent_listeners, self.listeners) def __nonzero__(self): - return bool(self.listeners) + return bool(self.listeners or self.parent_listeners) -class _ListenerCollection(object): +class _ListenerCollection(_CompoundListener): """Instance-level attributes on instances of :class:`._Dispatch`. Represents a collection of listeners. @@ -341,8 +401,6 @@ class _ListenerCollection(object): """ - _exec_once = False - def __init__(self, parent, target_cls): if target_cls not in parent._clslevel: parent.update_subclass(target_cls) @@ -360,42 +418,6 @@ class _ListenerCollection(object): """ return self - def exec_once(self, *args, **kw): - """Execute this event, but only if it has not been - executed already for this collection.""" - - if not self._exec_once: - self(*args, **kw) - self._exec_once = True - - def __call__(self, *args, **kw): - """Execute this event.""" - - for fn in self.parent_listeners: - fn(*args, **kw) - for fn in self.listeners: - fn(*args, **kw) - - # I'm not entirely thrilled about the overhead here, - # but this allows class-level listeners to be added - # at any point. - # - # In the absense of instance-level listeners, - # we stay with the _EmptyListener object when called - # at the instance level. - - def __len__(self): - return len(self.parent_listeners + self.listeners) - - def __iter__(self): - return iter(self.parent_listeners + self.listeners) - - def __getitem__(self, index): - return (self.parent_listeners + self.listeners)[index] - - def __nonzero__(self): - return bool(self.listeners or self.parent_listeners) - def _update(self, other, only_propagate=True): """Populate from the listeners in another :class:`_Dispatch` object.""" @@ -430,6 +452,62 @@ class _ListenerCollection(object): self.listeners[:] = [] self.propagate.clear() + +class _JoinedDispatcher(object): + """Represent a connection between two _Dispatch objects.""" + + def __init__(self, local, parent): + self.local = local + self.parent = parent + self._parent_cls = local._parent_cls + + +class _JoinedDispatchDescriptor(object): + def __init__(self, name): + self.name = name + + def __get__(self, obj, cls): + if obj is None: + return self + else: + obj.__dict__[self.name] = ret = _JoinedListener( + obj.parent, self.name, + getattr(obj.local, self.name) + ) + return ret + +class _JoinedListener(_CompoundListener): + _exec_once = False + def __init__(self, parent, name, local): + self.parent = parent + self.name = name + self.local = local + self.parent_listeners = self.local + + # fix .listeners for the parent. This means + # new events added to the parent won't be picked + # up here. Alternatively, the listeners can + # be via @property to just return getattr(self.parent, self.name) + # each time. less performant. + self.listeners = list(getattr(self.parent, self.name)) + + def for_modify(self, obj): + self.local = self.parent_listeners = self.local.for_modify(obj) + return self + + def insert(self, obj, target, propagate): + self.local.insert(obj, target, propagate) + + def append(self, obj, target, propagate): + self.local.append(obj, target, propagate) + + def remove(self, obj, target): + self.local.remove(obj, target) + + def clear(self): + raise NotImplementedError() + + class dispatcher(object): """Descriptor used by target classes to deliver the _Dispatch class at the class level @@ -446,3 +524,4 @@ class dispatcher(object): return self.dispatch_cls obj.__dict__['dispatch'] = disp = self.dispatch_cls(cls) return disp + diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py index 10717ee50..61392ea62 100644 --- a/lib/sqlalchemy/events.py +++ b/lib/sqlalchemy/events.py @@ -341,18 +341,56 @@ class ConnectionEvents(event.Events): The methods here define the name of an event as well as the names of members that are passed to listener functions. - An event listener can be associated with any :class:`.Connectable`, - such as an :class:`.Engine`, e.g.:: + An event listener can be associated with any :class:`.Connectable` + class or instance, such as an :class:`.Engine`, e.g.:: from sqlalchemy import event, create_engine - def before_execute(conn, clauseelement, multiparams, params): - log.info("Received statement: %s" % clauseelement) + def before_cursor_execute(conn, cursor, statement, parameters, context, + executemany): + log.info("Received statement: %s" % statement) engine = create_engine('postgresql://scott:tiger@localhost/test') - event.listen(engine, "before_execute", before_execute) + event.listen(engine, "before_cursor_execute", before_cursor_execute) - Some events allow modifiers to the :func:`.event.listen` function. + or with a specific :class:`.Connection`:: + + with engine.begin() as conn: + @event.listens_for(conn, 'before_cursor_execute') + def before_cursor_execute(conn, cursor, statement, parameters, + context, executemany): + log.info("Received statement: %s" % statement) + + The :meth:`.before_execute` and :meth:`.before_cursor_execute` + events can also be established with the ``retval=True`` flag, which + allows modification of the statement and parameters to be sent + to the database. The :meth:`.before_cursor_execute` event is + particularly useful here to add ad-hoc string transformations, such + as comments, to all executions:: + + from sqlalchemy.engine import Engine + from sqlalchemy import event + + @event.listens_for(Engine, "before_cursor_execute", retval=True) + def comment_sql_calls(conn, cursor, statement, parameters, + context, executemany): + statement = statement + " -- some comment" + return statement, parameters + + .. note:: :class:`.ConnectionEvents` can be established on any + combination of :class:`.Engine`, :class:`.Connection`, as well + as instances of each of those classes. Events across all + four scopes will fire off for a given instance of + :class:`.Connection`. However, for performance reasons, the + :class:`.Connection` object determines at instantiation time + whether or not its parent :class:`.Engine` has event listeners + established. Event listeners added to the :class:`.Engine` + class or to an instance of :class:`.Engine` *after* the instantiation + of a dependent :class:`.Connection` instance will usually + *not* be available on that :class:`.Connection` instance. The newly + added listeners will instead take effect for :class:`.Connection` + instances created subsequent to those event listeners being + established on the parent :class:`.Engine` class or instance. :param retval=False: Applies to the :meth:`.before_execute` and :meth:`.before_cursor_execute` events only. When True, the @@ -400,18 +438,107 @@ class ConnectionEvents(event.Events): event.Events._listen(target, identifier, fn) def before_execute(self, conn, clauseelement, multiparams, params): - """Intercept high level execute() events.""" + """Intercept high level execute() events, receiving uncompiled + SQL constructs and other objects prior to rendering into SQL. + + This event is good for debugging SQL compilation issues as well + as early manipulation of the parameters being sent to the database, + as the parameter lists will be in a consistent format here. + + This event can be optionally established with the ``retval=True`` + flag. The ``clauseelement``, ``multiparams``, and ``params`` + arguments should be returned as a three-tuple in this case:: + + @event.listens_for(Engine, "before_execute", retval=True) + def before_execute(conn, conn, clauseelement, multiparams, params): + # do something with clauseelement, multiparams, params + return clauseelement, multiparams, params + + :param conn: :class:`.Connection` object + :param clauseelement: SQL expression construct, :class:`.Compiled` + instance, or string statement passed to :meth:`.Connection.execute`. + :param multiparams: Multiple parameter sets, a list of dictionaries. + :param params: Single parameter set, a single dictionary. + + See also: + + :meth:`.before_cursor_execute` + + """ def after_execute(self, conn, clauseelement, multiparams, params, result): - """Intercept high level execute() events.""" + """Intercept high level execute() events after execute. + + + :param conn: :class:`.Connection` object + :param clauseelement: SQL expression construct, :class:`.Compiled` + instance, or string statement passed to :meth:`.Connection.execute`. + :param multiparams: Multiple parameter sets, a list of dictionaries. + :param params: Single parameter set, a single dictionary. + :param result: :class:`.ResultProxy` generated by the execution. + + """ def before_cursor_execute(self, conn, cursor, statement, parameters, context, executemany): - """Intercept low-level cursor execute() events.""" + """Intercept low-level cursor execute() events before execution, + receiving the string + SQL statement and DBAPI-specific parameter list to be invoked + against a cursor. + + This event is a good choice for logging as well as late modifications + to the SQL string. It's less ideal for parameter modifications except + for those which are specific to a target backend. + + This event can be optionally established with the ``retval=True`` + flag. The ``statement`` and ``parameters`` arguments should be + returned as a two-tuple in this case:: + + @event.listens_for(Engine, "before_cursor_execute", retval=True) + def before_cursor_execute(conn, cursor, statement, + parameters, context, executemany): + # do something with statement, parameters + return statement, parameters + + See the example at :class:`.ConnectionEvents`. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object + :param statement: string SQL statement + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param executemany: boolean, if ``True``, this is an ``executemany()`` + call, if ``False``, this is an ``execute()`` call. + + See also: + + :meth:`.before_execute` + + :meth:`.after_cursor_execute` + + """ def after_cursor_execute(self, conn, cursor, statement, parameters, context, executemany): - """Intercept low-level cursor execute() events.""" + """Intercept low-level cursor execute() events after execution. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object. Will have results pending + if the statement was a SELECT, but these should not be consumed + as they will be needed by the :class:`.ResultProxy`. + :param statement: string SQL statement + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param executemany: boolean, if ``True``, this is an ``executemany()`` + call, if ``False``, this is an ``execute()`` call. + + """ def dbapi_error(self, conn, cursor, statement, parameters, context, exception): @@ -439,37 +566,99 @@ class ConnectionEvents(event.Events): exception is then wrapped in a SQLAlchemy DBAPI exception wrapper and re-thrown. + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object + :param statement: string SQL statement + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param exception: The **unwrapped** exception emitted directly from the + DBAPI. The class here is specific to the DBAPI module in use. + .. versionadded:: 0.7.7 """ def begin(self, conn): - """Intercept begin() events.""" + """Intercept begin() events. + + :param conn: :class:`.Connection` object + + """ def rollback(self, conn): - """Intercept rollback() events.""" + """Intercept rollback() events. + + :param conn: :class:`.Connection` object + + """ def commit(self, conn): - """Intercept commit() events.""" + """Intercept commit() events. + + :param conn: :class:`.Connection` object + """ def savepoint(self, conn, name=None): - """Intercept savepoint() events.""" + """Intercept savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + + """ def rollback_savepoint(self, conn, name, context): - """Intercept rollback_savepoint() events.""" + """Intercept rollback_savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + :param context: :class:`.ExecutionContext` in use. May be ``None``. + + """ def release_savepoint(self, conn, name, context): - """Intercept release_savepoint() events.""" + """Intercept release_savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + :param context: :class:`.ExecutionContext` in use. May be ``None``. + + """ def begin_twophase(self, conn, xid): - """Intercept begin_twophase() events.""" + """Intercept begin_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + + """ def prepare_twophase(self, conn, xid): - """Intercept prepare_twophase() events.""" + """Intercept prepare_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + """ def rollback_twophase(self, conn, xid, is_prepared): - """Intercept rollback_twophase() events.""" + """Intercept rollback_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + :param is_prepared: boolean, indicates if + :meth:`.TwoPhaseTransaction.prepare` was called. + + """ def commit_twophase(self, conn, xid, is_prepared): - """Intercept commit_twophase() events.""" + """Intercept commit_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + :param is_prepared: boolean, indicates if + :meth:`.TwoPhaseTransaction.prepare` was called. + + """ diff --git a/test/base/test_events.py b/test/base/test_events.py index 57c06c328..7ae05e6c3 100644 --- a/test/base/test_events.py +++ b/test/base/test_events.py @@ -5,7 +5,7 @@ from test.lib.testing import eq_, assert_raises, assert_raises_message, \ from sqlalchemy import event, exc, util from test.lib import fixtures -class TestEvents(fixtures.TestBase): +class EventsTest(fixtures.TestBase): """Test class- and instance-level event registration.""" def setUp(self): @@ -161,7 +161,7 @@ class TestEvents(fixtures.TestBase): meth ) -class TestClsLevelListen(fixtures.TestBase): +class ClsLevelListenTest(fixtures.TestBase): def tearDown(self): @@ -242,7 +242,7 @@ class TestClsLevelListen(fixtures.TestBase): assert handler2 not in s2.dispatch.event_one -class TestAcceptTargets(fixtures.TestBase): +class AcceptTargetsTest(fixtures.TestBase): """Test default target acceptance.""" def setUp(self): @@ -313,7 +313,7 @@ class TestAcceptTargets(fixtures.TestBase): [listen_two, listen_four] ) -class TestCustomTargets(fixtures.TestBase): +class CustomTargetsTest(fixtures.TestBase): """Test custom target acceptance.""" def setUp(self): @@ -352,7 +352,7 @@ class TestCustomTargets(fixtures.TestBase): listen, "event_one", self.Target ) -class TestListenOverride(fixtures.TestBase): +class ListenOverrideTest(fixtures.TestBase): """Test custom listen functions which change the listener function signature.""" def setUp(self): @@ -398,7 +398,7 @@ class TestListenOverride(fixtures.TestBase): ] ) -class TestPropagate(fixtures.TestBase): +class PropagateTest(fixtures.TestBase): def setUp(self): class TargetEvents(event.Events): def event_one(self, arg): @@ -432,3 +432,217 @@ class TestPropagate(fixtures.TestBase): t2.dispatch.event_one(t2, 1) t2.dispatch.event_two(t2, 2) eq_(result, [(t2, 1)]) + +class JoinTest(fixtures.TestBase): + def setUp(self): + class TargetEvents(event.Events): + def event_one(self, target, arg): + pass + + class BaseTarget(object): + dispatch = event.dispatcher(TargetEvents) + + class TargetFactory(BaseTarget): + def create(self): + return TargetElement(self) + + class TargetElement(BaseTarget): + def __init__(self, parent): + self.dispatch = self.dispatch._join(parent.dispatch) + + def run_event(self, arg): + list(self.dispatch.event_one) + self.dispatch.event_one(self, arg) + + self.BaseTarget = BaseTarget + self.TargetFactory = TargetFactory + self.TargetElement = TargetElement + + def tearDown(self): + for cls in (self.TargetElement, + self.TargetFactory, self.BaseTarget): + if 'dispatch' in cls.__dict__: + event._remove_dispatcher(cls.__dict__['dispatch'].events) + + def _listener(self): + canary = [] + def listen(target, arg): + canary.append((target, arg)) + return listen, canary + + def test_neither(self): + element = self.TargetFactory().create() + element.run_event(1) + element.run_event(2) + element.run_event(3) + + def test_parent_class_only(self): + _listener, canary = self._listener() + + event.listen(self.TargetFactory, "event_one", _listener) + + element = self.TargetFactory().create() + element.run_event(1) + element.run_event(2) + element.run_event(3) + eq_( + canary, + [(element, 1), (element, 2), (element, 3)] + ) + + def test_parent_class_child_class(self): + l1, c1 = self._listener() + l2, c2 = self._listener() + + event.listen(self.TargetFactory, "event_one", l1) + event.listen(self.TargetElement, "event_one", l2) + + element = self.TargetFactory().create() + element.run_event(1) + element.run_event(2) + element.run_event(3) + eq_( + c1, + [(element, 1), (element, 2), (element, 3)] + ) + eq_( + c2, + [(element, 1), (element, 2), (element, 3)] + ) + + def test_parent_class_child_instance_apply_after(self): + l1, c1 = self._listener() + l2, c2 = self._listener() + + event.listen(self.TargetFactory, "event_one", l1) + element = self.TargetFactory().create() + + element.run_event(1) + + event.listen(element, "event_one", l2) + element.run_event(2) + element.run_event(3) + + eq_( + c1, + [(element, 1), (element, 2), (element, 3)] + ) + eq_( + c2, + [(element, 2), (element, 3)] + ) + + def test_parent_class_child_instance_apply_before(self): + l1, c1 = self._listener() + l2, c2 = self._listener() + + event.listen(self.TargetFactory, "event_one", l1) + element = self.TargetFactory().create() + + event.listen(element, "event_one", l2) + + element.run_event(1) + element.run_event(2) + element.run_event(3) + + eq_( + c1, + [(element, 1), (element, 2), (element, 3)] + ) + eq_( + c2, + [(element, 1), (element, 2), (element, 3)] + ) + + def test_parent_instance_child_class_apply_before(self): + l1, c1 = self._listener() + l2, c2 = self._listener() + + event.listen(self.TargetElement, "event_one", l2) + + factory = self.TargetFactory() + event.listen(factory, "event_one", l1) + + element = factory.create() + + element.run_event(1) + element.run_event(2) + element.run_event(3) + + eq_( + c1, + [(element, 1), (element, 2), (element, 3)] + ) + eq_( + c2, + [(element, 1), (element, 2), (element, 3)] + ) + + def test_parent_instance_child_class_apply_after(self): + l1, c1 = self._listener() + l2, c2 = self._listener() + + event.listen(self.TargetElement, "event_one", l2) + + factory = self.TargetFactory() + element = factory.create() + + element.run_event(1) + + event.listen(factory, "event_one", l1) + + element.run_event(2) + element.run_event(3) + + # c1 gets no events due to _JoinedListener + # fixing the "parent" at construction time. + # this can be changed to be "live" at the cost + # of performance. + eq_( + c1, + [] + #(element, 2), (element, 3)] + ) + eq_( + c2, + [(element, 1), (element, 2), (element, 3)] + ) + + def test_parent_instance_child_instance_apply_before(self): + l1, c1 = self._listener() + l2, c2 = self._listener() + factory = self.TargetFactory() + + event.listen(factory, "event_one", l1) + + element = factory.create() + event.listen(element, "event_one", l2) + + element.run_event(1) + element.run_event(2) + element.run_event(3) + + eq_( + c1, + [(element, 1), (element, 2), (element, 3)] + ) + eq_( + c2, + [(element, 1), (element, 2), (element, 3)] + ) + + def test_parent_events_child_no_events(self): + l1, c1 = self._listener() + factory = self.TargetFactory() + + event.listen(self.TargetElement, "event_one", l1) + element = factory.create() + + element.run_event(1) + element.run_event(2) + element.run_event(3) + + eq_( + c1, + [(element, 1), (element, 2), (element, 3)] + ) diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index a23f8d05a..85b03e0e9 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -1,4 +1,5 @@ -from test.lib.testing import eq_, assert_raises, assert_raises_message, config +from test.lib.testing import eq_, assert_raises, assert_raises_message, \ + config, is_ import re from test.lib.util import picklers from sqlalchemy.interfaces import ConnectionProxy @@ -1253,6 +1254,33 @@ class EngineEventsTest(fixtures.TestBase): canary, ['execute', 'cursor_execute'] ) + @testing.requires.sequences + @testing.provide_metadata + def test_cursor_execute(self): + canary = [] + def tracker(name): + def go(conn, cursor, statement, parameters, context, executemany): + canary.append((statement, context)) + return go + engine = engines.testing_engine() + + + t = Table('t', self.metadata, + Column('x', Integer, Sequence('t_id_seq'), primary_key=True), + implicit_returning=False + ) + self.metadata.create_all(engine) + with engine.begin() as conn: + event.listen(conn, 'before_cursor_execute', tracker('cursor_execute')) + conn.execute(t.insert()) + # we see the sequence pre-executed in the first call + assert "t_id_seq" in canary[0][0] + assert "INSERT" in canary[1][0] + # same context + is_( + canary[0][1], canary[1][1] + ) + def test_transactional(self): canary = [] def tracker(name): diff --git a/test/lib/profiles.txt b/test/lib/profiles.txt index 48e5caff9..d58d2ac38 100644 --- a/test/lib/profiles.txt +++ b/test/lib/profiles.txt @@ -21,28 +21,39 @@ test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2 test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_nocextensions 62 test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_cextensions 62 test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_nocextensions 62 -test.aaa_profiling.test_compiler.CompileTest.test_insert 3.2_postgresql_psycopg2_nocextensions 68 -test.aaa_profiling.test_compiler.CompileTest.test_insert 3.2_sqlite_pysqlite_nocextensions 68 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_select -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_cextensions 133 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_cextensions 133 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_nocextensions 133 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_cextensions 133 -test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_nocextensions 133 - -# TEST: test.aaa_profiling.test_compiler.CompileTest.test_select_second_time - -test.aaa_profiling.test_compiler.CompileTest.test_select_second_time 2.7_sqlite_pysqlite_nocextensions 133 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.5_sqlite_pysqlite_nocextensions 134 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.6_sqlite_pysqlite_nocextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_cextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_nocextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_cextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_nocextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_cextensions 135 +test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_nocextensions 135 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_update +test.aaa_profiling.test_compiler.CompileTest.test_update 2.5_sqlite_pysqlite_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.6_sqlite_pysqlite_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_cextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_nocextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_cextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_nocextensions 65 test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_cextensions 65 +test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_nocextensions 65 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.5_sqlite_pysqlite_nocextensions 129 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.6_sqlite_pysqlite_nocextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_cextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_nocextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_cextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_nocextensions 130 test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_cextensions 130 +test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_nocextensions 130 # TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity @@ -54,47 +65,39 @@ test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_ test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_postgresql_psycopg2_nocextensions 17987 test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_sqlite_pysqlite_cextensions 17987 test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 2.7_sqlite_pysqlite_nocextensions 17987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.2_postgresql_psycopg2_nocextensions 18987 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.2_sqlite_pysqlite_nocextensions 18987 # 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.5_sqlite_pysqlite_nocextensions 116038 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.6_sqlite_pysqlite_nocextensions 114788 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_cextensions 122288 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_nocextensions 125038 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_cextensions 114788 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_nocextensions 117538 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_cextensions 113760 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_nocextensions 116538 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.2_postgresql_psycopg2_nocextensions 121541 -test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.2_sqlite_pysqlite_nocextensions 121541 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.5_sqlite_pysqlite_nocextensions 116289 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.6_sqlite_pysqlite_nocextensions 116790 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_cextensions 122540 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_mysql_mysqldb_nocextensions 125290 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_cextensions 115040 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_postgresql_psycopg2_nocextensions 117790 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_cextensions 114040 +test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_nocextensions 116790 # 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.5_sqlite_pysqlite_nocextensions 19770 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.6_sqlite_pysqlite_nocextensions 18941 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_cextensions 19449 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_nocextensions 19709 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_cextensions 18873 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_nocextensions 19096 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_cextensions 18885 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_nocextensions 19117 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.2_postgresql_psycopg2_nocextensions 20217 -test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.2_sqlite_pysqlite_nocextensions 20274 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.5_sqlite_pysqlite_nocextensions 19852 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.6_sqlite_pysqlite_nocextensions 19217 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_cextensions 19491 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_mysql_mysqldb_nocextensions 19781 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_cextensions 18878 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_postgresql_psycopg2_nocextensions 19168 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_cextensions 18957 +test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_nocextensions 19217 # TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_load -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.5_sqlite_pysqlite_nocextensions 1132 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.6_sqlite_pysqlite_nocextensions 1113 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_cextensions 1295 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_nocextensions 1320 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_cextensions 1154 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_nocextensions 1179 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_cextensions 1103 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_nocextensions 1128 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.2_postgresql_psycopg2_nocextensions 1202 -test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.2_sqlite_pysqlite_nocextensions 1165 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.5_sqlite_pysqlite_nocextensions 1178 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.6_sqlite_pysqlite_nocextensions 1174 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_cextensions 1341 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_mysql_mysqldb_nocextensions 1366 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_cextensions 1200 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_postgresql_psycopg2_nocextensions 1225 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_cextensions 1149 +test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_nocextensions 1174 # TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_no_load @@ -106,8 +109,6 @@ test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_postgresql_psycopg2_nocextensions 98,16 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_cextensions 98,16 test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_nocextensions 98,16 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.2_postgresql_psycopg2_nocextensions 103,17 -test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.2_sqlite_pysqlite_nocextensions 103,17 # TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect @@ -119,8 +120,6 @@ test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_postgresql_psy test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_postgresql_psycopg2_nocextensions 67 test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlite_cextensions 67 test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlite_nocextensions 67 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.2_postgresql_psycopg2_nocextensions 55 -test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.2_sqlite_pysqlite_nocextensions 55 # TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect @@ -132,8 +131,6 @@ test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_postgresql_ps test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_postgresql_psycopg2_nocextensions 29 test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqlite_cextensions 29 test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqlite_nocextensions 29 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.2_postgresql_psycopg2_nocextensions 23 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.2_sqlite_pysqlite_nocextensions 23 # TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect @@ -145,8 +142,6 @@ test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_po test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_postgresql_psycopg2_nocextensions 6 test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sqlite_pysqlite_cextensions 6 test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sqlite_pysqlite_nocextensions 6 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.2_postgresql_psycopg2_nocextensions 7 -test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.2_sqlite_pysqlite_nocextensions 7 # TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute @@ -158,8 +153,6 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_postgresql_psycopg2_nocextensions 42 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_cextensions 40 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_nocextensions 42 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.2_postgresql_psycopg2_nocextensions 40 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.2_sqlite_pysqlite_nocextensions 40 # TEST: test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute @@ -171,12 +164,13 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_postgresql_psycopg2_nocextensions 65 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_cextensions 63 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_nocextensions 65 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.2_postgresql_psycopg2_nocextensions 63 -test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.2_sqlite_pysqlite_nocextensions 63 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.5_sqlite_pysqlite_nocextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.6_sqlite_pysqlite_nocextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_cextensions 14 +test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_mysql_mysqldb_nocextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_cextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_postgresql_psycopg2_nocextensions 14 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_cextensions 14 @@ -184,103 +178,87 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_string -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.5_sqlite_pysqlite_nocextensions 14369 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.6_sqlite_pysqlite_nocextensions 14370 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_cextensions 408 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_nocextensions 14428 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_cextensions 20394 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_nocextensions 34414 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_cextensions 350 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_nocextensions 14370 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.2_postgresql_psycopg2_nocextensions 14382 -test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.2_sqlite_pysqlite_nocextensions 14353 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.5_sqlite_pysqlite_nocextensions 14413 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.6_sqlite_pysqlite_nocextensions 14414 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_cextensions 452 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_nocextensions 14472 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_cextensions 20438 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_nocextensions 34458 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_cextensions 394 +test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_nocextensions 14414 # TEST: test.aaa_profiling.test_resultset.ResultSetTest.test_unicode -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.5_sqlite_pysqlite_nocextensions 14369 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.6_sqlite_pysqlite_nocextensions 14370 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_cextensions 408 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_nocextensions 44428 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_cextensions 20394 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_nocextensions 34414 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_cextensions 350 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_nocextensions 14370 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.2_postgresql_psycopg2_nocextensions 14382 -test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.2_sqlite_pysqlite_nocextensions 14353 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.5_sqlite_pysqlite_nocextensions 14413 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.6_sqlite_pysqlite_nocextensions 14414 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_cextensions 452 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_nocextensions 44472 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_cextensions 20438 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_nocextensions 34458 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_cextensions 394 +test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_nocextensions 14414 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 4915 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 4959 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 3.2_postgresql_psycopg2_nocextensions 4707 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5044 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 5088 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 247 test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 247 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 3.2_postgresql_psycopg2_nocextensions 236 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 3302 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 3526 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 3366 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 3590 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 10062 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 11678 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 3.2_postgresql_psycopg2_nocextensions 10973 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 10366 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 11982 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 998 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1102 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 3.2_postgresql_psycopg2_nocextensions 1076 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1005 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1109 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 1654 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 1697 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 3.2_postgresql_psycopg2_nocextensions 1600 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 1736 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 1779 # TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_cextensions 2154 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_nocextensions 2384 -test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 3.2_postgresql_psycopg2_nocextensions 2365 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_cextensions 2219 +test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_nocextensions 2449 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5842 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 5968 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 3.2_postgresql_psycopg2_nocextensions 5981 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5977 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 6096 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 391 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 398 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 3.2_postgresql_psycopg2_nocextensions 396 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 392 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 399 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 5846 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 6078 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 3.2_postgresql_psycopg2_nocextensions 6039 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 6124 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 6356 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 17885 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 19316 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 3.2_postgresql_psycopg2_nocextensions 19197 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 18140 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 19571 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1011 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1107 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 3.2_postgresql_psycopg2_nocextensions 1082 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1018 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1114 # TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 2555 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 2618 -test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 3.2_postgresql_psycopg2_nocextensions 2650 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 2614 +test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 2677 diff --git a/test/sql/test_cte.py b/test/sql/test_cte.py index 59b347ccd..6360e278c 100644 --- a/test/sql/test_cte.py +++ b/test/sql/test_cte.py @@ -350,3 +350,4 @@ class CTETest(fixtures.TestBase, AssertsCompiledSQL): checkpositional=('x', 'y'), dialect=dialect ) + |