From: Mike Bayer Date: Thu, 23 Oct 2008 02:22:57 +0000 (+0000) Subject: call count pinata party X-Git-Tag: rel_0_5rc3~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1ccdfb517237408f746068f466548f9331468d7f;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git call count pinata party --- diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 8e8a7528b8..bd0f15ecdf 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -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, diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 96dadd0455..c176f7082d 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -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 diff --git a/lib/sqlalchemy/pool.py b/lib/sqlalchemy/pool.py index 2df189498f..46307d19a7 100644 --- a/lib/sqlalchemy/pool.py +++ b/lib/sqlalchemy/pool.py @@ -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) diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 51664d64e9..ac46d57686 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -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 diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index cb2bcd6d60..39c31c8cb0 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -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 diff --git a/test/engine/pool.py b/test/engine/pool.py index 37310ec0a5..fa8b03b9f9 100644 --- a/test/engine/pool.py +++ b/test/engine/pool.py @@ -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() diff --git a/test/profiling/compiler.py b/test/profiling/compiler.py index 452940e099..9a09814987 100644 --- a/test/profiling/compiler.py +++ b/test/profiling/compiler.py @@ -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() diff --git a/test/profiling/zoomark.py b/test/profiling/zoomark.py index 47e459e6e7..f73a7882a8 100644 --- a/test/profiling/zoomark.py +++ b/test/profiling/zoomark.py @@ -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() diff --git a/test/profiling/zoomark_orm.py b/test/profiling/zoomark_orm.py index 1a179cc0ce..54f96ddbfa 100644 --- a/test/profiling/zoomark_orm.py +++ b/test/profiling/zoomark_orm.py @@ -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() diff --git a/test/testlib/profiling.py b/test/testlib/profiling.py index 5a406a7568..05b1435c37 100644 --- a/test/testlib/profiling.py +++ b/test/testlib/profiling.py @@ -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)):