From: Mike Bayer Date: Mon, 15 Oct 2007 17:24:08 +0000 (+0000) Subject: - inline optimizations added to locate_dirty() which can greatly speed up X-Git-Tag: rel_0_4_0~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7fac6542c5b6501ef23e8b79d50cc748de573d81;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - inline optimizations added to locate_dirty() which can greatly speed up repeated calls to flush(), as occurs with autoflush=True [ticket:816] --- diff --git a/CHANGES b/CHANGES index cebf107c7d..b337d7a496 100644 --- 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 diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 4b64d52e31..8d035d5682 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -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. diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 39387b4cfc..7a443b3314 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -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: diff --git a/test/orm/session.py b/test/orm/session.py index 53afb7bbab..553a14c8ed 100644 --- a/test/orm/session.py +++ b/test/orm/session.py @@ -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):