summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2016-03-14 17:56:57 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2016-03-14 17:56:57 -0400
commitaf92f6763d72fa853f2ac0968e077c24e88b0c93 (patch)
tree979bfa495212ce0b0720c86866cc451d6de06f44 /lib/sqlalchemy
parent6e5e64e27ef2c6a86c9aebdcefdf2cd726f1476a (diff)
downloadsqlalchemy-af92f6763d72fa853f2ac0968e077c24e88b0c93.tar.gz
- Fixed bug where a newly inserted instance that is rolled back
would still potentially cause persistence conflicts on the next transaction, because the instance would not be checked that it was expired. This fix will resolve a large class of cases that erronously cause the "New instance with identity X conflicts with persistent instance Y" error. fixes #3677
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/persistence.py34
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py13
2 files changed, 31 insertions, 16 deletions
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index b9a1f9df9..a5e0d9d95 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -295,22 +295,24 @@ def _organize_states_for_save(base_mapper, states, uowtransaction):
instance = \
uowtransaction.session.identity_map[instance_key]
existing = attributes.instance_state(instance)
- if not uowtransaction.is_deleted(existing):
- raise orm_exc.FlushError(
- "New instance %s with identity key %s conflicts "
- "with persistent instance %s" %
- (state_str(state), instance_key,
- state_str(existing)))
-
- base_mapper._log_debug(
- "detected row switch for identity %s. "
- "will update %s, remove %s from "
- "transaction", instance_key,
- state_str(state), state_str(existing))
-
- # remove the "delete" flag from the existing element
- uowtransaction.remove_state_actions(existing)
- row_switch = existing
+
+ if not uowtransaction.was_already_deleted(existing):
+ if not uowtransaction.is_deleted(existing):
+ raise orm_exc.FlushError(
+ "New instance %s with identity key %s conflicts "
+ "with persistent instance %s" %
+ (state_str(state), instance_key,
+ state_str(existing)))
+
+ base_mapper._log_debug(
+ "detected row switch for identity %s. "
+ "will update %s, remove %s from "
+ "transaction", instance_key,
+ state_str(state), state_str(existing))
+
+ # remove the "delete" flag from the existing element
+ uowtransaction.remove_state_actions(existing)
+ row_switch = existing
if (has_identity or row_switch) and mapper.version_id_col is not None:
update_version_id = mapper._get_committed_state_attr_by_column(
diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py
index 8b4ae64bb..f3e39d9b5 100644
--- a/lib/sqlalchemy/orm/unitofwork.py
+++ b/lib/sqlalchemy/orm/unitofwork.py
@@ -16,6 +16,7 @@ organizes them in order of dependency, and executes.
from .. import util, event
from ..util import topological
from . import attributes, persistence, util as orm_util
+from . import exc as orm_exc
import itertools
@@ -155,6 +156,18 @@ class UOWTransaction(object):
def has_work(self):
return bool(self.states)
+ def was_already_deleted(self, state):
+ """return true if the given state is expired and was deleted
+ previously.
+ """
+ if state.expired:
+ try:
+ state._load_expired(state, attributes.PASSIVE_OFF)
+ except orm_exc.ObjectDeletedError:
+ self.session._remove_newly_deleted([state])
+ return True
+ return False
+
def is_deleted(self, state):
"""return true if the given state is marked as deleted
within this uowtransaction."""