]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- more inlining. nominal execution on sqlite down to 36 calls, from 51 in 0.6.
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 5 Dec 2010 17:09:06 +0000 (12:09 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 5 Dec 2010 17:09:06 +0000 (12:09 -0500)
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/event.py
test/aaa_profiling/test_pool.py
test/aaa_profiling/test_resultset.py

index 907b1930b8a7dddb181f623309134adc673bc9c5..dad075b3454d8dae3c7b2337a8789778782e0a21 100644 (file)
@@ -1225,13 +1225,14 @@ class Connection(Connectable):
     def _execute_function(self, func, multiparams, params):
         """Execute a sql.FunctionElement object."""
 
-        return self._execute_clauseelement(func.select(), multiparams, params)
+        return self._execute_clauseelement(func.select(), 
+                                            multiparams, params)
 
     def _execute_default(self, default, multiparams, params):
         """Execute a schema.ColumnDefault object."""
         
         try:
-            dialect = self.engine.dialect
+            dialect = self.dialect
             ctx = dialect.execution_ctx_cls._init_default(
                                 dialect, self)
         except Exception, e:
@@ -1242,21 +1243,18 @@ class Connection(Connectable):
         if self.should_close_with_result:
             self.close()
         return ret
-
+    
     def _execute_ddl(self, ddl, params, multiparams):
         """Execute a schema.DDL object."""
         
-        try:
-            dialect = self.engine.dialect
-            context = dialect.execution_ctx_cls.\
-                        _init_ddl(
-                                    dialect, 
-                                    self, 
-                                    ddl.compile(dialect=self.dialect))
-        except Exception, e:
-            self._handle_dbapi_exception(e, None, None, None, None)
-            raise
-        return self.__execute_context(context)
+        dialect = self.dialect
+        return self._execute_context(
+            dialect,
+            dialect.execution_ctx_cls._init_ddl,
+            None, 
+            None,
+            ddl.compile(dialect=dialect)
+        )
 
     def _execute_clauseelement(self, elem, multiparams, params):
         """Execute a sql.ClauseElement object."""
@@ -1267,7 +1265,7 @@ class Connection(Connectable):
         else:
             keys = []
 
-        dialect = self.engine.dialect
+        dialect = self.dialect
         if 'compiled_cache' in self._execution_options:
             key = dialect, elem, tuple(keys), len(params) > 1
             if key in self._execution_options['compiled_cache']:
@@ -1282,57 +1280,103 @@ class Connection(Connectable):
                             dialect=dialect, column_keys=keys, 
                             inline=len(params) > 1)
 
-        try:
-            context = dialect.execution_ctx_cls.\
-                            _init_compiled(dialect, self, compiled_sql, params)
-        except Exception, e:
-            self._handle_dbapi_exception(e, None, params, None, None)
-            raise
-        return self.__execute_context(context)
+
+        return self._execute_context(
+            dialect,
+            dialect.execution_ctx_cls._init_compiled,
+            None, 
+            params,
+            compiled_sql, params
+        )
 
     def _execute_compiled(self, compiled, multiparams, params):
         """Execute a sql.Compiled object."""
 
-        try:
-            dialect = self.engine.dialect
-            parameters=self.__distill_params(multiparams, params)
-            context = dialect.execution_ctx_cls.\
-                            _init_compiled(dialect, self, 
-                                            compiled, parameters)
-        except Exception, e:
-            self._handle_dbapi_exception(e, None, parameters, None, None)
-            raise
-        return self.__execute_context(context)
+        dialect = self.dialect
+        parameters=self.__distill_params(multiparams, params)
+        return self._execute_context(
+            dialect,
+            dialect.execution_ctx_cls._init_compiled,
+            None, 
+            parameters,
+            compiled, parameters
+        )
 
     def _execute_text(self, statement, multiparams, params):
         """Execute a string SQL statement."""
         
+        dialect = self.dialect
         parameters = self.__distill_params(multiparams, params)
