beginning to address cycles but its not worked out yet

This commit is contained in:
Mike Bayer
2010-03-31 17:31:34 -04:00
parent 15f4a6ef05
commit 89afcfdda4
3 changed files with 232 additions and 14 deletions
+69 -6
View File
@@ -191,10 +191,73 @@ class OneToManyDP(DependencyProcessor):
uow.dependencies.update([
(parent_saves, after_save),
(after_save, child_saves),
(child_deletes, before_delete),
(before_delete, parent_deletes)
(child_saves, parent_deletes),
(before_delete, child_saves),
(child_deletes, parent_deletes)
])
def per_saved_state_flush_actions(self, uow, state):
if True:
parent_saves = unitofwork.SaveUpdateAll(uow, self.parent)
child_saves = unitofwork.SaveUpdateAll(uow, self.mapper)
assert parent_saves in uow.cycles
assert child_saves in uow.cycles
added, updated, deleted = uow.get_attribute_history(state, self.key, passive=True)
if not added and not unchanged and not deleted:
return
save_parent = unitofwork.SaveUpdateState(state)
after_save = unitofwork.ProcessState(uow, self, False, state)
for child_state in added + unchanged + deleted:
if child_state is None:
continue
(deleted, listonly) = uow.states[child_state]
if deleted:
child_action = unitofwork.DeleteState(child_state)
else:
child_action = unitofwork.SaveUpdateState(child_state)
uow.dependencies.update([
(save_parent, after_save),
(after_save, child_action),
])
def per_deleted_state_flush_actions(self, uow, state):
if True:
parent_deletes = unitofwork.DeleteAll(uow, self.parent)
child_deletes = unitofwork.DeleteAll(uow, self.mapper)
assert parent_deletes in uow.cycles
assert child_deletes in uow.cycles
added, updated, deleted = uow.get_attribute_history(state, self.key, passive=True)
if not added and not unchanged and not deleted:
return
delete_parent = unitofwork.DeleteState(state)
after_delete = unitofwork.ProcessState(uow, self, True, state)
for child_state in added + unchanged + deleted:
if child_state is None:
continue
(deleted, listonly) = uow.states[child_state]
if deleted:
child_action = unitofwork.DeleteState(child_state)
else:
child_action = unitofwork.SaveUpdateState(child_state)
uow.dependencies.update([
(child_action, )
(save_parent, after_save),
(after_save, child_action),
])
def presort_deletes(self, uowcommit, states):
# head object is being deleted, and we manage its list of child objects
# the child objects have to have their foreign key to the parent set to NULL
@@ -318,12 +381,11 @@ class ManyToOneDP(DependencyProcessor):
else:
unitofwork.GetDependentObjects(uow, self, False, True)
unitofwork.GetDependentObjects(uow, self, True, True)
uow.dependencies.update([
(after_save, parent_saves),
(child_saves, after_save),
(parent_deletes, before_delete),
(before_delete, child_deletes)
(after_save, parent_saves),
(parent_saves, child_deletes)
])
def presort_deletes(self, uowcommit, states):
@@ -375,6 +437,7 @@ class ManyToOneDP(DependencyProcessor):
if history:
for child in history.added:
self._synchronize(state, child, None, False, uowcommit)
self._conditional_post_update(state, uowcommit, history.sum())
+27 -5
View File
@@ -166,16 +166,23 @@ class UOWTransaction(object):
if not ret:
break
self.cycles = cycles = topological.find_cycles(self.dependencies, self.postsort_actions.values())
assert not cycles
for rec in cycles:
rec.per_state_flush_actions(self)
for edge in list(self.dependencies):
# both nodes in this edge were part of a cycle.
# remove that from our deps as it has replaced
# itself with per-state actions
if cycles.issuperset(edge):
self.dependencies.remove(edge)
sort = topological.sort(self.dependencies, self.postsort_actions.values())
print sort
for rec in sort:
rec.execute(self)
# if cycles:
# break up actions into finer grained actions along those cycles
# for rec in topological.sort(self.dependencies, self.actions):
# rec.execute()
def finalize_flush_changes(self):
"""mark processed objects as clean / deleted after a successful flush().
@@ -276,6 +283,13 @@ class ProcessAll(PropertyRecMixin, PostSortRec):
else:
self.dependency_processor.process_saves(uow, states)
def per_state_flush_actions(self, uow):
for state in self._elements(uow):
if self.delete:
self.dependency_processor.per_deleted_state_flush_actions(uow, self.dependency_processor, state)
else:
self.dependency_processor.per_saved_state_flush_actions(uow, self.dependency_processor, state)
class SaveUpdateAll(PostSortRec):
def __init__(self, uow, mapper):
self.mapper = mapper
@@ -285,6 +299,10 @@ class SaveUpdateAll(PostSortRec):
uow.states_for_mapper_hierarchy(self.mapper, False, False),
uow
)
def per_state_flush_actions(self, uow):
for state in uow.states_for_mapper_hierarchy(self.mapper, False, False):
SaveUpdateState(uow, state)
class DeleteAll(PostSortRec):
def __init__(self, uow, mapper):
@@ -296,6 +314,10 @@ class DeleteAll(PostSortRec):
uow
)
def per_state_flush_actions(self, uow):
for state in uow.states_for_mapper_hierarchy(self.mapper, True, False):
DeleteState(uow, state)
class ProcessState(PostSortRec):
def __init__(self, uow, dependency_processor, delete, state):
self.dependency_processor = dependency_processor
+136 -3
View File
@@ -15,7 +15,7 @@ class UOWTest(_fixtures.FixtureTest, testing.AssertsExecutionResults):
class RudimentaryFlushTest(UOWTest):
def test_one_to_many(self):
def test_one_to_many_save(self):
mapper(User, users, properties={
'addresses':relationship(Address),
})
@@ -44,6 +44,17 @@ class RudimentaryFlushTest(UOWTest):
lambda ctx: {'email_address': 'a2', 'user_id':u1.id}
),
)
def test_one_to_many_delete_all(self):
mapper(User, users, properties={
'addresses':relationship(Address),
})
mapper(Address, addresses)
sess = create_session()
a1, a2 = Address(email_address='a1'), Address(email_address='a2')
u1 = User(name='u1', addresses=[a1, a2])
sess.add(u1)
sess.flush()
sess.delete(u1)
sess.delete(a1)
@@ -60,8 +71,37 @@ class RudimentaryFlushTest(UOWTest):
{'id':u1.id}
),
)
def test_one_to_many_delete_parent(self):
mapper(User, users, properties={
'addresses':relationship(Address),
})
mapper(Address, addresses)
sess = create_session()
a1, a2 = Address(email_address='a1'), Address(email_address='a2')
u1 = User(name='u1', addresses=[a1, a2])
sess.add(u1)
sess.flush()
sess.delete(u1)
self.assert_sql_execution(
testing.db,
sess.flush,
CompiledSQL(
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
[{u'addresses_id': 1, 'user_id': None}]
),
CompiledSQL(
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
[{u'addresses_id': 2, 'user_id': None}]
),
CompiledSQL(
"DELETE FROM users WHERE users.id = :id",
{'id':u1.id}
),
)
def test_many_to_one(self):
def test_many_to_one_save(self):
mapper(User, users)
mapper(Address, addresses, properties={
@@ -93,6 +133,19 @@ class RudimentaryFlushTest(UOWTest):
),
)
def test_many_to_one_delete_all(self):
mapper(User, users)
mapper(Address, addresses, properties={
'user':relationship(User)
})
sess = create_session()
u1 = User(name='u1')
a1, a2 = Address(email_address='a1', user=u1), \
Address(email_address='a2', user=u1)
sess.add_all([a1, a2])
sess.flush()
sess.delete(u1)
sess.delete(a1)
sess.delete(a2)
@@ -108,4 +161,84 @@ class RudimentaryFlushTest(UOWTest):
{'id':u1.id}
),
)
def test_many_to_one_delete_target(self):
mapper(User, users)
mapper(Address, addresses, properties={
'user':relationship(User)
})
sess = create_session()
u1 = User(name='u1')
a1, a2 = Address(email_address='a1', user=u1), \
Address(email_address='a2', user=u1)
sess.add_all([a1, a2])
sess.flush()
sess.delete(u1)
a1.user = a2.user = None
self.assert_sql_execution(
testing.db,
sess.flush,
CompiledSQL(
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
[{u'addresses_id': 1, 'user_id': None}]
),
CompiledSQL(
"UPDATE addresses SET user_id=:user_id WHERE addresses.id = :addresses_id",
[{u'addresses_id': 2, 'user_id': None}]
),
CompiledSQL(
"DELETE FROM users WHERE users.id = :id",
{'id':u1.id}
),
)
class SingleCycleTest(UOWTest):
def test_one_to_many(self):
mapper(Node, nodes, properties={
'children':relationship(Node)
})
sess = create_session()
n2, n3 = Node(data='n2'), Node(data='n3')
n1 = Node(data='n1', children=[n2, n3])
sess.add(n1)
self.assert_sql_execution(
testing.db,
sess.flush,
CompiledSQL(
"INSERT INTO nodes (data) VALUES (:data)",
{'data': 'n1'}
),
CompiledSQL(
"INSERT INTO addresses (user_id, email_address) "
"VALUES (:user_id, :email_address)",
lambda ctx: {'email_address': 'a1', 'user_id':u1.id}
),
CompiledSQL(
"INSERT INTO addresses (user_id, email_address) "
"VALUES (:user_id, :email_address)",
lambda ctx: {'email_address': 'a2', 'user_id':u1.id}
),
)
return
sess.delete(n1)
sess.delete(n2)
sess.delete(n3)
self.assert_sql_execution(
testing.db,
sess.flush,
CompiledSQL(
"DELETE FROM addresses WHERE addresses.id = :id",
[{'id':a1.id},{'id':a2.id}]
),
CompiledSQL(
"DELETE FROM users WHERE users.id = :id",
{'id':u1.id}
),
)