]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- various cruft removal and optimizations to load process.
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 3 Sep 2007 18:18:09 +0000 (18:18 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 3 Sep 2007 18:18:09 +0000 (18:18 +0000)
removes about 15K method calls from masseagerload.py test.

lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/session.py
lib/sqlalchemy/orm/strategies.py
lib/sqlalchemy/orm/unitofwork.py

index ec180d74a879d4938ed4bc6b6e480b5eff707bab..c1933d3ffddd7272b57f042b9454cbf1e73d5a99 100644 (file)
@@ -258,20 +258,25 @@ class MapperProperty(object):
         
         callables are of the following form::
         
-            def execute(instance, row, **flags):
-                # process incoming instance and given row.
+            def new_execute(instance, row, **flags):
+                # process incoming instance and given row.  the instance is "new" and
+                # was just created upon receipt of this row.
                 # flags is a dictionary containing at least the following attributes:
                 #   isnew - indicates if the instance was newly created as a result of reading this row
                 #   instancekey - identity key of the instance
                 # optional attribute:
                 #   ispostselect - indicates if this row resulted from a 'post' select of additional tables/columns
+
+            def existing_execute(instance, row, **flags):
+                # process incoming instance and given row.  the instance is "existing" and
+                # was created based on a previous row.
                 
             def post_execute(instance, **flags):
                 # process instance after all result rows have been processed.  this
                 # function should be used to issue additional selections in order to
                 # eagerly load additional properties.
                 
-            return (execute, post_execute)
+            return (new_execute, existing_execute, post_execute)
             
         either tuple value can also be ``None`` in which case no function is called.
         
@@ -528,7 +533,7 @@ class SynonymProperty(MapperProperty):
         pass
 
     def create_row_processor(self, selectcontext, mapper, row):
-        return (None, None)
+        return (None, None, None)
 
     def do_init(self):
         if not self.proxy:
index 5f60d26e07918011e3bcea16237b2be1baa97a45..960282255500a4e870c1bfa78650d96c889169c0 100644 (file)
@@ -1396,7 +1396,7 @@ class Mapper(object):
         identitykey = self.identity_key_from_row(row)
         populate_existing = context.populate_existing or self.always_refresh
         if identitykey in context.session.identity_map:
-            instance = context.session._get(identitykey)
+            instance = context.session.identity_map[identitykey]
             if self.__should_log_debug:
                 self.__log_debug("_instance(): using existing instance %s identity %s" % (mapperutil.instance_str(instance), str(identitykey)))
             isnew = False
@@ -1454,6 +1454,9 @@ class Mapper(object):
         if extension.append_result(self, context, row, instance, result, **flags) is EXT_CONTINUE:
             if result is not None:
                 result.append(instance)
+                
+        instance._instance_key = identitykey
+        
         return instance
 
     def _create_instance(self, session):
@@ -1495,7 +1498,7 @@ class Mapper(object):
                 newrow[c] = row[c2]
         return newrow
 
-    def populate_instance(self, selectcontext, instance, row, ispostselect=None, **flags):
+    def populate_instance(self, selectcontext, instance, row, ispostselect=None, isnew=False, **flags):
         """populate an instance from a result row."""
 
         selectcontext.stack.push_mapper(self)
@@ -1504,17 +1507,21 @@ class Mapper(object):
         # "snapshot" of the stack, which represents a path from the lead mapper in the query to this one,
         # including relation() names.  the key also includes "self", and allows us to distinguish between
         # other mappers within our inheritance hierarchy
-        populators = selectcontext.attributes.get(('instance_populators', self, selectcontext.stack.snapshot(), ispostselect), None)
+        snapshot = selectcontext.stack.snapshot()
+        populators = selectcontext.attributes.get(((isnew or ispostselect) and 'new_populators' or 'existing_populators', self, snapshot, ispostselect), None)
         if populators is None:
             # no populators; therefore this is the first time we are receiving a row for
             # this result set.  issue create_row_processor() on all MapperProperty objects
             # and cache in the select context.
-            populators = []
+            new_populators = []
+            existing_populators = []
             post_processors = []
             for prop in self.__props.values():
-                (pop, post_proc) = prop.create_row_processor(selectcontext, self, row)
-                if pop is not None:
-                    populators.append(pop)
+                (newpop, existingpop, post_proc) = prop.create_row_processor(selectcontext, self, row)
+                if newpop is not None:
+                    new_populators.append(newpop)
+                if existingpop is not None:
+                    existing_populators.append(existingpop)
                 if post_proc is not None:
                     post_processors.append(post_proc)
                     
@@ -1522,11 +1529,16 @@ class Mapper(object):
             if poly_select_loader is not None:
                 post_processors.append(poly_select_loader)
                 
-            selectcontext.attributes[('instance_populators', self, selectcontext.stack.snapshot(), ispostselect)] = populators
+            selectcontext.attributes[('new_populators', self, snapshot, ispostselect)] = new_populators
+            selectcontext.attributes[('existing_populators', self, snapshot, ispostselect)] = existing_populators
             selectcontext.attributes[('post_processors', self, ispostselect)] = post_processors
-
+            if isnew or ispostselect:
+                populators = new_populators
+            else:
+                populators = existing_populators
+                
         for p in populators:
-            p(instance, row, ispostselect=ispostselect, **flags)
+            p(instance, row, ispostselect=ispostselect, isnew=isnew, **flags)
         
         selectcontext.stack.pop()
             
@@ -1570,9 +1582,10 @@ class ClassKey(object):
     def __init__(self, class_, entity_name):
         self.class_ = class_
         self.entity_name = entity_name
-
+        self._hash = hash((self.class_, self.entity_name))
+        
     def __hash__(self):
-        return hash((self.class_, self.entity_name))
+        return self._hash
 
     def __eq__(self, other):
         return self is other
@@ -1615,7 +1628,7 @@ def object_mapper(object, entity_name=None, raiseerror=True):
             raise exceptions.InvalidRequestError("Class '%s' entity name '%s' has no mapper associated with it" % (object.__class__.__name__, getattr(object, '_entity_name', entity_name)))
         else:
             return None
-    return mapper.compile()
+    return mapper
 
 def class_mapper(class_, entity_name=None, compile=True):
     """Given a class and optional entity_name, return the primary Mapper associated with the key.
index 5cbe19ce2c3c23354215e5a128b61057ca1ff534..cb30eafcfe1652734e2700406824ad1c207270b0 100644 (file)
@@ -698,7 +698,7 @@ class Query(object):
         lockmode = lockmode or self._lockmode
         if not reload and not self.mapper.always_refresh and lockmode is None:
             try:
-                return self.session._get(key)
+                return self.session.identity_map[key]
             except KeyError:
                 pass
 
index c6922b928bb3e07bf5d0d0dc6af946059338cab8..ebd4bd3d312e46d1575d3518e54a730c912c2868 100644 (file)
@@ -933,14 +933,14 @@ class Session(object):
         return object_session(obj)
     object_session = classmethod(object_session)
     
-    def _save_impl(self, object, **kwargs):
-        if hasattr(object, '_instance_key'):
-            if object._instance_key not in self.identity_map:
+    def _save_impl(self, obj, **kwargs):
+        if hasattr(obj, '_instance_key'):
+            if obj._instance_key not in self.identity_map:
                 raise exceptions.InvalidRequestError("Instance '%s' is a detached instance "
                                                      "or is already persistent in a "
-                                                     "different Session" % repr(object))
+                                                     "different Session" % repr(obj))
         else:
-            m = _class_mapper(object.__class__, entity_name=kwargs.get('entity_name', None))
+            m = _class_mapper(obj.__class__, entity_name=kwargs.get('entity_name', None))
 
             # this would be a nice exception to raise...however this is incompatible with a contextual
             # session which puts all objects into the session upon construction.
@@ -949,31 +949,23 @@ class Session(object):
             #                                         "and must be attached to a parent "
             #                                         "object to be saved" % (repr(object)))
 
-            m._assign_entity_name(object)
-            self._register_pending(object)
+            m._assign_entity_name(obj)
+            self._attach(obj)
+            self.uow.register_new(obj)
 
-    def _update_impl(self, object, **kwargs):
-        if self._is_attached(object) and object not in self.deleted:
+    def _update_impl(self, obj, **kwargs):
+        if self._is_attached(obj) and obj not in self.deleted:
             return
-        if not hasattr(object, '_instance_key'):
-            raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % repr(object))
-        self._attach(object)
-
-    def _register_pending(self, obj):
+        if not hasattr(obj, '_instance_key'):
+            raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % repr(obj))
         self._attach(obj)
-        self.uow.register_new(obj)
 
     def _register_persistent(self, obj):
-        self._attach(obj)
-        self.uow.register_clean(obj)
-
-    def _register_deleted(self, obj):
-        self._attach(obj)
-        self.uow.register_deleted(obj)
+        obj._sa_session_id = self.hash_key
+        self.identity_map[obj._instance_key] = obj
+        attribute_manager.commit(obj)
 
     def _attach(self, obj):
-        """Attach the given object to this ``Session``."""
-
         old_id = getattr(obj, '_sa_session_id', None)
         if old_id != self.hash_key:
             if old_id is not None and old_id in _sessions:
@@ -1024,9 +1016,6 @@ class Session(object):
         
         return iter(list(self.uow.new) + self.uow.identity_map.values())
 
-    def _get(self, key):
-        return self.identity_map[key]
-
     dirty = property(lambda s:s.uow.locate_dirty(),
                      doc="A ``Set`` of all objects marked as 'dirty' within this ``Session``")
 
@@ -1036,11 +1025,6 @@ class Session(object):
     new = property(lambda s:s.uow.new,
                    doc="A ``Set`` of all objects marked as 'new' within this ``Session``.")
 
-    def import_instance(self, *args, **kwargs):
-        """A synynom for ``merge()``."""
-
-        return self.merge(*args, **kwargs)
-    import_instance = util.deprecated(import_instance)
 
 # this is the AttributeManager instance used to provide attribute behavior on objects.
 # to all the "global variable police" out there:  its a stateless object.
index bdb17e1d64da72d2561c074d295c3fdcf6f22662..86be77ae0ef5a1d6c41faf1d6173f6b2d73c6116 100644 (file)
@@ -62,22 +62,22 @@ class ColumnLoader(LoaderStrategy):
                 if c not in row:
                     break
             else:
-                def execute(instance, row, isnew, ispostselect=None, **flags):
-                    if isnew or ispostselect:
-                        if self._should_log_debug:
-                            self.logger.debug("populating %s with %s/%s..." % (mapperutil.attribute_str(instance, self.key), row.__class__.__name__, self.columns[0].key))
-                        instance.__dict__[self.key] = self.parent_property.composite_class(*[row[c] for c in self.columns])
-                self.logger.debug("Returning active composite column fetcher for %s %s" % (mapper, self.key))
-                return (execute, None)
+                def new_execute(instance, row, **flags):
+                    if self._should_log_debug:
+                        self.logger.debug("populating %s with %s/%s..." % (mapperutil.attribute_str(instance, self.key), row.__class__.__name__, self.columns[0].key))
+                    instance.__dict__[self.key] = self.parent_property.composite_class(*[row[c] for c in self.columns])
+                if self._should_log_debug:
+                    self.logger.debug("Returning active composite column fetcher for %s %s" % (mapper, self.key))
+                return (new_execute, None, None)
                 
         elif self.columns[0] in row:
-            def execute(instance, row, isnew, ispostselect=None, **flags):
-                if isnew or ispostselect:
-                    if self._should_log_debug:
-                        self.logger.debug("populating %s with %s/%s" % (mapperutil.attribute_str(instance, self.key), row.__class__.__name__, self.columns[0].key))
-                    instance.__dict__[self.key] = row[self.columns[0]]
-            self.logger.debug("Returning active column fetcher for %s %s" % (mapper, self.key))
-            return (execute, None)
+            def new_execute(instance, row, **flags):
+                if self._should_log_debug:
+                    self.logger.debug("populating %s with %s/%s" % (mapperutil.attribute_str(instance, self.key), row.__class__.__name__, self.columns[0].key))
+                instance.__dict__[self.key] = row[self.columns[0]]
+            if self._should_log_debug:
+                self.logger.debug("Returning active column fetcher for %s %s" % (mapper, self.key))
+            return (new_execute, None, None)
 
         # our mapped column is not present in the row.  check if we need to initialize a polymorphic
         # row fetcher used by inheritance.
@@ -87,15 +87,17 @@ class ColumnLoader(LoaderStrategy):
         
         if hosted_mapper.polymorphic_fetch == 'deferred':
             # 'deferred' polymorphic row fetcher, put a callable on the property.
-            def execute(instance, row, isnew, **flags):
+            def new_execute(instance, row, isnew, **flags):
                 if isnew:
                     sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=self._get_deferred_inheritance_loader(instance, mapper, needs_tables))
-            self.logger.debug("Returning deferred column fetcher for %s %s" % (mapper, self.key))
-            return (execute, None)
+            if self._should_log_debug:
+                self.logger.debug("Returning deferred column fetcher for %s %s" % (mapper, self.key))
+            return (new_execute, None, None)
         else:  
             # immediate polymorphic row fetcher.  no processing needed for this row.
-            self.logger.debug("Returning no column fetcher for %s %s" % (mapper, self.key))
-            return (None, None)
+            if self._should_log_debug:
+                self.logger.debug("Returning no column fetcher for %s %s" % (mapper, self.key))
+            return (None, None, None)
 
     def _get_deferred_inheritance_loader(self, instance, mapper, needs_tables):
         def create_statement():
@@ -125,19 +127,17 @@ class DeferredColumnLoader(LoaderStrategy):
         if self.group is not None and selectcontext.attributes.get(('undefer', self.group), False):
             return self.parent_property._get_strategy(ColumnLoader).create_row_processor(selectcontext, mapper, row)
         elif not self.is_class_level or len(selectcontext.options):
-            def execute(instance, row, isnew, **flags):
-                if isnew:
-                    if self._should_log_debug:
-                        self.logger.debug("set deferred callable on %s" % mapperutil.attribute_str(instance, self.key))
-                    sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=self.setup_loader(instance))
-            return (execute, None)
+            def new_execute(instance, row, **flags):
+                if self._should_log_debug:
+                    self.logger.debug("set deferred callable on %s" % mapperutil.attribute_str(instance, self.key))
+                sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=self.setup_loader(instance))
+            return (new_execute, None, None)
         else:
-            def execute(instance, row, isnew, **flags):
-                if isnew:
-                    if self._should_log_debug:
-                        self.logger.debug("set deferred callable on %s" % mapperutil.attribute_str(instance, self.key))
-                    sessionlib.attribute_manager.reset_instance_attribute(instance, self.key)
-            return (execute, None)
+            def new_execute(instance, row, **flags):
+                if self._should_log_debug:
+                    self.logger.debug("set deferred callable on %s" % mapperutil.attribute_str(instance, self.key))
+                sessionlib.attribute_manager.reset_instance_attribute(instance, self.key)
+            return (new_execute, None, None)
 
     def init(self):
         super(DeferredColumnLoader, self).init()
@@ -250,7 +250,7 @@ class DynaLoader(AbstractRelationLoader):
         self._register_attribute(self.parent.class_, dynamic=True, target_mapper=self.parent_property.mapper)
 
     def create_row_processor(self, selectcontext, mapper, row):
-        return (None, None)
+        return (None, None, None)
 
 DynaLoader.logger = logging.class_logger(DynaLoader)
         
@@ -260,12 +260,12 @@ class NoLoader(AbstractRelationLoader):
         self._register_attribute(self.parent.class_)
 
     def create_row_processor(self, selectcontext, mapper, row):
-        def execute(instance, row, isnew, **flags):
-            if isnew:
+        def new_execute(instance, row, ispostselect, **flags):
+            if not ispostselect:
                 if self._should_log_debug:
                     self.logger.debug("initializing blank scalar/collection on %s" % mapperutil.attribute_str(instance, self.key))
                 self._init_instance_attribute(instance)
-        return (execute, None)
+        return (new_execute, None, None)
 
 NoLoader.logger = logging.class_logger(NoLoader)
         
@@ -314,7 +314,8 @@ class LazyLoader(AbstractRelationLoader):
                 return prop._get_strategy(LazyLoader).setup_loader(instance)
 
         def lazyload():
-            self.logger.debug("lazy load attribute %s on instance %s" % (self.key, mapperutil.instance_str(instance)))
+            if self._should_log_debug:
+                self.logger.debug("lazy load attribute %s on instance %s" % (self.key, mapperutil.instance_str(instance)))
 
             if not mapper.has_identity(instance):
                 return None
@@ -362,17 +363,17 @@ class LazyLoader(AbstractRelationLoader):
 
     def create_row_processor(self, selectcontext, mapper, row):
         if not self.is_class_level or len(selectcontext.options):
-            def execute(instance, row, isnew, **flags):
-                if isnew:
+            def new_execute(instance, row, ispostselect, **flags):
+                if not ispostselect:
                     if self._should_log_debug:
                         self.logger.debug("set instance-level lazy loader on %s" % mapperutil.attribute_str(instance, self.key))
                     # we are not the primary manager for this attribute on this class - set up a per-instance lazyloader,
                     # which will override the class-level behavior
                     self._init_instance_attribute(instance, callable_=self.setup_loader(instance, selectcontext.options))
-            return (execute, None)
+            return (new_execute, None, None)
         else:
-            def execute(instance, row, isnew, **flags):
-                if isnew:
+            def new_execute(instance, row, ispostselect, **flags):
+                if not ispostselect:
                     if self._should_log_debug:
                         self.logger.debug("set class-level lazy loader on %s" % mapperutil.attribute_str(instance, self.key))
                     # we are the primary manager for this attribute on this class - reset its per-instance attribute state, 
@@ -380,7 +381,7 @@ class LazyLoader(AbstractRelationLoader):
                     # this usually is not needed unless the constructor of the object referenced the attribute before we got 
                     # to load data into it.
                     sessionlib.attribute_manager.reset_instance_attribute(instance, self.key)
-            return (execute, None)
+            return (new_execute, None, None)
 
     def _create_lazy_clause(cls, prop, reverse_direction=False):
         (primaryjoin, secondaryjoin, remote_side) = (prop.polymorphic_primaryjoin, prop.polymorphic_secondaryjoin, prop.remote_side)
@@ -591,7 +592,7 @@ class EagerLoader(AbstractRelationLoader):
                         # FIXME: instead of...
                         sessionlib.attribute_manager.get_attribute(instance, self.key).set_raw_value(instance, self.select_mapper._instance(selectcontext, decorated_row, None))
                         # bypass and set directly:
-                        #instance.__dict__[self.key] = ...
+                        #instance.__dict__[self.key] = self.select_mapper._instance(selectcontext, decorated_row, None)
                     else:
                         # call _instance on the row, even though the object has been created,
                         # so that we further descend into properties
@@ -614,9 +615,10 @@ class EagerLoader(AbstractRelationLoader):
                 selectcontext.stack.pop()
 
             selectcontext.stack.pop()
-            return (execute, None)
+            return (execute, execute, None)
         else:
-            self.logger.debug("eager loader %s degrading to lazy loader" % str(self))
+            if self._should_log_debug:
+                self.logger.debug("eager loader %s degrading to lazy loader" % str(self))
             selectcontext.stack.pop()
             return self.parent_property._get_strategy(LazyLoader).create_row_processor(selectcontext, mapper, row)
         
index 2f285cf462bdf7de8f1d205884c5c024e7dc9318..74ece20c3c1cf91ad0b32939174c6c26db1f92a6 100644 (file)
@@ -121,8 +121,9 @@ class UnitOfWork(object):
         else:
             return True
 
-    def register_clean(self, obj):
-        """register the given object as 'clean' (i.e. persistent) within this unit of work."""
+    def _register_clean(self, obj):
+        """register the given object as 'clean' (i.e. persistent) within this unit of work, after
+        a save operation has taken place."""
         
         if obj in self.new:
             self.new.remove(obj)
@@ -429,7 +430,7 @@ class UOWTransaction(object):
                 if elem.isdelete:
                     self.uow._remove_deleted(elem.obj)
                 else:
-                    self.uow.register_clean(elem.obj)
+                    self.uow._register_clean(elem.obj)
 
     def _sort_dependencies(self):
         """Create a hierarchical tree of dependent UOWTask instances.