+        return self._execute_context(
+            dialect,
+            dialect.execution_ctx_cls._init_statement,
+            statement, 
+            parameters,
+            statement, parameters
+        )
+
+    _before_cursor_execute = None
+    _after_cursor_execute = None
+
+    def _execute_context(self, dialect, constructor, 
+                                    statement, parameters, *args):
+        """Create an :class:`.ExecutionContext` and execute, returning
+        a :class:`.ResultProxy`."""
+        
         try:
-            dialect = self.engine.dialect
-            context = dialect.execution_ctx_cls.\
-                            _init_statement(dialect, self, 
-                                            statement, parameters)
+            context = constructor(dialect, self, *args)
         except Exception, e:
-            self._handle_dbapi_exception(e, statement, parameters, 
-                                            None, None)
+            self._handle_dbapi_exception(e, 
+                        statement, parameters, 
+                        None, None)
             raise
-        return self.__execute_context(context)
 
-    def __execute_context(self, context):
         if context.compiled:
             context.pre_exec()
             
-        if context.executemany:
-            self._cursor_executemany(
-                            context.cursor, 
-                            context.statement, 
-                            context.parameters, context=context)
-        else:
-            self._cursor_execute(
-                            context.cursor, 
-                            context.statement, 
-                            context.parameters[0], context=context)
+        cursor, statement, parameters = context.cursor, \
+                                        context.statement, \
+                                        context.parameters
+                                        
+        if not context.executemany:
+            parameters = parameters[0]
+                
+        if self._before_cursor_execute:
+            statement, parameters = self._before_cursor_execute(
+                                            context,
+                                            cursor, 
+                                            statement, 
+                                            parameters)
+
+        if self._echo:
+            self.engine.logger.info(statement)
+            self.engine.logger.info("%r", parameters)
+        try:
+            if context.executemany:
+                self.dialect.do_executemany(
+                                    cursor, 
+                                    statement, 
+                                    parameters, 
+                                    context)
+            else:
+                self.dialect.do_execute(
+                                    cursor, 
+                                    statement, 
+                                    parameters, 
+                                    context)
+        except Exception, e:
+            self._handle_dbapi_exception(
+                                e, 
+                                statement, 
+                                parameters, 
+                                cursor, 
+                                context)
+            raise
+
+
+        if self._after_cursor_execute:
+            self._after_cursor_execute(context, cursor, 
+                                        statement, parameters)
             
         if context.compiled:
             context.post_exec()
@@ -1342,16 +1386,56 @@ class Connection(Connectable):
         
         # create a resultproxy, get rowcount/implicit RETURNING
         # rows, close cursor if no further results pending
-        r = context.get_result_proxy()._autoclose()
-
+        result = context.get_result_proxy()
+
+        if context.isinsert:
+            if context._is_implicit_returning:
+                context._fetch_implicit_returning(result)
+                result.close(_autoclose_connection=False)
+            elif not context._is_explicit_returning:
+                result.close(_autoclose_connection=False)
+        elif result._metadata is None:
+            # no results, get rowcount 
+            # (which requires open cursor on some drivers
+            # such as kintersbasdb, mxodbc),
+            result.rowcount
+            result.close(_autoclose_connection=False)
+        
         if self.__transaction is None and context.should_autocommit:
             self._commit_impl()
         
-        if r.closed and self.should_close_with_result:
+        if result.closed and self.should_close_with_result:
             self.close()
         
