summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-09-04 12:11:19 -0400
committermike bayer <mike_mp@zzzcomputing.com>2017-09-04 19:37:34 -0400
commit1e21f9c4f8984b9d0c228e49cb3fa1b03c667c25 (patch)
tree4f3cb2811cdb2334061e6ec0d4eaf2537d75be1a
parent65680b2343ef421a62582e23e2b35293732933ad (diff)
downloadsqlalchemy-1e21f9c4f8984b9d0c228e49cb3fa1b03c667c25.tar.gz
Guard against KeyError in session.merge after check for identity
Fixed bug in :meth:`.Session.merge` following along similar lines as that of :ticket:`4030`, where an internal check for a target object in the identity map could lead to an error if it were to be garbage collected immediately before the merge routine actually retrieves the object. Change-Id: Ifecfb8b9d50c52d0ebd5a03e1bd69fe3abf1dc40 Fixes: #4069
-rw-r--r--doc/build/changelog/unreleased_11/4069.rst9
-rw-r--r--lib/sqlalchemy/orm/session.py42
2 files changed, 33 insertions, 18 deletions
diff --git a/doc/build/changelog/unreleased_11/4069.rst b/doc/build/changelog/unreleased_11/4069.rst
new file mode 100644
index 000000000..f14ec9741
--- /dev/null
+++ b/doc/build/changelog/unreleased_11/4069.rst
@@ -0,0 +1,9 @@
+.. change::
+ :tags: bug, orm
+ :tickets: 4069
+ :versions: 1.2.0b3
+
+ Fixed bug in :meth:`.Session.merge` following along similar lines as that
+ of :ticket:`4030`, where an internal check for a target object in
+ the identity map could lead to an error if it were to be garbage collected
+ immediately before the merge routine actually retrieves the object. \ No newline at end of file
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 5488c3031..359370ab5 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -1920,28 +1920,34 @@ class Session(_SessionClassMethods):
key_is_persistent = True
if key in self.identity_map:
- merged = self.identity_map[key]
- elif key_is_persistent and key in _resolve_conflict_map:
- merged = _resolve_conflict_map[key]
-
- elif not load:
- if state.modified:
- raise sa_exc.InvalidRequestError(
- "merge() with load=False option does not support "
- "objects marked as 'dirty'. flush() all changes on "
- "mapped instances before merging with load=False.")
- merged = mapper.class_manager.new_instance()
- merged_state = attributes.instance_state(merged)
- merged_state.key = key
- self._update_impl(merged_state)
- new_instance = True
-
- elif key_is_persistent:
- merged = self.query(mapper.class_).get(key[1])
+ try:
+ merged = self.identity_map[key]
+ except KeyError:
+ # object was GC'ed right as we checked for it
+ merged = None
else:
merged = None
if merged is None:
+ if key_is_persistent and key in _resolve_conflict_map:
+ merged = _resolve_conflict_map[key]
+
+ elif not load:
+ if state.modified:
+ raise sa_exc.InvalidRequestError(
+ "merge() with load=False option does not support "
+ "objects marked as 'dirty'. flush() all changes on "
+ "mapped instances before merging with load=False.")
+ merged = mapper.class_manager.new_instance()
+ merged_state = attributes.instance_state(merged)
+ merged_state.key = key
+ self._update_impl(merged_state)
+ new_instance = True
+
+ elif key_is_persistent:
+ merged = self.query(mapper.class_).get(key[1])
+
+ if merged is None:
merged = mapper.class_manager.new_instance()
merged_state = attributes.instance_state(merged)
merged_dict = attributes.instance_dict(merged)