From: Mark Hahnenberg Date: Tue, 12 Jul 2016 18:07:52 +0000 (-0400) Subject: Fix issue with unbaking subqueries X-Git-Tag: rel_1_1_0b3~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bfa57063d49bcf355b00caaeb5e58d0b9d23acb3;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Fix issue with unbaking subqueries Fix improper capture of a loop variable inside a lambda during unbaking of subquery eager loaders, which would cause the incorrect query to be invoked. Fixes: #3743 Change-Id: I995110deb8ee2dae8540486729e1ae64578d28fc Pull-request: https://github.com/zzzeek/sqlalchemy/pull/290 --- diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index f75956bcf3..c977c7366d 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -18,6 +18,15 @@ .. changelog:: :version: 1.0.15 + .. change:: + :tags: bug, ext + :tickets: 3743 + + Fixed bug in ``sqlalchemy.ext.baked`` where the unbaking of a + subquery eager loader query would fail due to a variable scoping + issue, when multiple subquery loaders were involved. Pull request + courtesy Mark Hahnenberg. + .. changelog:: :version: 1.0.14 :released: July 6, 2016 diff --git a/lib/sqlalchemy/ext/baked.py b/lib/sqlalchemy/ext/baked.py index bfdc1e1a0f..3ca94925eb 100644 --- a/lib/sqlalchemy/ext/baked.py +++ b/lib/sqlalchemy/ext/baked.py @@ -194,7 +194,8 @@ class BakedQuery(object): """ for k, cache_key, query in context.attributes["baked_queries"]: - bk = BakedQuery(self._bakery, lambda sess: query.with_session(sess)) + bk = BakedQuery(self._bakery, + lambda sess, q=query: q.with_session(sess)) bk._cache_key = cache_key context.attributes[k] = bk.for_session(session).params(**params) diff --git a/test/ext/test_baked.py b/test/ext/test_baked.py index 8bfa58403e..4250e363b1 100644 --- a/test/ext/test_baked.py +++ b/test/ext/test_baked.py @@ -305,12 +305,16 @@ class ResultTest(BakedTest): def setup_mappers(cls): User = cls.classes.User Address = cls.classes.Address + Order = cls.classes.Order mapper(User, cls.tables.users, properties={ "addresses": relationship( - Address, order_by=cls.tables.addresses.c.id) + Address, order_by=cls.tables.addresses.c.id), + "orders": relationship( + Order, order_by=cls.tables.orders.c.id) }) mapper(Address, cls.tables.addresses) + mapper(Order, cls.tables.orders) def test_cachekeys_on_constructor(self): User = self.classes.User @@ -551,24 +555,29 @@ class ResultTest(BakedTest): def test_subquery_eagerloading(self): User = self.classes.User Address = self.classes.Address + Order = self.classes.Order - base_bq = self.bakery( - lambda s: s.query(User)) + # Override the default bakery for one with a smaller size. This used to + # trigger a bug when unbaking subqueries. + self.bakery = baked.bakery(size=3) + base_bq = self.bakery(lambda s: s.query(User)) - base_bq += lambda q: q.options(subqueryload(User.addresses)) + base_bq += lambda q: q.options(subqueryload(User.addresses), + subqueryload(User.orders)) base_bq += lambda q: q.order_by(User.id) assert_result = [ - User(id=7, addresses=[ - Address(id=1, email_address='jack@bean.com')]), + User(id=7, + addresses=[Address(id=1, email_address='jack@bean.com')], + orders=[Order(id=1), Order(id=3), Order(id=5)]), User(id=8, addresses=[ Address(id=2, email_address='ed@wood.com'), Address(id=3, email_address='ed@bettyboop.com'), Address(id=4, email_address='ed@lala.com'), ]), - User(id=9, addresses=[ - Address(id=5) - ]), + User(id=9, + addresses=[Address(id=5)], + orders=[Order(id=2), Order(id=4)]), User(id=10, addresses=[]) ] @@ -603,18 +612,18 @@ class ResultTest(BakedTest): def go(): result = bq(sess).all() eq_(assert_result[1:2], result) - self.assert_sql_count(testing.db, go, 2) + self.assert_sql_count(testing.db, go, 3) else: if cond1: def go(): result = bq(sess).all() eq_(assert_result[0:1], result) - self.assert_sql_count(testing.db, go, 2) + self.assert_sql_count(testing.db, go, 3) else: def go(): result = bq(sess).all() eq_(assert_result[1:3], result) - self.assert_sql_count(testing.db, go, 2) + self.assert_sql_count(testing.db, go, 3) sess.close()