-        return r
-    
+        return result
+
+    def _cursor_execute(self, cursor, statement, parameters):
+        """Execute a statement + params on the given cursor.
+
+        Adds appropriate logging and exception handling.
+        
+        This method is used by DefaultDialect for special-case
+        executions, such as for sequences and column defaults.   
+        The path of statement execution in the majority of cases 
+        terminates at _execute_context().
+        
+        """
+        if self._echo:
+            self.engine.logger.info(statement)
+            self.engine.logger.info("%r", parameters)
+        try:
+            self.dialect.do_execute(
+                                cursor, 
+                                statement, 
+                                parameters)
+        except Exception, e:
+            self._handle_dbapi_exception(
+                                e, 
+                                statement, 
+                                parameters, 
+                                cursor)
+            raise
+
     def _safe_close_cursor(self, cursor):
         """Close the given cursor, catching exceptions
         and turning into log warnings.
@@ -1419,45 +1503,6 @@ class Connection(Connectable):
         finally:
             del self._reentrant_error
 
-    def _cursor_execute(self, cursor, statement, parameters, context=None):
-        if self._echo:
-            self.engine.logger.info(statement)
-            self.engine.logger.info("%r", parameters)
-        try:
-            self.dialect.do_execute(
-                                cursor, 
-                                statement, 
-                                parameters, 
-                                context)
-        except Exception, e:
-            self._handle_dbapi_exception(
-                                e, 
-                                statement, 
-                                parameters, 
-                                cursor, 
-                                context)
-            raise
-
-    def _cursor_executemany(self, cursor, statement, 
-                                        parameters, context=None):
-        if self._echo:
-            self.engine.logger.info(statement)
-            self.engine.logger.info("%r", parameters)
-        try:
-            self.dialect.do_executemany(
-                                cursor, 
-                                statement, 
-                                parameters, 
-                                context)
-        except Exception, e:
-            self._handle_dbapi_exception(
-                                e, 
-                                statement, 
-                                parameters, 
-                                cursor, 
-                                context)
-            raise
-
     # poor man's multimethod/generic function thingy
     executors = {
         expression.FunctionElement: _execute_function,
@@ -1662,7 +1707,6 @@ class Engine(Connectable, log.Identified):
         self.engine = self
         log.instance_logger(self, echoflag=echo)
         if proxy:
-#            util.warn_deprecated("The 'proxy' argument to create_engine() is deprecated.  Use event.listen().")
             interfaces.ConnectionProxy._adapt_listener(self, proxy)
         if execution_options:
             self.update_execution_options(**execution_options)
@@ -1941,95 +1985,69 @@ def _listener_connection_cls(cls, dispatch):
                                     *(multiparams or []), 
                                     **(params or {}))
 
-        def _cursor_execute(self, cursor, statement, 
-                                    parameters, context=None):
-            for fn in dispatch.on_before_cursor_execute:
-                statement, parameters = \
-                            fn(self, cursor, statement, parameters, 
-                                            context, False)
-            
-            ret = super(EventListenerConnection, self).\
-                        _cursor_execute(cursor, statement, parameters, 
-                                            context)
-
-            for fn in dispatch.on_after_cursor_execute:
-                fn(self, cursor, statement, parameters, context, False)
-            
-            return ret
-            
-        def _cursor_executemany(self, cursor, statement, 
-                                    parameters, context=None):
+        def _before_cursor_execute(self, context, cursor, 
+                                            statement, parameters):
             for fn in dispatch.on_before_cursor_execute:
                 statement, parameters = \
                             fn(self, cursor, statement, parameters, 
-                                        context, True)
-
-            ret = super(EventListenerConnection, self).\
-                        _cursor_executemany(cursor, statement
-                                        parameters, context)
-
-            for fn in dispatch.on_after_cursor_execute:
-                fn(self, cursor, statement, parameters, context, True)
-            
-            return ret
+                                        context, context.executemany)
+            return statement, parameters
+        
+        def _after_cursor_execute(self, context, cursor
+                                            statement, parameters):
+            dispatch.on_after_cursor_execute(self, cursor, 
+                                                statement, 
+                                                parameters, 
+                                                context, 
+                                                context.executemany)
             
         def _begin_impl(self):
-            for fn in dispatch.on_begin:
-                fn(self)
+            dispatch.on_begin(self)
             return super(EventListenerConnection, self).\
                         _begin_impl()
             
         def _rollback_impl(self):
-            for fn in dispatch.on_rollback:
-                fn(self)
+            dispatch.on_rollback(self)
             return super(EventListenerConnection, self).\
                         _rollback_impl()
 
         def _commit_impl(self):
-            for fn in dispatch.on_commit:
-                fn(self)
+            dispatch.on_commit(self)
             return super(EventListenerConnection, self).\
                         _commit_impl()
 
         def _savepoint_impl(self, name=None):
-            for fn in dispatch.on_savepoint:
-                fn(self, name)
+            dispatch.on_savepoint(self, name)
             return super(EventListenerConnection, self).\
                         _savepoint_impl(name=name)
                 
         def _rollback_to_savepoint_impl(self, name, context):
-            for fn in dispatch.on_rollback_savepoint:
-                fn(self, name, context)
+            dispatch.on_rollback_savepoint(self, name, context)
             return super(EventListenerConnection, self).\
                         _rollback_to_savepoint_impl(name, context)
             
         def _release_savepoint_impl(self, name, context):
-            for fn in dispatch.on_release_savepoint:
-                fn(self, name, context)
+            dispatch.on_release_savepoint(self, name, context)
             return super(EventListenerConnection, self).\
                         _release_savepoint_impl(name, context)
             
         def _begin_twophase_impl(self, xid):
-            for fn in dispatch.on_begin_twophase:
-                fn(self, xid)
+            dispatch.on_begin_twophase(self, xid)
             return super(EventListenerConnection, self).\
                         _begin_twophase_impl(xid)
 
         def _prepare_twophase_impl(self, xid):
-            for fn in dispatch.on_prepare_twophase:
-                fn(self, xid)
+            dispatch.on_prepare_twophase(self, xid)
             return super(EventListenerConnection, self).\
                         _prepare_twophase_impl(xid)
 
         def _rollback_twophase_impl(self, xid, is_prepared):
-            for fn in dispatch.on_rollback_twophase:
-                fn(self, xid)
+            dispatch.on_rollback_twophase(self, xid)
             return super(EventListenerConnection, self).\
                         _rollback_twophase_impl(xid, is_prepared)
 
         def _commit_twophase_impl(self, xid, is_prepared):
-            for fn in dispatch.on_commit_twophase:
-                fn(self, xid, is_prepared)
+            dispatch.on_commit_twophase(self, xid, is_prepared)
             return super(EventListenerConnection, self).\
                         _commit_twophase_impl(xid, is_prepared)
 
@@ -2203,7 +2221,10 @@ class ResultMetaData(object):
         dialect = context.dialect
         typemap = dialect.dbapi_type_map
 
-        for i, (colname, coltype) in enumerate(m[0:2] for m in metadata):
+        for i, rec in enumerate(metadata):
+            colname = rec[0]
+            coltype = rec[1]
+            
             if dialect.description_encoding:
                 colname = colname.decode(dialect.description_encoding)
 
@@ -2289,9 +2310,6 @@ class ResultMetaData(object):
             except exc.NoSuchColumnError:
                 return False
 
-    def __len__(self):
-        return len(self.keys)
-
     def __getstate__(self):
         return {
             '_pickled_keymap': dict(
@@ -2404,27 +2422,6 @@ class ResultProxy(object):
         
         return self._saved_cursor.description
             
-    def _autoclose(self):
-        """called by the Connection to autoclose cursors that have no pending
-        results beyond those used by an INSERT/UPDATE/DELETE with no explicit
-        RETURNING clause.
-        
-        """
-        if self.context.isinsert:
-            if self.context._is_implicit_returning:
-                self.context._fetch_implicit_returning(self)
-                self.close(_autoclose_connection=False)
-            elif not self.context._is_explicit_returning:
-                self.close(_autoclose_connection=False)
-        elif self._metadata is None:
-            # no results, get rowcount 
-            # (which requires open cursor on some drivers
-            # such as kintersbasdb, mxodbc),
-            self.rowcount
-            self.close(_autoclose_connection=False)
-        
-        return self
-    
     def close(self, _autoclose_connection=True):
         """Close this ResultProxy.
 
