- 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 b0308a7b3a)
This commit is contained in:
Mike Bayer
2015-11-29 11:42:55 -05:00
parent d2d60acdcb
commit 670fd506fe
3 changed files with 42 additions and 0 deletions
+12
View File
@@ -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
+6
View File
@@ -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([
+24
View File
@@ -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