]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
call count pinata party
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 23 Oct 2008 02:22:57 +0000 (02:22 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 23 Oct 2008 02:22:57 +0000 (02:22 +0000)
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/pool.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/expression.py
test/engine/pool.py
test/profiling/compiler.py
test/profiling/zoomark.py
test/profiling/zoomark_orm.py
test/testlib/profiling.py

index 8e8a7528b8e8ca407ca18276cd7d27c0d9e94866..bd0f15ecdf5bb4825fa7a7667b06e1716ca287bd 100644 (file)
@@ -355,11 +355,6 @@ class ExecutionContext(object):
 
         raise NotImplementedError()
 
-    def get_rowcount(self):
-        """Return the count of rows updated/deleted for an UPDATE/DELETE statement."""
-
-        raise NotImplementedError()
-
     def should_autocommit_compiled(self, compiled):
         """return True if the given Compiled object refers to a "committable" statement."""
 
@@ -1400,17 +1395,12 @@ class ResultProxy(object):
         self.__echo = context.engine._should_log_info
         if context.returns_rows:
             self._init_metadata()
-            self._rowcount = None
         else:
-            self._rowcount = context.get_rowcount()
             self.close()
     
     @property
     def rowcount(self):
-        if self._rowcount is not None:
-            return self._rowcount
-        else:
-            return self.context.get_rowcount()
+        return self.context.get_rowcount()
 
     @property
     def lastrowid(self):
@@ -1421,8 +1411,8 @@ class ResultProxy(object):
         return self.context.out_parameters
 
     def _init_metadata(self):
-        self.__props = {}
-        self._key_cache = self._create_key_cache()
+        self._props = util.PopulateDict(None)
+        self._props.creator = self.__key_fallback()
         self.keys = []
         metadata = self.cursor.description
 
@@ -1449,49 +1439,45 @@ class ResultProxy(object):
 
                 rec = (type_, type_.dialect_impl(self.dialect).result_processor(self.dialect), i)
 
-                if self.__props.setdefault(name.lower(), rec) is not rec:
-                    self.__props[name.lower()] = (type_, self.__ambiguous_processor(name), 0)
+                if self._props.setdefault(name.lower(), rec) is not rec:
+                    self._props[name.lower()] = (type_, self.__ambiguous_processor(name), 0)
 
                 # store the "origname" if we truncated (sqlite only)
                 if origname:
-                    if self.__props.setdefault(origname.lower(), rec) is not rec:
-                        self.__props[origname.lower()] = (type_, self.__ambiguous_processor(origname), 0)
+                    if self._props.setdefault(origname.lower(), rec) is not rec:
+                        self._props[origname.lower()] = (type_, self.__ambiguous_processor(origname), 0)
 
                 self.keys.append(colname)
-                self.__props[i] = rec
+                self._props[i] = rec
                 if obj:
                     for o in obj:
-                        self.__props[o] = rec
+                        self._props[o] = rec
 
             if self.__echo:
                 self.context.engine.logger.debug(
                     "Col " + repr(tuple(x[0] for x in metadata)))
-
-    def _create_key_cache(self):
-        # local copies to avoid circular ref against 'self'
-        props = self.__props
-        def lookup_key(key):
-            """Given a key, which could be a ColumnElement, string, etc.,
-            matches it to the appropriate key we got from the result set's
-            metadata; then cache it locally for quick re-access."""
-
+    
+    def __key_fallback(self):
+        # create a closure without 'self' to avoid circular references
+        props = self._props
+        
+        def fallback(key):
             if isinstance(key, basestring):
                 key = key.lower()
-            try:
-                rec = props[key]
-            except KeyError:
-                # fallback for targeting a ColumnElement to a textual expression
-                # this is a rare use case which only occurs when matching text()
-                # constructs to ColumnElements
-                if isinstance(key, expression.ColumnElement):
-                    if key._label and key._label.lower() in props:
-                        return props[key._label.lower()]
-                    elif hasattr(key, 'name') and key.name.lower() in props:
-                        return props[key.name.lower()]
-                raise exc.NoSuchColumnError("Could not locate column in row for column '%s'" % (str(key)))
-
-            return rec
-        return util.PopulateDict(lookup_key)
+                if key in props:
+                    return props[key]
+
+            # fallback for targeting a ColumnElement to a textual expression
+            # this is a rare use case which only occurs when matching text()
+            # constructs to ColumnElements
+            if isinstance(key, expression.ColumnElement):
+                if key._label and key._label.lower() in props:
+                    return props[key._label.lower()]
+                elif hasattr(key, 'name') and key.name.lower() in props:
+                    return props[key.name.lower()]
+
+            raise exc.NoSuchColumnError("Could not locate column in row for column '%s'" % (str(key)))
+        return fallback
 
     def __ambiguous_processor(self, colname):
         def process(value):
@@ -1518,7 +1504,7 @@ class ResultProxy(object):
         try:
             # _key_cache uses __missing__ in 2.5, so not much alternative
             # to catching KeyError
-            self._key_cache[key]
+            self._props[key]
             return True
         except KeyError:
             return False
@@ -1586,7 +1572,7 @@ class ResultProxy(object):
 
     def _get_col(self, row, key):
         try:
-            type_, processor, index = self._key_cache[key]
+            type_, processor, index = self._props[key]
         except TypeError:
             # the 'slice' use case is very infrequent,
             # so we use an exception catch to reduce conditionals in _get_col
@@ -1735,7 +1721,7 @@ class BufferedColumnResultProxy(ResultProxy):
 
     def _get_col(self, row, key):
         try:
-            rec = self._key_cache[key]
+            rec = self._props[key]
             return row[rec[2]]
         except TypeError:
             # the 'slice' use case is very infrequent,
index 96dadd0455d9fa4c15048b7c28b6d52c858e0b73..c176f7082dfdcbed5cc436dbc1cb38f0c201bf54 100644 (file)
@@ -260,7 +260,6 @@ class DefaultExecutionContext(base.ExecutionContext):
     def should_autocommit_text(self, statement):
         return AUTOCOMMIT_REGEXP.match(statement)
 
-
     def create_cursor(self):
         return self._connection.connection.cursor()
 
@@ -287,7 +286,7 @@ class DefaultExecutionContext(base.ExecutionContext):
             return self._rowcount
         else:
             return self.cursor.rowcount
-
+        
     def supports_sane_rowcount(self):
         return self.dialect.supports_sane_rowcount
 
index 2df189498f19bcccf4bc9d4ffeb797874d40dba5..46307d19a72c49c579af54421ec6b55fa36fc313 100644 (file)
@@ -439,12 +439,13 @@ class _ConnectionFairy(object):
         self._connection_record = None
 
 class _CursorFairy(object):
-    __slots__ = '__parent', 'cursor'
+    __slots__ = '__parent', 'cursor', 'execute'
 
     def __init__(self, parent, cursor):
         self.__parent = parent
         self.cursor = cursor
-
+        self.execute = cursor.execute
+        
     def invalidate(self, e=None):
         self.__parent.invalidate(e=e)
 
index 51664d64e966dff7626a0c3a0d171b6b70ba4d00..ac46d57686a28ae5b4666f501abe029bf24d873b 100644 (file)
@@ -109,6 +109,22 @@ FUNCTIONS = {
     functions.user: 'USER'
 }
 
+
+class _CompileLabel(object):
+    """lightweight label object which acts as an expression._Label."""
+
+    __metaclass__ = sql._FigureVisitName
+    __visit_name__ = 'label'
+    __slots__ = 'element', 'name'
+    
+    def __init__(self, col, name):
+        self.element = col
+        self.name = name
+        
+    @property
+    def quote(self):
+        return self.element.quote
+
 class DefaultCompiler(engine.Compiled):
     """Default implementation of Compiled.
 
@@ -175,9 +191,7 @@ class DefaultCompiler(engine.Compiled):
         self.string = self.process(self.statement)
 
     def process(self, obj, **kwargs):
-        meth = getattr(self, "visit_%s" % obj.__visit_name__, None)
-        if meth:
-            return meth(obj, **kwargs)
+        return obj._compiler_dispatch(self, **kwargs)
 
     def is_subquery(self):
         return self.stack and len(self.stack) > 1 and self.stack[-1].get('from')
@@ -243,8 +257,8 @@ class DefaultCompiler(engine.Compiled):
 
         if result_map is not None:
             result_map[name.lower()] = (name, (column, ), column.type)
-
-        if getattr(column, "is_literal", False):
+        
+        if column.is_literal:
             name = self.escape_literal_column(name)
         else:
             name = self.preparer.quote(name, column.quote)
@@ -252,7 +266,7 @@ class DefaultCompiler(engine.Compiled):
         if column.table is None or not column.table.named_with_column:
             return name
         else:
-            if getattr(column.table, 'schema', None):
+            if column.table.schema:
                 schema_prefix = self.preparer.quote(column.table.schema, column.table.quote_schema) + '.'
             else:
                 schema_prefix = ''
@@ -432,8 +446,8 @@ class DefaultCompiler(engine.Compiled):
         if isinstance(column, sql._Label):
             return column
 
-        if select.use_labels and getattr(column, '_label', None):
-            return column.label(column._label)
+        if select.use_labels and column._label:
+            return _CompileLabel(column, column._label)
 
         if \
             asfrom and \
@@ -441,9 +455,9 @@ class DefaultCompiler(engine.Compiled):
             not column.is_literal and \
             column.table is not None and \
             not isinstance(column.table, sql.Select):
-            return column.label(column.name)
+            return _CompileLabel(column, column.name)
         elif not isinstance(column, (sql._UnaryExpression, sql._TextClause, sql._BindParamClause)) and (not hasattr(column, 'name') or isinstance(column, sql._Function)):
-            return column.label(column.anon_label)
+            return _CompileLabel(column, column.anon_label)
         else:
             return column
 
index cb2bcd6d606b43e50f50573d947b34922f9984f3..39c31c8cb0c4b8908c6da7f5ada906849f6629df 100644 (file)
@@ -944,7 +944,6 @@ def is_column(col):
     """True if ``col`` is an instance of ``ColumnElement``."""
     return isinstance(col, ColumnElement)
 
-
 class _FigureVisitName(type):
     def __init__(cls, clsname, bases, dict):
         if not '__visit_name__' in cls.__dict__:
@@ -952,6 +951,21 @@ class _FigureVisitName(type):
             x = m.group(1)
             x = re.sub(r'(?!^)[A-Z]', lambda m:'_'+m.group(0).lower(), x)
             cls.__visit_name__ = x.lower()
+        
+        # set up an optimized visit dispatch function
+        # for use by the compiler
+        visit_name = cls.__dict__["__visit_name__"]
+        if isinstance(visit_name, str):
+            func_text = "def _compiler_dispatch(self, visitor, **kw):\n"\
+            "    return visitor.visit_%s(self, **kw)" % visit_name
+        else:
+            func_text = "def _compiler_dispatch(self, visitor, **kw):\n"\
+            "    return getattr(visitor, 'visit_%s' % self.__visit_name__)(self, **kw)"
+    
+        env = locals().copy()
+        exec func_text in env
+        cls._compiler_dispatch = env['_compiler_dispatch']
+        
         super(_FigureVisitName, cls).__init__(clsname, bases, dict)
 
 class ClauseElement(object):
@@ -1682,6 +1696,7 @@ class FromClause(Selectable):
     named_with_column = False
     _hide_froms = []
     quote = None
+    schema = None
 
     def _get_from_objects(self, **modifiers):
         return []
@@ -2553,19 +2568,19 @@ class _Label(ColumnElement):
     def __init__(self, name, element, type_=None):
         while isinstance(element, _Label):
             element = element.element
-        self.name = name or "{ANON %d %s}" % (id(self), getattr(element, 'name', 'anon'))
-        self.element = element.self_group(against=operators.as_)
-        self.type = sqltypes.to_instance(type_ or getattr(element, 'type', None))
+        self.name = self.key = self._label = name or "{ANON %d %s}" % (id(self), getattr(element, 'name', 'anon'))
+        self._element = element
+        self._type = type_
         self.quote = element.quote
-
-    @property
-    def key(self):
-        return self.name
-
-    @property
-    def _label(self):
-        return self.name
-
+    
+    @util.memoized_property
+    def type(self):
+        return sqltypes.to_instance(self._type or getattr(element, 'type', None))
+        
+    @util.memoized_property
+    def element(self):
+        return self._element.self_group(against=operators.as_)
+        
     def _proxy_attr(name):
         get = attrgetter(name)
         def attr(self):
@@ -2693,7 +2708,7 @@ class TableClause(_Immutable, FromClause):
     """
 
     named_with_column = True
-
+    
     def __init__(self, name, *columns):
         super(TableClause, self).__init__()
         self.name = self.fullname = name
index 37310ec0a56f78a84e61696c4990cacb5c87dc3d..fa8b03b9f91aadadfff79756f62eac475bb94897 100644 (file)
@@ -29,6 +29,8 @@ class MockConnection(object):
     def cursor(self):
         return MockCursor()
 class MockCursor(object):
+    def execute(self, *args, **kw):
+        pass
     def close(self):
         pass
 mock_dbapi = MockDBAPI()
index 452940e099b2d82c187f28a4fefe4805a136a321..9a0981498735794ebaaaea9ce7eb5b0fdd49804b 100644 (file)
@@ -23,7 +23,7 @@ class CompileTest(TestBase, AssertsExecutionResults):
     def test_update(self):
         t1.update().compile()
 
-    @profiling.function_call_count(228, versions={'2.4':131})
+    @profiling.function_call_count(211, versions={'2.4':131})
     def test_select(self):
         s = select([t1], t1.c.c2==t2.c.c1)
         s.compile()
index 47e459e6e729067d5dc5b4b0149a9d8e2b455a9c..f73a7882a8fbc2d2503c1a5daab7e158b145f008 100644 (file)
@@ -332,11 +332,11 @@ class ZooMarkTest(TestBase):
     def test_profile_2_insert(self):
         self.test_baseline_2_insert()
 
-    @profiling.function_call_count(4662, {'2.4': 2557})
+    @profiling.function_call_count(4178, {'2.4': 2557})
     def test_profile_3_properties(self):
         self.test_baseline_3_properties()
 
-    @profiling.function_call_count(17098, {'2.4': 10549})
+    @profiling.function_call_count(15869, {'2.4': 10549})
     def test_profile_4_expressions(self):
         self.test_baseline_4_expressions()
 
@@ -348,7 +348,7 @@ class ZooMarkTest(TestBase):
     def test_profile_6_editing(self):
         self.test_baseline_6_editing()
 
-    @profiling.function_call_count(3614, {'2.4': 2198})
+    @profiling.function_call_count(3276, {'2.4': 2198})
     def test_profile_7_multiview(self):
         self.test_baseline_7_multiview()
 
index 1a179cc0ce3face66b2a350c1e57e484cb92aeef..54f96ddbfa707e114732cc9fedb09ee7fe369530 100644 (file)
@@ -298,11 +298,11 @@ class ZooMarkTest(TestBase):
     def test_profile_2_insert(self):
         self.test_baseline_2_insert()
 
-    @profiling.function_call_count(8097)
+    @profiling.function_call_count(7305)
     def test_profile_3_properties(self):
         self.test_baseline_3_properties()
 
-    @profiling.function_call_count(28211)
+    @profiling.function_call_count(25760)
     def test_profile_4_expressions(self):
         self.test_baseline_4_expressions()
 
@@ -310,7 +310,7 @@ class ZooMarkTest(TestBase):
     def test_profile_5_aggregates(self):
         self.test_baseline_5_aggregates()
 
-    @profiling.function_call_count(3985)
+    @profiling.function_call_count(3752)
     def test_profile_6_editing(self):
         self.test_baseline_6_editing()
 
index 5a406a75687c4ab43b144b18abeee44e03d9ab17..05b1435c37b7ec7b687ae44405859ec1c5135411 100644 (file)
@@ -60,7 +60,6 @@ def profiled(target=None, **target_opts):
                     stats.print_stats(limit)
                 else:
                     stats.print_stats()
-
             os.unlink(filename)
             return result
         return _function_named(profiled, fn.__name__)
@@ -116,7 +115,7 @@ def function_call_count(count=None, versions={}, variance=0.05):
                 if testlib.config.options.verbose:
                     stats.sort_stats('calls', 'cumulative')
                     stats.print_stats()
-
+                    #stats.print_callers()
                 deviance = int(count * variance)
                 if (calls < (count - deviance) or
                     calls > (count + deviance)):