diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-09-04 12:11:19 -0400 |
---|---|---|
committer | mike bayer <mike_mp@zzzcomputing.com> | 2017-09-04 19:37:34 -0400 |
commit | 1e21f9c4f8984b9d0c228e49cb3fa1b03c667c25 (patch) | |
tree | 4f3cb2811cdb2334061e6ec0d4eaf2537d75be1a | |
parent | 65680b2343ef421a62582e23e2b35293732933ad (diff) | |
download | sqlalchemy-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.rst | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/session.py | 42 |
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) |