From: Mike Bayer Date: Fri, 9 Apr 2010 22:04:18 +0000 (-0400) Subject: fix bug + add coverage to ensure unneeded SaveUpdateAll/DeleteAll plus extra X-Git-Tag: rel_0_6_0~31 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a91ea946c7ebd81243dd1413de9195ee2e385a30;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git fix bug + add coverage to ensure unneeded SaveUpdateAll/DeleteAll plus extra work doesn't occur during per-state usage --- diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index faff4c4aa8..bab3cb936b 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -117,7 +117,6 @@ class DependencyProcessor(object): # assertions to ensure this method isn't being # called unnecessarily. can comment these out when # code is stable - #assert ('has_flush_activity', self) in uow.attributes assert not self.post_update or not self._check_reverse(uow) diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 163adff141..1c1b93c472 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -80,8 +80,9 @@ class UOWTransaction(object): self.attributes = {} # dictionary of mappers to sets of - # DependencyProcessors which have that mapper - # as a parent. + # DependencyProcessors, which are also + # set to be part of the sorted flush actions, + # which have that mapper as a parent. self.deps = util.defaultdict(set) # dictionary of mappers to sets of InstanceState @@ -151,7 +152,6 @@ class UOWTransaction(object): def register_preprocessor(self, processor, fromparent): self.presort_actions.add(Preprocess(processor, fromparent)) - self.deps[processor.parent.base_mapper].add(processor) def register_object(self, state, isdelete=False, listonly=False): if not self.session._contains_state(state): @@ -248,8 +248,8 @@ class UOWTransaction(object): #sort = topological.sort(self.dependencies, postsort_actions) #print "--------------" #print self.dependencies - #print postsort_actions - #print "COUNT OF POSTSORT ACTIONS", len(postsort_actions) + print postsort_actions + print "COUNT OF POSTSORT ACTIONS", len(postsort_actions) # execute if self.cycles: @@ -360,6 +360,7 @@ class ProcessAll(IterateMappersMixin, PostSortRec): self.dependency_processor = dependency_processor self.delete = delete self.fromparent = fromparent + uow.deps[dependency_processor.parent.base_mapper].add(dependency_processor) def execute(self, uow): states = self._elements(uow) diff --git a/test/orm/test_unitofworkv2.py b/test/orm/test_unitofworkv2.py index 201b0ad94e..7fef87d33c 100644 --- a/test/orm/test_unitofworkv2.py +++ b/test/orm/test_unitofworkv2.py @@ -13,9 +13,7 @@ from test.orm._fixtures import keywords, addresses, Base, Keyword, \ order_items, Item, Order, Node, \ composite_pk_table, CompositePk -class UOWTest(_fixtures.FixtureTest, testing.AssertsExecutionResults): - run_inserts = None - +class AssertsUOW(object): def _assert_uow_size(self, session, expected @@ -31,6 +29,10 @@ class UOWTest(_fixtures.FixtureTest, testing.AssertsExecutionResults): postsort_actions = uow._generate_actions() print postsort_actions eq_(len(postsort_actions), expected, postsort_actions) + +class UOWTest(_fixtures.FixtureTest, testing.AssertsExecutionResults, AssertsUOW): + run_inserts = None + class RudimentaryFlushTest(UOWTest): @@ -223,7 +225,7 @@ class RudimentaryFlushTest(UOWTest): u1 = User(name='ed') sess.add(u1) self._assert_uow_size(sess, 2) - + def test_o2m_flush_size(self): mapper(User, users, properties={ 'addresses':relationship(Address), @@ -488,7 +490,50 @@ class SingleCycleTest(UOWTest): n1.children self._assert_uow_size(sess, 2) -class SingleCycleM2MTest(_base.MappedTest, testing.AssertsExecutionResults): +class SingleCyclePlusAttributeTest(_base.MappedTest, testing.AssertsExecutionResults, AssertsUOW): + @classmethod + def define_tables(cls, metadata): + Table('nodes', metadata, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('parent_id', Integer, ForeignKey('nodes.id')), + Column('data', String(30)) + ) + + Table('foobars', metadata, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + Column('parent_id', Integer, ForeignKey('nodes.id')), + ) + + @testing.resolve_artifact_names + def test_flush_size(self): + class Node(Base): + pass + class FooBar(Base): + pass + + mapper(Node, nodes, properties={ + 'children':relationship(Node), + 'foobars':relationship(FooBar) + }) + mapper(FooBar, foobars) + + sess = create_session() + n1 = Node(data='n1') + n2 = Node(data='n2') + n1.children.append(n2) + sess.add(n1) + # ensure "foobars" doesn't get yanked in here + self._assert_uow_size(sess, 3) + + n1.foobars.append(FooBar()) + # saveupdateall/deleteall for FooBar added here, + # plus processstate node.foobars + # currently the "all" procs stay in pairs + self._assert_uow_size(sess, 6) + + sess.flush() + +class SingleCycleM2MTest(_base.MappedTest, testing.AssertsExecutionResults, AssertsUOW): @classmethod def define_tables(cls, metadata): @@ -635,5 +680,4 @@ class SingleCycleM2MTest(_base.MappedTest, testing.AssertsExecutionResults): lambda ctx:[{'id': n2.id}, {'id': n3.id}] ), ) - - +