From 670fd506fe4cf9071e8e44b4d4028f0bb6ebebb7 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 29 Nov 2015 11:42:55 -0500 Subject: [PATCH] - Fixed an issue in baked queries where the .get() method, used either directly or within lazy loads, didn't consider the mapper's "get clause" as part of the cache key, causing bound parameter mismatches if the clause got re-generated. This clause is cached by mappers on the fly but in highly concurrent scenarios may be generated more than once when first accessed. fixes #3597 (cherry picked from commit b0308a7b3af91cc61fbe3347376024ad8b7be019) --- doc/build/changelog/changelog_10.rst | 12 ++++++++++++ lib/sqlalchemy/ext/baked.py | 6 ++++++ test/ext/test_baked.py | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 7e289a526b..8d6dcd2a99 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -18,6 +18,18 @@ .. changelog:: :version: 1.0.10 + .. change:: + :tags: bug, ext + :versions: 1.1.0b1 + :tickets: 3597 + + Fixed an issue in baked queries where the .get() method, used either + directly or within lazy loads, didn't consider the mapper's "get clause" + as part of the cache key, causing bound parameter mismatches if the + clause got re-generated. This clause is cached by mappers + on the fly but in highly concurrent scenarios may be generated more + than once when first accessed. + .. change:: :tags: feature, sql :versions: 1.1.0b1 diff --git a/lib/sqlalchemy/ext/baked.py b/lib/sqlalchemy/ext/baked.py index d8c8843f6d..2d2d26c59b 100644 --- a/lib/sqlalchemy/ext/baked.py +++ b/lib/sqlalchemy/ext/baked.py @@ -355,6 +355,12 @@ class Result(object): # (remember, we can map to an OUTER JOIN) bq = self.bq + # add the clause we got from mapper._get_clause to the cache + # key so that if a race causes multiple calls to _get_clause, + # we've cached on ours + bq = bq._clone() + bq._cache_key += (_get_clause, ) + bq = bq.with_criteria(setup, tuple(elem is None for elem in ident)) params = dict([ diff --git a/test/ext/test_baked.py b/test/ext/test_baked.py index abfaf8af3c..b3e4f16529 100644 --- a/test/ext/test_baked.py +++ b/test/ext/test_baked.py @@ -270,6 +270,30 @@ class LikeQueryTest(BakedTest): eq_(u2.name, 'chuck') self.assert_sql_count(testing.db, go, 0) + def test_get_includes_getclause(self): + # test issue #3597 + User = self.classes.User + + bq = self.bakery(lambda s: s.query(User)) + + for i in range(5): + sess = Session() + u1 = bq(sess).get(7) + eq_(u1.name, 'jack') + + eq_(len(bq._bakery), 2) + + # simulate race where mapper._get_clause + # may be generated more than once + from sqlalchemy import inspect + del inspect(User).__dict__['_get_clause'] + + for i in range(5): + sess = Session() + u1 = bq(sess).get(7) + eq_(u1.name, 'jack') + eq_(len(bq._bakery), 4) + class ResultTest(BakedTest): __backend__ = True -- 2.47.3