diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2009-05-17 21:51:40 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2009-05-17 21:51:40 +0000 |
commit | 155466aad1c5ae4b43ed167a8b6e6013f0241370 (patch) | |
tree | 7c237401dd7f0ee68097bb3d0474dd9c33b80500 /lib/sqlalchemy/orm/session.py | |
parent | 2be867ffac8881a4a20ca5387063ed207ac876dc (diff) | |
download | sqlalchemy-155466aad1c5ae4b43ed167a8b6e6013f0241370.tar.gz |
- Removed all* O(N) scanning behavior from the flush() process,
i.e. operations that were scanning the full session,
including an extremely expensive one that was erroneously
assuming primary key values were changing when this
was not the case.
* one edge case remains which may invoke a full scan,
if an existing primary key attribute is modified
to a new value.
Diffstat (limited to 'lib/sqlalchemy/orm/session.py')
-rw-r--r-- | lib/sqlalchemy/orm/session.py | 54 |
1 files changed, 31 insertions, 23 deletions
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 00a7d55e5..cbfb0c1d6 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -299,14 +299,14 @@ class SessionTransaction(object): self.session._expunge_state(s) for s in self.session.identity_map.all_states(): - _expire_state(s, None) + _expire_state(s, None, instance_dict=self.session.identity_map) def _remove_snapshot(self): assert self._is_transaction_boundary if not self.nested and self.session.expire_on_commit: for s in self.session.identity_map.all_states(): - _expire_state(s, None) + _expire_state(s, None, instance_dict=self.session.identity_map) def _connection_for_bind(self, bind): self._assert_is_active() @@ -900,7 +900,7 @@ class Session(object): def _finalize_loaded(self, states): for state, dict_ in states.items(): - state.commit_all(dict_) + state.commit_all(dict_, self.identity_map) def refresh(self, instance, attribute_names=None): """Refresh the attributes on the given instance. @@ -935,7 +935,7 @@ class Session(object): """Expires all persistent instances within this Session.""" for state in self.identity_map.all_states(): - _expire_state(state, None) + _expire_state(state, None, instance_dict=self.identity_map) def expire(self, instance, attribute_names=None): """Expire the attributes on an instance. @@ -956,14 +956,14 @@ class Session(object): raise exc.UnmappedInstanceError(instance) self._validate_persistent(state) if attribute_names: - _expire_state(state, attribute_names=attribute_names) + _expire_state(state, attribute_names=attribute_names, instance_dict=self.identity_map) else: # pre-fetch the full cascade since the expire is going to # remove associations cascaded = list(_cascade_state_iterator('refresh-expire', state)) - _expire_state(state, None) + _expire_state(state, None, instance_dict=self.identity_map) for (state, m, o) in cascaded: - _expire_state(state, None) + _expire_state(state, None, instance_dict=self.identity_map) def prune(self): """Remove unreferenced instances cached in the identity map. @@ -1022,8 +1022,8 @@ class Session(object): state.key = instance_key self.identity_map.replace(state) - state.commit_all(state.dict) - + state.commit_all(state.dict, self.identity_map) + # remove from new last, might be the last strong ref if state in self._new: if self._enable_transaction_accounting and self.transaction: @@ -1211,7 +1211,7 @@ class Session(object): prop.merge(self, instance, merged, dont_load, _recursive) if dont_load: - attributes.instance_state(merged).commit_all(attributes.instance_dict(merged)) # remove any history + attributes.instance_state(merged).commit_all(attributes.instance_dict(merged), self.identity_map) # remove any history if new_instance: merged_state._run_on_load(merged) @@ -1360,10 +1360,9 @@ class Session(object): not self._deleted and not self._new): return - dirty = self._dirty_states if not dirty and not self._deleted and not self._new: - self.identity_map.modified = False + self.identity_map._modified.clear() return flush_context = UOWTransaction(self) @@ -1389,15 +1388,19 @@ class Session(object): raise exc.UnmappedInstanceError(o) objset.add(state) else: - # or just everything - objset = set(self.identity_map.all_states()).union(new) + objset = None # store objects whose fate has been decided processed = set() # put all saves/updates into the flush context. detect top-level # orphans and throw them into deleted. - for state in new.union(dirty).intersection(objset).difference(deleted): + if objset: + proc = new.union(dirty).intersection(objset).difference(deleted) + else: + proc = new.union(dirty).difference(deleted) + + for state in proc: is_orphan = _state_mapper(state)._is_orphan(state) if is_orphan and not _state_has_identity(state): path = ", nor ".join( @@ -1413,7 +1416,11 @@ class Session(object): processed.add(state) # put all remaining deletes into the flush context. - for state in deleted.intersection(objset).difference(processed): + if objset: + proc = deleted.intersection(objset).difference(processed) + else: + proc = deleted.difference(processed) + for state in proc: flush_context.register_object(state, isdelete=True) if len(flush_context.tasks) == 0: @@ -1433,9 +1440,13 @@ class Session(object): flush_context.finalize_flush_changes() - if not objects: - self.identity_map.modified = False - + # useful assertions: + #if not objects: + # assert not self.identity_map._modified + #else: + # assert self.identity_map._modified == self.identity_map._modified.difference(objects) + #self.identity_map._modified.clear() + for ext in self.extensions: ext.after_flush_postexec(self, flush_context) @@ -1484,10 +1495,7 @@ class Session(object): those that were possibly deleted. """ - return util.IdentitySet( - [state - for state in self.identity_map.all_states() - if state.modified]) + return self.identity_map._dirty_states() @property def dirty(self): |