index ebc0018210d81bc08a61a5692f427f7812d43733..0717a8fef3e541192bc9ecf8c1524a8c38fd6215 100644 (file)
@@ -556,8 +556,6 @@ class DefaultExecutionContext(base.ExecutionContext):
     def connection(self):
         return self._connection._branch()
 
-
-
     def should_autocommit_text(self, statement):
         return AUTOCOMMIT_REGEXP.match(statement)
 
index 2df42d64d4fd69d98b6bb7a9d131b385f9716007..ed33bb74c63d5051cd31034cd8951443ddbb7bb0 100644 (file)
@@ -45,6 +45,20 @@ class _Dispatch(object):
     """Mirror the event listening definitions of an Events class with 
     listener collections.
     
+    Classes which define a "dispatch" member will return a 
+    non-instantiated :class:`._Dispatch` subclass when the member 
+    is accessed at the class level.  When the "dispatch" member is 
+    accessed at the instance level of its owner, an instance
+    of the :class:`._Dispatch` class is returned.
+    
+    A :class:`._Dispatch` class is generated for each :class:`.Events`
+    class defined, by the :func:`._create_dispatcher_class` function.  
+    The original :class:`.Events` classes remain untouched.   
+    This decouples the construction of :class:`.Events` subclasses from
+    the implementation used by the event internals, and allows 
+    inspecting tools like Sphinx to work in an unsurprising
+    way against the public API.
+    
     """
     
     def __init__(self, parent_cls):
