summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-11-29 11:42:55 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2015-11-29 11:42:55 -0500
commitb0308a7b3af91cc61fbe3347376024ad8b7be019 (patch)
tree9ea6e04086f85d8a9bd9855a9e7531b95db67368
parentc90f0a49f332867f6b337c79ddf192299788667f (diff)
downloadsqlalchemy-b0308a7b3af91cc61fbe3347376024ad8b7be019.tar.gz
- 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
-rw-r--r--doc/build/changelog/changelog_10.rst12
-rw-r--r--lib/sqlalchemy/ext/baked.py6
-rw-r--r--test/ext/test_baked.py24
3 files changed, 42 insertions, 0 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 7e289a526..8d6dcd2a9 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -19,6 +19,18 @@
: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
:pullreq: github:200
diff --git a/lib/sqlalchemy/ext/baked.py b/lib/sqlalchemy/ext/baked.py
index ee9f6f9bb..eb0c36805 100644
--- a/lib/sqlalchemy/ext/baked.py
+++ b/lib/sqlalchemy/ext/baked.py
@@ -354,6 +354,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 dcf333184..7004358fc 100644
--- a/test/ext/test_baked.py
+++ b/test/ext/test_baked.py
@@ -271,6 +271,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