]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
callcount destructo engage
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 10 Dec 2010 00:06:22 +0000 (19:06 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 10 Dec 2010 00:06:22 +0000 (19:06 -0500)
lib/sqlalchemy/event.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/state.py
test/aaa_profiling/test_zoomark_orm.py
test/orm/inheritance/test_basic.py

index ed33bb74c63d5051cd31034cd8951443ddbb7bb0..dce09220cab3f5c1b1c82db8c1fc760d7af87724 100644 (file)
@@ -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,
index 5686bdd334d05b897fa2d9a0a0668c8dabd4107f..90d105dc94bf893e263ebf7699dc7501c0cac539 100644 (file)
@@ -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.
 
index 5bd4e0f41ace2bf9c5963c790c4b970378cc33c1..54d6a53e270f0c6781fa250251136d22b405d0cf 100644 (file)
@@ -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.
index 953974af3713f99c3d1684b4af796089acb57eb1..c2f88f9ed0bf4e69d99a085627675aa46ac53a8c 100644 (file)
@@ -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).\
index 668c04f508d1f0c0d21c7f7c60f218f8da16fa01..3984c0e0c467b23825e88c76058797f44a3ffbf0 100644 (file)
@@ -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.
         
index 11285f9725ba6350a68fd52f95f19b3c06339245..6efb8be786e5b6f07e24998d8d01db8f87de3a30 100644 (file)
@@ -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()
 
index 57c40ccc12a71f6ab6f8692803a3c9be58cf7933..0954349997ec3042cad2914268c47e71925b39bc 100644 (file)
@@ -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