diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-11-05 04:22:30 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-11-05 04:22:30 -0500 |
commit | edec583b459e955a30d40b5c5d8baaed0a2ec1c6 (patch) | |
tree | fab94a50db294b0fd9ac8df493861f6505d533ce | |
parent | 7bf5ac9c1e814c999d4930941935e1d5cfd236bf (diff) | |
download | sqlalchemy-edec583b459e955a30d40b5c5d8baaed0a2ec1c6.tar.gz |
- Fixed bug regarding expression mutations which could express
itself as a "Could not locate column" error when using
:class:`.Query` to select from multiple, anonymous column
entities when querying against SQLite, as a side effect of the
"join rewriting" feature used by the SQLite dialect.
fixes #3241
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/elements.py | 7 | ||||
-rw-r--r-- | test/sql/test_generative.py | 13 | ||||
-rw-r--r-- | test/sql/test_join_rewriting.py | 25 |
4 files changed, 55 insertions, 1 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 6909da357..8ed2ea776 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -14,6 +14,17 @@ :version: 0.9.9 .. change:: + :tags: bug, orm, sqlite + :versions: 1.0.0 + :tickets: 3241 + + Fixed bug regarding expression mutations which could express + itself as a "Could not locate column" error when using + :class:`.Query` to select from multiple, anonymous column + entities when querying against SQLite, as a side effect of the + "join rewriting" feature used by the SQLite dialect. + + .. change:: :tags: feature, sqlite :versions: 1.0.0 diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 4d5bb9476..fa9b66024 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -861,6 +861,9 @@ class ColumnElement(operators.ColumnOperators, ClauseElement): expressions and function calls. """ + while self._is_clone_of is not None: + self = self._is_clone_of + return _anonymous_label( '%%(%d %s)s' % (id(self), getattr(self, 'name', 'anon')) ) @@ -2779,6 +2782,10 @@ class Grouping(ColumnElement): return self @property + def _key_label(self): + return self._label + + @property def _label(self): return getattr(self.element, '_label', None) or self.anon_label diff --git a/test/sql/test_generative.py b/test/sql/test_generative.py index 6044cecb0..6b86614e6 100644 --- a/test/sql/test_generative.py +++ b/test/sql/test_generative.py @@ -132,6 +132,19 @@ class TraversalTest(fixtures.TestBase, AssertsExecutionResults): assert struct == s2 assert struct.is_other(s2) + def test_clone_anon_label(self): + from sqlalchemy.sql.elements import Grouping + c1 = Grouping(literal_column('q')) + s1 = select([c1]) + + class Vis(CloningVisitor): + def visit_grouping(self, elem): + pass + + vis = Vis() + s2 = vis.traverse(s1) + eq_(list(s2.inner_columns)[0].anon_label, c1.anon_label) + def test_change_in_place(self): struct = B(A("expr1"), A("expr2"), B(A("expr1b"), A("expr2b")), A("expr3")) diff --git a/test/sql/test_join_rewriting.py b/test/sql/test_join_rewriting.py index c8b24e2f2..ced65d7f1 100644 --- a/test/sql/test_join_rewriting.py +++ b/test/sql/test_join_rewriting.py @@ -251,6 +251,16 @@ class _JoinRewriteTestBase(AssertsCompiledSQL): self._f_b1a_where_in_b2a ) + def test_anon_scalar_subqueries(self): + s1 = select([1]).as_scalar() + s2 = select([2]).as_scalar() + + s = select([s1, s2]).apply_labels() + self._test( + s, + self._anon_scalar_subqueries + ) + class JoinRewriteTest(_JoinRewriteTestBase, fixtures.TestBase): @@ -389,6 +399,10 @@ class JoinRewriteTest(_JoinRewriteTestBase, fixtures.TestBase): "FROM a JOIN b2 ON a.id = b2.a_id)" ) + _anon_scalar_subqueries = ( + "SELECT (SELECT 1) AS anon_1, (SELECT 2) AS anon_2" + ) + class JoinPlainTest(_JoinRewriteTestBase, fixtures.TestBase): @@ -497,6 +511,10 @@ class JoinPlainTest(_JoinRewriteTestBase, fixtures.TestBase): "FROM a JOIN b2 ON a.id = b2.a_id)" ) + _anon_scalar_subqueries = ( + "SELECT (SELECT 1) AS anon_1, (SELECT 2) AS anon_2" + ) + class JoinNoUseLabelsTest(_JoinRewriteTestBase, fixtures.TestBase): @@ -605,6 +623,10 @@ class JoinNoUseLabelsTest(_JoinRewriteTestBase, fixtures.TestBase): "FROM a JOIN b2 ON a.id = b2.a_id)" ) + _anon_scalar_subqueries = ( + "SELECT (SELECT 1) AS anon_1, (SELECT 2) AS anon_2" + ) + class JoinExecTest(_JoinRewriteTestBase, fixtures.TestBase): @@ -615,7 +637,8 @@ class JoinExecTest(_JoinRewriteTestBase, fixtures.TestBase): _a_bc = _a_bc_comma_a1_selbc = _a__b_dc = _a_bkeyassoc = \ _a_bkeyassoc_aliased = _a_atobalias_balias_c_w_exists = \ _a_atobalias_balias = _b_ab1_union_c_ab2 = \ - _b_a_id_double_overlap_annotated = _f_b1a_where_in_b2a = None + _b_a_id_double_overlap_annotated = _f_b1a_where_in_b2a = \ + _anon_scalar_subqueries = None @classmethod def setup_class(cls): |