Commits

Mike Bayer  committed c4e4f4e

- inline optimizations added to locate_dirty() which can greatly speed up
repeated calls to flush(), as occurs with autoflush=True [ticket:816]

  • Participants
  • Parent commits a9f8d7d

Comments (0)

Files changed (4)

   collection doesn't contain the item, supports noload collections
   [ticket:813]
 
+- inline optimizations added to locate_dirty() which can greatly speed up
+  repeated calls to flush(), as occurs with autoflush=True [ticket:816]
+  
 - The IdentifierPreprarer's _requires_quotes test is now regex based.  Any
   out-of-tree dialects that provide custom sets of legal_characters or
   illegal_initial_characters will need to move to regexes or override

File lib/sqlalchemy/orm/attributes.py

         self.callable_ = callable_
         self.trackparent = trackparent
         self.mutable_scalars = mutable_scalars
+        if mutable_scalars:
+            class_._sa_has_mutable_scalars = True
         self.copy = None
         if compare_function is None:
             self.is_equal = lambda x,y: x == y
     def __init__(self, class_, manager, key, callable_, trackparent=False, extension=None, copy_function=None, compare_function=None, mutable_scalars=False, **kwargs):
         super(ScalarAttributeImpl, self).__init__(class_, manager, key,
           callable_, trackparent=trackparent, extension=extension,
-          compare_function=compare_function, **kwargs)
-        self.mutable_scalars = mutable_scalars
+          compare_function=compare_function, mutable_scalars=mutable_scalars, **kwargs)
 
         if copy_function is None:
             copy_function = self.__copy
     def _is_modified(self, state):
         if state.modified:
             return True
-        else:
+        elif getattr(state.class_, '_sa_has_mutable_scalars', False):
             for attr in self.managed_attributes(state.class_):
-                if attr.impl.check_mutable_modified(state):
+                if getattr(attr.impl, 'mutable_scalars', False) and attr.impl.check_mutable_modified(state):
                     return True
             else:
                 return False
-        
+        else:
+            return False
+            
     def get_history(self, obj, key, **kwargs):
         """Return a new ``AttributeHistory`` object for the given
         attribute on the given object.

File lib/sqlalchemy/orm/unitofwork.py

         either contain changes or are marked as deleted.
         """
         
-        return util.Set([x for x in self.identity_map.values() if x not in self.deleted and attribute_manager.is_modified(x)])
+        # a little bit of inlining for speed
+        return util.Set([x for x in self.identity_map.values() 
+            if x not in self.deleted 
+            and (
+                x._state.modified
+                or (getattr(x.__class__, '_sa_has_mutable_scalars', False) and attribute_manager._is_modified(x._state))
+            )
+            ])
 
     def flush(self, session, objects=None):
         """create a dependency tree of all pending SQL operations within this unit of work and execute."""
         # communication with the mappers and relationships to fire off SQL
         # and synchronize attributes between related objects.
 
+        # detect persistent objects that have changes
+        dirty = self.locate_dirty()
+
+        if len(dirty) == 0 and len(self.deleted) == 0 and len(self.new) == 0:
+            return
+
         flush_context = UOWTransaction(self, session)
 
         if session.extension is not None:
         else:
             # or just everything
             objset = util.Set(self.identity_map.values()).union(self.new)
-
-        # detect persistent objects that have changes
-        dirty = self.locate_dirty()
-
+            
         # store objects whose fate has been decided
         processed = util.Set()
 
         for obj in self.deleted.intersection(objset).difference(processed):
             flush_context.register_object(obj, isdelete=True)
 
+        if len(flush_context.tasks) == 0:
+            return
+            
         session.create_transaction(autoflush=False)
         flush_context.transaction = session.transaction
         try:

File test/orm/session.py

         assert log == ['before_flush', 'after_flush', 'after_flush_postexec']
 
         log = []
+        u.user_name = 'ed'
         sess.commit()
         assert log == ['before_commit', 'before_flush', 'after_flush', 'after_flush_postexec', 'after_commit']
-        
+
+        log = []
+        sess.commit()
+        assert log == ['before_commit', 'after_commit']
         
 class ScopedSessionTest(ORMTest):