From 9deea462a5188c2d9faa93d493abb4042493b5f9 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 8 Feb 2010 18:37:47 +0000 Subject: [PATCH] - Query called in the context of an expression will render disambiguating labels in all cases. Note that this does not apply to the existing .statement and .subquery() accessor/method, which still honors the .with_labels() setting that defaults to False. - Query.union() retains disambiguating labels within the returned statement, thus avoiding various SQL composition errors which can result from column name conflicts. [ticket:1676] --- CHANGES | 11 +++++++++++ lib/sqlalchemy/orm/query.py | 14 ++++++++++++-- test/orm/test_query.py | 33 ++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 5ecb3c4dd5..13c5322ff2 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,17 @@ CHANGES being detached from any Session. UnboundExecutionError is specific to engines bound to sessions and statements. + - Query called in the context of an expression will render + disambiguating labels in all cases. Note that this does + not apply to the existing .statement and .subquery() + accessor/method, which still honors the .with_labels() + setting that defaults to False. + + - Query.union() retains disambiguating labels within the + returned statement, thus avoiding various SQL composition + errors which can result from column name conflicts. + [ticket:1676] + - sql - Added math negation operator support, -x. diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index fff7fb9d92..ed55351514 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -355,7 +355,13 @@ class Query(object): @property def statement(self): - """The full SELECT statement represented by this Query.""" + """The full SELECT statement represented by this Query. + + The statement by default will not have disambiguating labels + applied to the construct unless with_labels(True) is called + first. + + """ return self._compile_context(labels=self._with_labels).\ statement._annotate({'_halt_adapt': True}) @@ -365,11 +371,15 @@ class Query(object): Eager JOIN generation within the query is disabled. + The statement by default will not have disambiguating labels + applied to the construct unless with_labels(True) is called + first. + """ return self.enable_eagerloads(False).statement.alias() def __clause_element__(self): - return self.enable_eagerloads(False).statement + return self.enable_eagerloads(False).with_labels().statement @_generative() def enable_eagerloads(self, value): diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 0d71b30257..c1201a14bb 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -586,9 +586,12 @@ class ExpressionTest(QueryTest, AssertsCompiledSQL): # this is actually not legal on most DBs since the subquery has no alias q1 = s.query(User).filter(User.name=='ed') + + self.assert_compile( select([q1]), - "SELECT id, name FROM (SELECT users.id AS id, users.name AS name FROM users WHERE users.name = :name_1)", + "SELECT users_id, users_name FROM (SELECT users.id AS users_id, " + "users.name AS users_name FROM users WHERE users.name = :name_1)", dialect=default.DefaultDialect() ) @@ -959,8 +962,27 @@ class SetOpsTest(QueryTest, AssertsCompiledSQL): [User(name='ed'), User(name='fred'), User(name='jack')] ) + def test_statement_labels(self): + """test that label conflicts don't occur with joins etc.""" + + s = create_session() + q1 = s.query(User, Address).join(User.addresses).\ + filter(Address.email_address=="ed@wood.com") + q2 = s.query(User, Address).join(User.addresses).\ + filter(Address.email_address=="jack@bean.com") + q3 = q1.union(q2).order_by(User.name) + + eq_( + q3.all(), + [ + (User(name='ed'), Address(email_address="ed@wood.com")), + (User(name='jack'), Address(email_address="jack@bean.com")), + ] + ) + def test_union_labels(self): - """test that column expressions translate during the _from_statement() portion of union(), others""" + """test that column expressions translate during + the _from_statement() portion of union(), others""" s = create_session() q1 = s.query(User, literal("x")) @@ -969,9 +991,10 @@ class SetOpsTest(QueryTest, AssertsCompiledSQL): self.assert_compile( q3, - "SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name, anon_1.anon_2 AS anon_1_anon_2 FROM " - "(SELECT users.id AS id, users.name AS name, :param_1 AS anon_2 FROM users " - "UNION SELECT users.id AS id, users.name AS name, 'y' FROM users) AS anon_1" + "SELECT anon_1.users_id AS anon_1_users_id, anon_1.users_name AS anon_1_users_name," + " anon_1.anon_2 AS anon_1_anon_2 FROM (SELECT users.id AS users_id, users.name AS" + " users_name, :param_1 AS anon_2 FROM users UNION SELECT users.id AS users_id, " + "users.name AS users_name, 'y' FROM users) AS anon_1" , use_default_dialect = True ) -- 2.47.3