]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Guard against KeyError in session.merge after check for identity
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 4 Sep 2017 16:11:19 +0000 (12:11 -0400)
committermike bayer <mike_mp@zzzcomputing.com>
Mon, 4 Sep 2017 23:36:51 +0000 (19:36 -0400)
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
(cherry picked from commit bfad032017fde2e519ad5eacc0011a71bf7a22a9)

doc/build/changelog/unreleased_11/4069.rst [new file with mode: 0644]
lib/sqlalchemy/orm/session.py

diff --git a/doc/build/changelog/unreleased_11/4069.rst b/doc/build/changelog/unreleased_11/4069.rst
new file mode 100644 (file)
index 0000000..dadae42
--- /dev/null
@@ -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
index ebf1f61f1d0512d5375531473ea03f86bdbbf4d7..2c040561b09b0dee3b8cd9edda5de6db6edb9a21 100644 (file)
@@ -1875,27 +1875,33 @@ 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)