From: Mike Bayer Date: Fri, 10 Dec 2010 00:06:22 +0000 (-0500) Subject: callcount destructo engage X-Git-Tag: rel_0_7b1~185 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5622cfd5ecd5efe5e8c737be6b2ba0100aaf86b0;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git callcount destructo engage --- diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py index ed33bb74c6..dce09220ca 100644 --- a/lib/sqlalchemy/event.py +++ b/lib/sqlalchemy/event.py @@ -204,7 +204,9 @@ class _ListenerCollection(object): def __call__(self, *args, **kw): """Execute this event.""" - for fn in self.parent_listeners + self.listeners: + 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, diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 5686bdd334..90d105dc94 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -7,11 +7,13 @@ """ -Semi-private module containing various base classes used throughout the ORM. +Contains various base classes used throughout the ORM. -Defines the extension classes :class:`MapperExtension`, -:class:`SessionExtension`, and :class:`AttributeExtension` as -well as other user-subclassable extension objects. +Defines the now deprecated ORM extension classes as well +as ORM internals. + +Other than the deprecated extensions, this module and the +classes within should be considered mostly private. """ @@ -67,6 +69,19 @@ class MapperProperty(object): """ + get_col_value = None + """Optional method which converts an attribute value into a per-column + value:: + + def get_col_value(self, column, value): + ... + + Basically used by CompositeProperty. + + The mapper checks this attribute for non-None to reduce callcounts. + + """ + def setup(self, context, entity, path, adapter, **kwargs): """Called by Query for the purposes of constructing a SQL statement. diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 5bd4e0f41a..54d6a53e27 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -1262,9 +1262,12 @@ class Mapper(object): return [self._get_state_attr_by_column(state, dict_, column) for column in self.primary_key] - # TODO: improve names? def _get_state_attr_by_column(self, state, dict_, column, passive=False): - return self._columntoproperty[column]._getattr(state, dict_, column, passive=passive) + prop = self._columntoproperty[column] + value = state.manager[prop.key].impl.get(state, dict_, passive=passive) + if prop.get_col_value: + value = prop.get_col_value(column, value) + return value def _set_state_attr_by_column(self, state, dict_, column, value): return self._columntoproperty[column]._setattr(state, dict_, value, column) @@ -1499,9 +1502,10 @@ class Mapper(object): history = attributes.get_state_history( state, prop.key, passive=True) if history.added: - params[col.key] = \ - prop.get_col_value(col, - history.added[0]) + value = history.added[0] + if prop.get_col_value: + value = prop.get_col_value(col, value) + params[col.key] = value hasdata = True if hasdata: update.append((state, state_dict, params, mapper, @@ -1636,8 +1640,7 @@ class Mapper(object): pks = mapper._pks_by_table[table] - isinsert = not has_identity and \ - not row_switch + isinsert = not has_identity and not row_switch params = {} value_params = {} @@ -1648,23 +1651,25 @@ class Mapper(object): if col is mapper.version_id_col: params[col.key] = \ mapper.version_id_generator(None) - elif col in pks: - value = \ - mapper._get_state_attr_by_column( - state, state_dict, col) - if value is not None: - params[col.key] = value else: - value = \ - mapper._get_state_attr_by_column( - state, state_dict, col) - if ((col.default is None and - col.server_default is None) or - value is not None): - if isinstance(value, sql.ClauseElement): - value_params[col] = value - else: - params[col.key] = value + # inline of _get_state_attr_by_column + prop = mapper._columntoproperty[col] + value = state_dict.get(prop.key, None) + + if prop.get_col_value: + value = prop.get_col_value(col, value) + + if value is None: + if col.default is None and \ + col.server_default is None and \ + col not in pks: + + params[col.key] = value + elif isinstance(value, sql.ClauseElement): + value_params[col] = value + else: + params[col.key] = value + insert.append((state, state_dict, params, mapper, connection, value_params)) else: @@ -1709,9 +1714,10 @@ class Mapper(object): sql.ClauseElement): value_params[col] = history.added[0] else: - params[col.key] = \ - prop.get_col_value(col, - history.added[0]) + value = history.added[0] + if prop.get_col_value: + value = prop.get_col_value(col, value) + params[col.key] = value if col in pks: if history.deleted: @@ -1722,15 +1728,17 @@ class Mapper(object): if ("pk_cascaded", state, col) in \ uowtransaction.\ attributes: - params[col._label] = \ - prop.get_col_value(col, - history.added[0]) + value = history.added[0] + if prop.get_col_value: + value = prop.get_col_value(col, value) + params[col._label] = value else: # use the old value to # locate the row - params[col._label] = \ - prop.get_col_value(col, - history.deleted[0]) + value = history.deleted[0] + if prop.get_col_value: + value = prop.get_col_value(col, value) + params[col._label] = value hasdata = True else: # row switch logic can reach us here @@ -1739,16 +1747,17 @@ class Mapper(object): # attempt to include the pk in the # update statement del params[col.key] - params[col._label] = \ - prop.get_col_value(col, - history.added[0]) + value = history.added[0] + if prop.get_col_value: + value = prop.get_col_value(col, value) + params[col._label] = value else: hasdata = True elif col in pks: - params[col._label] = \ - mapper._get_state_attr_by_column( - state, - state_dict, col) + value = state.manager[prop.key].impl.get(state, state_dict) + if prop.get_col_value: + value = prop.get_col_value(col, value) + params[col._label] = value if hasdata: update.append((state, state_dict, params, mapper, connection, value_params)) @@ -1840,13 +1849,12 @@ class Mapper(object): for state, state_dict, mapper, connection, has_identity, \ instance_key, row_switch in tups: - # expire readonly attributes - readonly = state.unmodified.intersection( - p.key for p in mapper._readonly_props - ) - - if readonly: - sessionlib._expire_state(state, state.dict, readonly) + if mapper._readonly_props: + readonly = state.unmodified_intersection( + [p.key for p in mapper._readonly_props] + ) + if readonly: + sessionlib._expire_state(state, state.dict, readonly) # if eager_defaults option is enabled, # refresh whatever has been expired. diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 953974af37..c2f88f9ed0 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -119,9 +119,6 @@ class ColumnProperty(StrategizedProperty): active_history=self.active_history, *self.columns) - def _getattr(self, state, dict_, column, passive=False): - return state.get_impl(self.key).get(state, dict_, passive=passive) - def _getcommitted(self, state, dict_, column, passive=False): return state.get_impl(self.key).\ get_committed_value(state, dict_, passive=passive) @@ -143,9 +140,6 @@ class ColumnProperty(StrategizedProperty): if dest_state.has_identity and self.key not in dest_dict: dest_state.expire_attributes(dest_dict, [self.key]) - def get_col_value(self, column, value): - return value - class Comparator(PropComparator): @util.memoized_instancemethod def __clause_element__(self): @@ -195,10 +189,6 @@ class CompositeProperty(ColumnProperty): # which issues assertions that do not apply to CompositeColumnProperty super(ColumnProperty, self).do_init() - def _getattr(self, state, dict_, column, passive=False): - obj = state.get_impl(self.key).get(state, dict_, passive=passive) - return self.get_col_value(column, obj) - def _getcommitted(self, state, dict_, column, passive=False): # TODO: no coverage here obj = state.get_impl(self.key).\ diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 668c04f508..3984c0e0c4 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -118,7 +118,7 @@ class InstanceState(object): return self.manager.get_impl(key).get_history(self, self.dict, **kwargs) def get_impl(self, key): - return self.manager.get_impl(key) + return self.manager[key].impl def get_pending(self, key): if key not in self.pending: @@ -298,6 +298,13 @@ class InstanceState(object): return set(self.manager).difference(self.committed_state) + def unmodified_intersection(self, keys): + """Return self.unmodified.intersection(keys).""" + + return set(keys).intersection(self.manager).\ + difference(self.committed_state) + + @property def unloaded(self): """Return the set of keys which do not have a loaded value. @@ -457,7 +464,19 @@ class MutableAttrInstanceState(InstanceState): if (key not in self.committed_state or (key in self.manager.mutable_attributes and not self.manager[key].impl.check_mutable_modified(self, dict_)))]) + + def unmodified_intersection(self, keys): + """Return self.unmodified.intersection(keys).""" + dict_ = self.dict + + return set([ + key for key in keys + if (key not in self.committed_state or + (key in self.manager.mutable_attributes and + not self.manager[key].impl.check_mutable_modified(self, dict_)))]) + + def _is_really_none(self): """do a check modified/resurrect. diff --git a/test/aaa_profiling/test_zoomark_orm.py b/test/aaa_profiling/test_zoomark_orm.py index 11285f9725..6efb8be786 100644 --- a/test/aaa_profiling/test_zoomark_orm.py +++ b/test/aaa_profiling/test_zoomark_orm.py @@ -335,11 +335,11 @@ class ZooMarkTest(TestBase): def test_profile_1_create_tables(self): self.test_baseline_1_create_tables() - @profiling.function_call_count(8469) + @profiling.function_call_count(7321) def test_profile_1a_populate(self): self.test_baseline_1a_populate() - @profiling.function_call_count(591) + @profiling.function_call_count(507) def test_profile_2_insert(self): self.test_baseline_2_insert() diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index 57c40ccc12..0954349997 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -1326,18 +1326,24 @@ class OptimizedLoadTest(_base.MappedTest): "INSERT INTO base (data, type) VALUES (:data, :type)", [{'data':'s1','type':'sub'}] ), - CompiledSQL( - "SELECT sub.counter AS sub_counter, base.counter AS " - "base_counter FROM base JOIN sub ON base.id = " - "sub.id WHERE base.id = :param_1", - lambda ctx:{'param_1':s1.id} - ), CompiledSQL( "INSERT INTO sub (id, sub) VALUES (:id, :sub)", lambda ctx:{'id':s1.id, 'sub':None} ), ) - + def go(): + eq_( s1.counter2, 1 ) + self.assert_sql_execution( + testing.db, + go, + CompiledSQL( + "SELECT sub.counter AS sub_counter, base.counter AS base_counter, " + "sub.counter2 AS sub_counter2 FROM base JOIN sub ON " + "base.id = sub.id WHERE base.id = :param_1", + lambda ctx:{u'param_1': s1.id} + ), + ) + @testing.resolve_artifact_names def test_dont_generate_on_none(self): class Base(_base.ComparableEntity): @@ -1393,19 +1399,26 @@ class OptimizedLoadTest(_base.MappedTest): "(:id, :sub, :counter)", lambda ctx:[{'counter': 1, 'sub': None, 'id': s1.id}] ), - CompiledSQL( - "SELECT subsub.counter2 AS subsub_counter2, sub.counter2 AS " - "sub_counter2 FROM base JOIN sub ON base.id = sub.id JOIN " - "subsub ON sub.id = subsub.id WHERE base.id = :param_1", - lambda ctx:{u'param_1': s1.id} - ), CompiledSQL( "INSERT INTO subsub (id) VALUES (:id)", lambda ctx:{'id':s1.id} ), ) - - + + def go(): + eq_( + s1.counter2, 1 + ) + self.assert_sql_execution( + testing.db, + go, + CompiledSQL( + "SELECT subsub.counter2 AS subsub_counter2, " + "sub.counter2 AS sub_counter2 FROM subsub, sub " + "WHERE :param_1 = sub.id AND sub.id = subsub.id", + lambda ctx:{u'param_1': s1.id} + ), + ) class PKDiscriminatorTest(_base.MappedTest): @classmethod