]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- inline optimizations added to locate_dirty() which can greatly speed up
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 15 Oct 2007 17:24:08 +0000 (17:24 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 15 Oct 2007 17:24:08 +0000 (17:24 +0000)
  repeated calls to flush(), as occurs with autoflush=True [ticket:816]

CHANGES
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/unitofwork.py
test/orm/session.py

diff --git a/CHANGES b/CHANGES
index cebf107c7d431cc949d89d071ea76d39825d61ff..b337d7a496f11825f9b454655bf8c95a243d3658 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,9 @@ CHANGES
   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
index 4b64d52e31ded655d2d56b781036045ad1f94d3f..8d035d568298d3c3b5be3e1f4c89bbf83b346a29 100644 (file)
@@ -106,6 +106,8 @@ class AttributeImpl(object):
         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
@@ -314,8 +316,7 @@ class ScalarAttributeImpl(AttributeImpl):
     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
@@ -876,13 +877,15 @@ class AttributeManager(object):
     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.
index 39387b4cfcd39172353e32ab3ca2a663544390e7..7a443b331427ba8516bc8c245b5f5e72a4e0b0cc 100644 (file)
@@ -156,7 +156,14 @@ class UnitOfWork(object):
         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."""
@@ -166,6 +173,12 @@ class UnitOfWork(object):
         # 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:
@@ -178,10 +191,7 @@ class UnitOfWork(object):
         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()
 
@@ -197,6 +207,9 @@ class UnitOfWork(object):
         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:
index 53afb7bbab91876d0bf9e42d66fae3668a915b45..553a14c8ed85f3f2d9b771f03639ca69b675938a 100644 (file)
@@ -660,9 +660,13 @@ class SessionTest(AssertMixin):
         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):