@@ -75,6 +89,9 @@ class _EventMeta(type):
         return type.__init__(cls, classname, bases, dict_)
     
 def _create_dispatcher_class(cls, classname, bases, dict_):
+    """Create a :class:`._Dispatch` class corresponding to an 
+    :class:`.Events` class."""
+    
     # there's all kinds of ways to do this,
     # i.e. make a Dispatch class that shares the 'listen' method
     # of the Event class, this is the straight monkeypatch.
@@ -130,7 +147,7 @@ class Events(object):
                 getattr(cls.dispatch, attr).clear()
                 
 class _DispatchDescriptor(object):
-    """Class-level attributes on _Dispatch classes."""
+    """Class-level attributes on :class:`._Dispatch` classes."""
     
     def __init__(self, fn):
         self.__name__ = fn.__name__
@@ -162,8 +179,9 @@ class _DispatchDescriptor(object):
         return result
 
 class _ListenerCollection(object):
-    """Represent a collection of listeners linked
-    to an instance of _Dispatch.
+    """Instance-level attributes on instances of :class:`._Dispatch`.
+    
+    Represents a collection of listeners.
     
     """
 
index 1df93d980e77152af508097ac37fb592d842f681..b03afbacc6baca2f96a527e4518202a720b73a0c 100644 (file)
@@ -18,8 +18,8 @@ class QueuePoolTest(TestBase, AssertsExecutionResults):
                          use_threadlocal=True)
 
 
-    @profiling.function_call_count(64, {'2.4': 42, '2.7':75
-                                            '2.7+cextension':75,
+    @profiling.function_call_count(64, {'2.4': 42, '2.7':67
+                                            '2.7+cextension':67,
                                             '3.0':65, '3.1':65},
                                             variance=.10)
     def test_first_connect(self):
index 9904267dc602edf238c50e6c982a25de543aadda..74c0927c680bdf0c10a31bf38931531e7b86cc0c 100644 (file)
@@ -40,3 +40,30 @@ class ResultSetTest(TestBase, AssertsExecutionResults):
                                    '2.6+cextension': 409, '2.7+cextension':409})
     def test_unicode(self):
         [tuple(row) for row in t2.select().execute().fetchall()]
+
+class ExecutionTest(TestBase):
+    __only_on__ = 'sqlite'
+    
+    def test_minimal_connection_execute(self):
+        # create an engine without any instrumentation.
+        e = create_engine('sqlite://')
+        c = e.connect()
+        # ensure initial connect activities complete
+        c.execute("select 1")
+        
+        @profiling.function_call_count(36, variance=.01)
+        def go():
+            c.execute("select 1")
+        go()
+
+    def test_minimal_engine_execute(self):
+        # create an engine without any instrumentation.
+        e = create_engine('sqlite://')
+        # ensure initial connect activities complete
+        e.execute("select 1")
+
+        @profiling.function_call_count(59, variance=.01)
+        def go():
+            e.execute("select 1")
+        go()
+