summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/changelog_08.rst20
-rw-r--r--doc/build/changelog/migration_08.rst56
-rw-r--r--lib/sqlalchemy/sql/compiler.py9
-rw-r--r--lib/sqlalchemy/sql/expression.py43
-rw-r--r--test/orm/test_froms.py6
-rw-r--r--test/orm/test_query.py29
-rw-r--r--test/sql/test_compiler.py272
-rw-r--r--test/sql/test_generative.py293
8 files changed, 523 insertions, 205 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst
index c71f3be0c..8c34dab09 100644
--- a/doc/build/changelog/changelog_08.rst
+++ b/doc/build/changelog/changelog_08.rst
@@ -8,14 +8,30 @@
.. note::
- Be sure to *re-read* :doc:`migration_08` for this release.
There are some new behavioral changes as of 0.8.0
- not present in 0.8.0b2, including:
+ not present in 0.8.0b2. They are present in the
+ migration document as follows:
* :ref:`legacy_is_orphan_addition`
* :ref:`metadata_create_drop_tables`
+ * :ref:`correlation_context_specific`
+
+ .. change::
+ :tags: bug, sql
+ :tickets: 2668
+
+ The behavior of SELECT correlation has been improved such that
+ the :meth:`.Select.correlate` and :meth:`.Select.correlate_except`
+ methods, as well as their ORM analogues, will still retain
+ "auto-correlation" behavior in that the FROM clause is modified
+ only if the output would be legal SQL; that is, the FROM clause
+ is left intact if the correlated SELECT is not used in the context
+ of an enclosing SELECT inside of the WHERE, columns, or HAVING clause.
+ The two methods now only specify conditions to the default
+ "auto correlation", rather than absolute FROM lists.
+
.. change::
:tags: feature, mysql
:pullreq: 42
diff --git a/doc/build/changelog/migration_08.rst b/doc/build/changelog/migration_08.rst
index bb6d85e58..971dd2f51 100644
--- a/doc/build/changelog/migration_08.rst
+++ b/doc/build/changelog/migration_08.rst
@@ -7,9 +7,10 @@ What's New in SQLAlchemy 0.8?
This document describes changes between SQLAlchemy version 0.7,
undergoing maintenance releases as of October, 2012,
and SQLAlchemy version 0.8, which is expected for release
- in late 2012.
+ in early 2013.
Document date: October 25, 2012
+ Updated: March 9, 2013
Introduction
============
@@ -1145,6 +1146,59 @@ entity, ``query.correlate(someentity)``.
:ticket:`2179`
+.. _correlation_context_specific:
+
+Correlation is now always context-specific
+------------------------------------------
+
+To allow a wider variety of correlation scenarios, the behavior of
+:meth:`.Select.correlate` and :meth:`.Query.correlate` has changed slightly
+such that the SELECT statement will omit the "correlated" target from the
+FROM clause only if the statement is actually used in that context. Additionally,
+it's no longer possible for a SELECT statement that's placed as a FROM
+in an enclosing SELECT statement to "correlate" (i.e. omit) a FROM clause.
+
+This change only makes things better as far as rendering SQL, in that it's no
+longer possible to render illegal SQL where there are insufficient FROM
+objects relative to what's being selected::
+
+ from sqlalchemy.sql import table, column, select
+
+ t1 = table('t1', column('x'))
+ t2 = table('t2', column('y'))
+ s = select([t1, t2]).correlate(t1)
+
+ print(s)
+
+Prior to this change, the above would return::
+
+ SELECT t1.x, t2.y FROM t2
+
+which is invalid SQL as "t1" is not referred to in any FROM clause.
+
+Now, in the absense of an enclosing SELECT, it returns::
+
+ SELECT t1.x, t2.y FROM t1, t2
+
+Within a SELECT, the correlation takes effect as expected::
+
+ s2 = select([t1, t2]).where(t1.c.x == t2.c.y).where(t1.c.x == s)
+
+ print (s2)
+
+ SELECT t1.x, t2.y FROM t1, t2
+ WHERE t1.x = t2.y AND t1.x =
+ (SELECT t1.x, t2.y FROM t2)
+
+This change is not expected to impact any existing applications, as
+the correlation behavior remains identical for properly constructed
+expressions. Only an application that relies, most likely within a
+testing scenario, on the invalid string output of a correlated
+SELECT used in a non-correlating context would see any change.
+
+:ticket:`2668`
+
+
.. _metadata_create_drop_tables:
create_all() and drop_all() will now honor an empty list as such
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 59e46de12..90e906727 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -1086,14 +1086,9 @@ class SQLCompiler(engine.Compiled):
positional_names=None, **kwargs):
entry = self.stack and self.stack[-1] or {}
- if not asfrom:
- existingfroms = entry.get('from', None)
- else:
- # don't render correlations if we're rendering a FROM list
- # entry
- existingfroms = []
+ existingfroms = entry.get('from', None)
- froms = select._get_display_froms(existingfroms)
+ froms = select._get_display_froms(existingfroms, asfrom=asfrom)
correlate_froms = set(sql._from_objects(*froms))
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 490004e39..41eaace71 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -4980,7 +4980,7 @@ class CompoundSelect(SelectBase):
INTERSECT_ALL = util.symbol('INTERSECT ALL')
def __init__(self, keyword, *selects, **kwargs):
- self._should_correlate = kwargs.pop('correlate', False)
+ self._auto_correlate = kwargs.pop('correlate', False)
self.keyword = keyword
self.selects = []
@@ -5159,7 +5159,7 @@ class Select(HasPrefixes, SelectBase):
:class:`SelectBase` superclass.
"""
- self._should_correlate = correlate
+ self._auto_correlate = correlate
if distinct is not False:
if distinct is True:
self._distinct = True
@@ -5232,7 +5232,7 @@ class Select(HasPrefixes, SelectBase):
return froms
- def _get_display_froms(self, existing_froms=None):
+ def _get_display_froms(self, existing_froms=None, asfrom=False):
"""Return the full list of 'from' clauses to be displayed.
Takes into account a set of existing froms which may be
@@ -5258,18 +5258,29 @@ class Select(HasPrefixes, SelectBase):
# using a list to maintain ordering
froms = [f for f in froms if f not in toremove]
- if len(froms) > 1 or self._correlate or self._correlate_except:
+ if not asfrom:
if self._correlate:
- froms = [f for f in froms if f not in
- _cloned_intersection(froms,
- self._correlate)]
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(
+ _cloned_intersection(froms, existing_froms or ()),
+ self._correlate
+ )
+ ]
if self._correlate_except:
- froms = [f for f in froms if f in _cloned_intersection(froms,
- self._correlate_except)]
- if self._should_correlate and existing_froms:
- froms = [f for f in froms if f not in
- _cloned_intersection(froms,
- existing_froms)]
+ froms = [
+ f for f in froms if f in
+ _cloned_intersection(
+ froms,
+ self._correlate_except
+ )
+ ]
+
+ if self._auto_correlate and existing_froms and len(froms) > 1:
+ froms = [
+ f for f in froms if f not in
+ _cloned_intersection(froms, existing_froms)
+ ]
if not len(froms):
raise exc.InvalidRequestError("Select statement '%s"
@@ -5642,7 +5653,7 @@ class Select(HasPrefixes, SelectBase):
:ref:`correlated_subqueries`
"""
- self._should_correlate = False
+ self._auto_correlate = False
if fromclauses and fromclauses[0] is None:
self._correlate = ()
else:
@@ -5662,7 +5673,7 @@ class Select(HasPrefixes, SelectBase):
:ref:`correlated_subqueries`
"""
- self._should_correlate = False
+ self._auto_correlate = False
if fromclauses and fromclauses[0] is None:
self._correlate_except = ()
else:
@@ -5673,7 +5684,7 @@ class Select(HasPrefixes, SelectBase):
"""append the given correlation expression to this select()
construct."""
- self._should_correlate = False
+ self._auto_correlate = False
self._correlate = set(self._correlate).union(
_interpret_as_from(f) for f in fromclause)
diff --git a/test/orm/test_froms.py b/test/orm/test_froms.py
index 4c566948a..b98333e3d 100644
--- a/test/orm/test_froms.py
+++ b/test/orm/test_froms.py
@@ -174,9 +174,7 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
)
# a little tedious here, adding labels to work around Query's
- # auto-labelling. TODO: can we detect only one table in the
- # "froms" and then turn off use_labels ? note: this query is
- # incorrect SQL with the correlate of users in the FROM list.
+ # auto-labelling.
s = sess.query(addresses.c.id.label('id'),
addresses.c.email_address.label('email')).\
filter(addresses.c.user_id == users.c.id).correlate(users).\
@@ -188,7 +186,7 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
"SELECT users.id AS users_id, users.name AS users_name, "
"anon_1.email AS anon_1_email "
"FROM users JOIN (SELECT addresses.id AS id, "
- "addresses.email_address AS email FROM addresses "
+ "addresses.email_address AS email FROM addresses, users "
"WHERE addresses.user_id = users.id) AS anon_1 "
"ON anon_1.id = users.id",
)
diff --git a/test/orm/test_query.py b/test/orm/test_query.py
index f418d2581..ac9c95f41 100644
--- a/test/orm/test_query.py
+++ b/test/orm/test_query.py
@@ -194,22 +194,33 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
Address = self.classes.Address
self.assert_compile(
- select([User]).where(User.id == Address.user_id).
- correlate(Address),
- "SELECT users.id, users.name FROM users "
- "WHERE users.id = addresses.user_id"
+ select([User.name, Address.id,
+ select([func.count(Address.id)]).\
+ where(User.id == Address.user_id).\
+ correlate(User).as_scalar()
+ ]),
+ "SELECT users.name, addresses.id, "
+ "(SELECT count(addresses.id) AS count_1 "
+ "FROM addresses WHERE users.id = addresses.user_id) AS anon_1 "
+ "FROM users, addresses"
)
def test_correlate_aliased_entity(self):
User = self.classes.User
Address = self.classes.Address
- aa = aliased(Address, name="aa")
+ uu = aliased(User, name="uu")
self.assert_compile(
- select([User]).where(User.id == aa.user_id).
- correlate(aa),
- "SELECT users.id, users.name FROM users "
- "WHERE users.id = aa.user_id"
+ select([uu.name, Address.id,
+ select([func.count(Address.id)]).\
+ where(uu.id == Address.user_id).\
+ correlate(uu).as_scalar()
+ ]),
+ # curious, "address.user_id = uu.id" is reversed here
+ "SELECT uu.name, addresses.id, "
+ "(SELECT count(addresses.id) AS count_1 "
+ "FROM addresses WHERE addresses.user_id = uu.id) AS anon_1 "
+ "FROM users AS uu, addresses"
)
def test_columns_clause_entity(self):
diff --git a/test/sql/test_compiler.py b/test/sql/test_compiler.py
index 3b8aed23f..fe52402ec 100644
--- a/test/sql/test_compiler.py
+++ b/test/sql/test_compiler.py
@@ -87,6 +87,7 @@ keyed = Table('keyed', metadata,
Column('z', Integer),
)
+
class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
__dialect__ = 'default'
@@ -424,35 +425,6 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
"AS z FROM keyed) AS anon_2) AS anon_1"
)
- def test_dont_overcorrelate(self):
- self.assert_compile(select([table1], from_obj=[table1,
- table1.select()]),
- "SELECT mytable.myid, mytable.name, "
- "mytable.description FROM mytable, (SELECT "
- "mytable.myid AS myid, mytable.name AS "
- "name, mytable.description AS description "
- "FROM mytable)")
-
- def test_full_correlate(self):
- # intentional
- t = table('t', column('a'), column('b'))
- s = select([t.c.a]).where(t.c.a == 1).correlate(t).as_scalar()
-
- s2 = select([t.c.a, s])
- self.assert_compile(s2,
- "SELECT t.a, (SELECT t.a WHERE t.a = :a_1) AS anon_1 FROM t")
-
- # unintentional
- t2 = table('t2', column('c'), column('d'))
- s = select([t.c.a]).where(t.c.a == t2.c.d).as_scalar()
- s2 = select([t, t2, s])
- assert_raises(exc.InvalidRequestError, str, s2)
-
- # intentional again
- s = s.correlate(t, t2)
- s2 = select([t, t2, s])
- self.assert_compile(s, "SELECT t.a WHERE t.a = t2.d")
-
def test_exists(self):
s = select([table1.c.myid]).where(table1.c.myid == 5)
@@ -3189,6 +3161,246 @@ class SchemaTest(fixtures.TestBase, AssertsCompiledSQL):
"(:rem_id, :datatype_id, :value)")
+class CorrelateTest(fixtures.TestBase, AssertsCompiledSQL):
+ __dialect__ = 'default'
+
+ def test_dont_overcorrelate(self):
+ self.assert_compile(select([table1], from_obj=[table1,
+ table1.select()]),
+ "SELECT mytable.myid, mytable.name, "
+ "mytable.description FROM mytable, (SELECT "
+ "mytable.myid AS myid, mytable.name AS "
+ "name, mytable.description AS description "
+ "FROM mytable)")
+
+ def _fixture(self):
+ t1 = table('t1', column('a'))
+ t2 = table('t2', column('a'))
+ return t1, t2, select([t1]).where(t1.c.a == t2.c.a)
+
+ def _assert_where_correlated(self, stmt):
+ self.assert_compile(
+ stmt,
+ "SELECT t2.a FROM t2 WHERE t2.a = "
+ "(SELECT t1.a FROM t1 WHERE t1.a = t2.a)")
+
+ def _assert_where_all_correlated(self, stmt):
+ self.assert_compile(
+ stmt,
+ "SELECT t1.a, t2.a FROM t1, t2 WHERE t2.a = "
+ "(SELECT t1.a WHERE t1.a = t2.a)")
+
+ def _assert_where_backwards_correlated(self, stmt):
+ self.assert_compile(
+ stmt,
+ "SELECT t2.a FROM t2 WHERE t2.a = "
+ "(SELECT t1.a FROM t2 WHERE t1.a = t2.a)")
+
+ def _assert_column_correlated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a, (SELECT t1.a FROM t1 WHERE t1.a = t2.a) "
+ "AS anon_1 FROM t2")
+
+ def _assert_column_all_correlated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t1.a, t2.a, "
+ "(SELECT t1.a WHERE t1.a = t2.a) AS anon_1 FROM t1, t2")
+
+ def _assert_column_backwards_correlated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a, (SELECT t1.a FROM t2 WHERE t1.a = t2.a) "
+ "AS anon_1 FROM t2")
+
+ def _assert_having_correlated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a FROM t2 HAVING t2.a = "
+ "(SELECT t1.a FROM t1 WHERE t1.a = t2.a)")
+
+ def _assert_from_uncorrelated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a, anon_1.a FROM t2, "
+ "(SELECT t1.a AS a FROM t1, t2 WHERE t1.a = t2.a) AS anon_1")
+
+ def _assert_from_all_uncorrelated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t1.a, t2.a, anon_1.a FROM t1, t2, "
+ "(SELECT t1.a AS a FROM t1, t2 WHERE t1.a = t2.a) AS anon_1")
+
+ def _assert_where_uncorrelated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a FROM t2 WHERE t2.a = "
+ "(SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a)")
+
+ def _assert_column_uncorrelated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a, (SELECT t1.a FROM t1, t2 "
+ "WHERE t1.a = t2.a) AS anon_1 FROM t2")
+
+ def _assert_having_uncorrelated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t2.a FROM t2 HAVING t2.a = "
+ "(SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a)")
+
+ def _assert_where_single_full_correlated(self, stmt):
+ self.assert_compile(stmt,
+ "SELECT t1.a FROM t1 WHERE t1.a = (SELECT t1.a)")
+
+ def test_correlate_semiauto_where(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_where_correlated(
+ select([t2]).where(t2.c.a == s1.correlate(t2)))
+
+ def test_correlate_semiauto_column(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_column_correlated(
+ select([t2, s1.correlate(t2).as_scalar()]))
+
+ def test_correlate_semiauto_from(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_from_uncorrelated(
+ select([t2, s1.correlate(t2).alias()]))
+
+ def test_correlate_semiauto_having(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_having_correlated(
+ select([t2]).having(t2.c.a == s1.correlate(t2)))
+
+ def test_correlate_except_inclusion_where(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_where_correlated(
+ select([t2]).where(t2.c.a == s1.correlate_except(t1)))
+
+ def test_correlate_except_exclusion_where(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_where_backwards_correlated(
+ select([t2]).where(t2.c.a == s1.correlate_except(t2)))
+
+ def test_correlate_except_inclusion_column(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_column_correlated(
+ select([t2, s1.correlate_except(t1).as_scalar()]))
+
+ def test_correlate_except_exclusion_column(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_column_backwards_correlated(
+ select([t2, s1.correlate_except(t2).as_scalar()]))
+
+ def test_correlate_except_inclusion_from(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_from_uncorrelated(
+ select([t2, s1.correlate_except(t1).alias()]))
+
+ def test_correlate_except_exclusion_from(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_from_uncorrelated(
+ select([t2, s1.correlate_except(t2).alias()]))
+
+ def test_correlate_except_having(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_having_correlated(
+ select([t2]).having(t2.c.a == s1.correlate_except(t1)))
+
+ def test_correlate_auto_where(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_where_correlated(
+ select([t2]).where(t2.c.a == s1))
+
+ def test_correlate_auto_column(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_column_correlated(
+ select([t2, s1.as_scalar()]))
+
+ def test_correlate_auto_from(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_from_uncorrelated(
+ select([t2, s1.alias()]))
+
+ def test_correlate_auto_having(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_having_correlated(
+ select([t2]).having(t2.c.a == s1))
+
+ def test_correlate_disabled_where(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_where_uncorrelated(
+ select([t2]).where(t2.c.a == s1.correlate(None)))
+
+ def test_correlate_disabled_column(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_column_uncorrelated(
+ select([t2, s1.correlate(None).as_scalar()]))
+
+ def test_correlate_disabled_from(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_from_uncorrelated(
+ select([t2, s1.correlate(None).alias()]))
+
+ def test_correlate_disabled_having(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_having_uncorrelated(
+ select([t2]).having(t2.c.a == s1.correlate(None)))
+
+ def test_correlate_all_where(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_where_all_correlated(
+ select([t1, t2]).where(t2.c.a == s1.correlate(t1, t2)))
+
+ def test_correlate_all_column(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_column_all_correlated(
+ select([t1, t2, s1.correlate(t1, t2).as_scalar()]))
+
+ def test_correlate_all_from(self):
+ t1, t2, s1 = self._fixture()
+ self._assert_from_all_uncorrelated(
+ select([t1, t2, s1.correlate(t1, t2).alias()]))
+
+ def test_correlate_where_all_unintentional(self):
+ t1, t2, s1 = self._fixture()
+ assert_raises_message(
+ exc.InvalidRequestError,
+ "returned no FROM clauses due to auto-correlation",
+ select([t1, t2]).where(t2.c.a == s1).compile
+ )
+
+ def test_correlate_from_all_ok(self):
+ t1, t2, s1 = self._fixture()
+ self.assert_compile(
+ select([t1, t2, s1]),
+ "SELECT t1.a, t2.a, a FROM t1, t2, "
+ "(SELECT t1.a AS a FROM t1, t2 WHERE t1.a = t2.a)"
+ )
+
+ def test_correlate_auto_where_singlefrom(self):
+ t1, t2, s1 = self._fixture()
+ s = select([t1.c.a])
+ s2 = select([t1]).where(t1.c.a == s)
+ self.assert_compile(s2,
+ "SELECT t1.a FROM t1 WHERE t1.a = "
+ "(SELECT t1.a FROM t1)")
+
+ def test_correlate_semiauto_where_singlefrom(self):
+ t1, t2, s1 = self._fixture()
+
+ s = select([t1.c.a])
+
+ s2 = select([t1]).where(t1.c.a == s.correlate(t1))
+ self._assert_where_single_full_correlated(s2)
+
+ def test_correlate_except_semiauto_where_singlefrom(self):
+ t1, t2, s1 = self._fixture()
+
+ s = select([t1.c.a])
+
+ s2 = select([t1]).where(t1.c.a == s.correlate_except(t2))
+ self._assert_where_single_full_correlated(s2)
+
+ def test_correlate_alone_noeffect(self):
+ # new as of #2668
+ t1, t2, s1 = self._fixture()
+ self.assert_compile(s1.correlate(t1, t2),
+ "SELECT t1.a FROM t1, t2 WHERE t1.a = t2.a")
+
class CoercionTest(fixtures.TestBase, AssertsCompiledSQL):
__dialect__ = 'default'
@@ -3315,4 +3527,4 @@ class ResultMapTest(fixtures.TestBase):
)
is_(
comp.result_map['t1_a'][1][2], t1.c.a
- ) \ No newline at end of file
+ )
diff --git a/test/sql/test_generative.py b/test/sql/test_generative.py
index e868cbe88..8b2abef0e 100644
--- a/test/sql/test_generative.py
+++ b/test/sql/test_generative.py
@@ -590,13 +590,18 @@ class ClauseTest(fixtures.TestBase, AssertsCompiledSQL):
def test_correlated_select(self):
s = select(['*'], t1.c.col1 == t2.c.col1,
from_obj=[t1, t2]).correlate(t2)
+
class Vis(CloningVisitor):
def visit_select(self, select):
select.append_whereclause(t1.c.col2 == 7)
- self.assert_compile(Vis().traverse(s),
- "SELECT * FROM table1 WHERE table1.col1 = table2.col1 "
- "AND table1.col2 = :col2_1")
+ self.assert_compile(
+ select([t2]).where(t2.c.col1 == Vis().traverse(s)),
+ "SELECT table2.col1, table2.col2, table2.col3 "
+ "FROM table2 WHERE table2.col1 = "
+ "(SELECT * FROM table1 WHERE table1.col1 = table2.col1 "
+ "AND table1.col2 = :col2_1)"
+ )
def test_this_thing(self):
s = select([t1]).where(t1.c.col1 == 'foo').alias()
@@ -616,35 +621,49 @@ class ClauseTest(fixtures.TestBase, AssertsCompiledSQL):
'AS table1_1 WHERE table1_1.col1 = '
':col1_1) AS anon_1')
- def test_select_fromtwice(self):
+ def test_select_fromtwice_one(self):
t1a = t1.alias()
- s = select([1], t1.c.col1 == t1a.c.col1, from_obj=t1a).correlate(t1)
+ s = select([1], t1.c.col1 == t1a.c.col1, from_obj=t1a).correlate(t1a)
+ s = select([t1]).where(t1.c.col1 == s)
self.assert_compile(s,
- 'SELECT 1 FROM table1 AS table1_1 WHERE '
- 'table1.col1 = table1_1.col1')
-
+ "SELECT table1.col1, table1.col2, table1.col3 FROM table1 "
+ "WHERE table1.col1 = "
+ "(SELECT 1 FROM table1, table1 AS table1_1 "
+ "WHERE table1.col1 = table1_1.col1)"
+ )
s = CloningVisitor().traverse(s)
self.assert_compile(s,
- 'SELECT 1 FROM table1 AS table1_1 WHERE '
- 'table1.col1 = table1_1.col1')
+ "SELECT table1.col1, table1.col2, table1.col3 FROM table1 "
+ "WHERE table1.col1 = "
+ "(SELECT 1 FROM table1, table1 AS table1_1 "
+ "WHERE table1.col1 = table1_1.col1)")
+ def test_select_fromtwice_two(self):
s = select([t1]).where(t1.c.col1 == 'foo').alias()
s2 = select([1], t1.c.col1 == s.c.col1, from_obj=s).correlate(t1)
- self.assert_compile(s2,
- 'SELECT 1 FROM (SELECT table1.col1 AS '
- 'col1, table1.col2 AS col2, table1.col3 AS '
- 'col3 FROM table1 WHERE table1.col1 = '
- ':col1_1) AS anon_1 WHERE table1.col1 = '
- 'anon_1.col1')
- s2 = ReplacingCloningVisitor().traverse(s2)
- self.assert_compile(s2,
- 'SELECT 1 FROM (SELECT table1.col1 AS '
- 'col1, table1.col2 AS col2, table1.col3 AS '
- 'col3 FROM table1 WHERE table1.col1 = '
- ':col1_1) AS anon_1 WHERE table1.col1 = '
- 'anon_1.col1')
+ s3 = select([t1]).where(t1.c.col1 == s2)
+ self.assert_compile(s3,
+ "SELECT table1.col1, table1.col2, table1.col3 "
+ "FROM table1 WHERE table1.col1 = "
+ "(SELECT 1 FROM "
+ "(SELECT table1.col1 AS col1, table1.col2 AS col2, "
+ "table1.col3 AS col3 FROM table1 "
+ "WHERE table1.col1 = :col1_1) "
+ "AS anon_1 WHERE table1.col1 = anon_1.col1)"
+ )
+
+ s4 = ReplacingCloningVisitor().traverse(s3)
+ self.assert_compile(s4,
+ "SELECT table1.col1, table1.col2, table1.col3 "
+ "FROM table1 WHERE table1.col1 = "
+ "(SELECT 1 FROM "
+ "(SELECT table1.col1 AS col1, table1.col2 AS col2, "
+ "table1.col3 AS col3 FROM table1 "
+ "WHERE table1.col1 = :col1_1) "
+ "AS anon_1 WHERE table1.col1 = anon_1.col1)"
+ )
class ClauseAdapterTest(fixtures.TestBase, AssertsCompiledSQL):
__dialect__ = 'default'
@@ -763,67 +782,125 @@ class ClauseAdapterTest(fixtures.TestBase, AssertsCompiledSQL):
'FROM addresses WHERE users_1.id = '
'addresses.user_id')
- def test_table_to_alias(self):
-
+ def test_table_to_alias_1(self):
t1alias = t1.alias('t1alias')
vis = sql_util.ClauseAdapter(t1alias)
ff = vis.traverse(func.count(t1.c.col1).label('foo'))
assert list(_from_objects(ff)) == [t1alias]
+ def test_table_to_alias_2(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(vis.traverse(select(['*'], from_obj=[t1])),
'SELECT * FROM table1 AS t1alias')
+
+ def test_table_to_alias_3(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(select(['*'], t1.c.col1 == t2.c.col2),
'SELECT * FROM table1, table2 WHERE '
'table1.col1 = table2.col2')
+
+ def test_table_to_alias_4(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(vis.traverse(select(['*'], t1.c.col1
== t2.c.col2)),
'SELECT * FROM table1 AS t1alias, table2 '
'WHERE t1alias.col1 = table2.col2')
+
+ def test_table_to_alias_5(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(vis.traverse(select(['*'], t1.c.col1
== t2.c.col2, from_obj=[t1, t2])),
'SELECT * FROM table1 AS t1alias, table2 '
'WHERE t1alias.col1 = table2.col2')
- self.assert_compile(vis.traverse(select(['*'], t1.c.col1
- == t2.c.col2, from_obj=[t1,
- t2]).correlate(t1)),
- 'SELECT * FROM table2 WHERE t1alias.col1 = '
- 'table2.col2')
- self.assert_compile(vis.traverse(select(['*'], t1.c.col1
- == t2.c.col2, from_obj=[t1,
- t2]).correlate(t2)),
- 'SELECT * FROM table1 AS t1alias WHERE '
- 't1alias.col1 = table2.col2')
+
+ def test_table_to_alias_6(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
+ self.assert_compile(
+ select([t1alias, t2]).where(t1alias.c.col1 ==
+ vis.traverse(select(['*'],
+ t1.c.col1 == t2.c.col2,
+ from_obj=[t1, t2]).correlate(t1))),
+ "SELECT t1alias.col1, t1alias.col2, t1alias.col3, "
+ "table2.col1, table2.col2, table2.col3 "
+ "FROM table1 AS t1alias, table2 WHERE t1alias.col1 = "
+ "(SELECT * FROM table2 WHERE t1alias.col1 = table2.col2)"
+ )
+
+ def test_table_to_alias_7(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
+ self.assert_compile(
+ select([t1alias, t2]).where(t1alias.c.col1 ==
+ vis.traverse(select(['*'],
+ t1.c.col1 == t2.c.col2,
+ from_obj=[t1, t2]).correlate(t2))),
+ "SELECT t1alias.col1, t1alias.col2, t1alias.col3, "
+ "table2.col1, table2.col2, table2.col3 "
+ "FROM table1 AS t1alias, table2 "
+ "WHERE t1alias.col1 = "
+ "(SELECT * FROM table1 AS t1alias "
+ "WHERE t1alias.col1 = table2.col2)")
+
+ def test_table_to_alias_8(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(vis.traverse(case([(t1.c.col1 == 5,
t1.c.col2)], else_=t1.c.col1)),
'CASE WHEN (t1alias.col1 = :col1_1) THEN '
't1alias.col2 ELSE t1alias.col1 END')
+
+ def test_table_to_alias_9(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(vis.traverse(case([(5, t1.c.col2)],
value=t1.c.col1, else_=t1.c.col1)),
'CASE t1alias.col1 WHEN :param_1 THEN '
't1alias.col2 ELSE t1alias.col1 END')
+ def test_table_to_alias_10(self):
s = select(['*'], from_obj=[t1]).alias('foo')
self.assert_compile(s.select(),
'SELECT foo.* FROM (SELECT * FROM table1) '
'AS foo')
+
+ def test_table_to_alias_11(self):
+ s = select(['*'], from_obj=[t1]).alias('foo')
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
self.assert_compile(vis.traverse(s.select()),
'SELECT foo.* FROM (SELECT * FROM table1 '
'AS t1alias) AS foo')
+
+ def test_table_to_alias_12(self):
+ s = select(['*'], from_obj=[t1]).alias('foo')
self.assert_compile(s.select(),
'SELECT foo.* FROM (SELECT * FROM table1) '
'AS foo')
+
+ def test_table_to_alias_13(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
ff = vis.traverse(func.count(t1.c.col1).label('foo'))
self.assert_compile(select([ff]),
'SELECT count(t1alias.col1) AS foo FROM '
'table1 AS t1alias')
assert list(_from_objects(ff)) == [t1alias]
+ #def test_table_to_alias_2(self):
# TODO: self.assert_compile(vis.traverse(select([func.count(t1.c
# .col1).l abel('foo')]), clone=True), "SELECT
# count(t1alias.col1) AS foo FROM table1 AS t1alias")
+ def test_table_to_alias_14(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
t2alias = t2.alias('t2alias')
vis.chain(sql_util.ClauseAdapter(t2alias))
self.assert_compile(vis.traverse(select(['*'], t1.c.col1
@@ -831,28 +908,59 @@ class ClauseAdapterTest(fixtures.TestBase, AssertsCompiledSQL):
'SELECT * FROM table1 AS t1alias, table2 '
'AS t2alias WHERE t1alias.col1 = '
't2alias.col2')
+
+ def test_table_to_alias_15(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
+ t2alias = t2.alias('t2alias')
+ vis.chain(sql_util.ClauseAdapter(t2alias))
self.assert_compile(vis.traverse(select(['*'], t1.c.col1
== t2.c.col2, from_obj=[t1, t2])),
'SELECT * FROM table1 AS t1alias, table2 '
'AS t2alias WHERE t1alias.col1 = '
't2alias.col2')
- self.assert_compile(vis.traverse(select(['*'], t1.c.col1
- == t2.c.col2, from_obj=[t1,
- t2]).correlate(t1)),
- 'SELECT * FROM table2 AS t2alias WHERE '
- 't1alias.col1 = t2alias.col2')
- self.assert_compile(vis.traverse(select(['*'], t1.c.col1
- == t2.c.col2, from_obj=[t1,
- t2]).correlate(t2)),
- 'SELECT * FROM table1 AS t1alias WHERE '
- 't1alias.col1 = t2alias.col2')
+
+ def test_table_to_alias_16(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
+ t2alias = t2.alias('t2alias')
+ vis.chain(sql_util.ClauseAdapter(t2alias))
+ self.assert_compile(
+ select([t1alias, t2alias]).where(
+ t1alias.c.col1 ==
+ vis.traverse(select(['*'],
+ t1.c.col1 == t2.c.col2,
+ from_obj=[t1, t2]).correlate(t1))
+ ),
+ "SELECT t1alias.col1, t1alias.col2, t1alias.col3, "
+ "t2alias.col1, t2alias.col2, t2alias.col3 "
+ "FROM table1 AS t1alias, table2 AS t2alias "
+ "WHERE t1alias.col1 = "
+ "(SELECT * FROM table2 AS t2alias "
+ "WHERE t1alias.col1 = t2alias.col2)"
+ )
+
+ def test_table_to_alias_17(self):
+ t1alias = t1.alias('t1alias')
+ vis = sql_util.ClauseAdapter(t1alias)
+ t2alias = t2.alias('t2alias')
+ vis.chain(sql_util.ClauseAdapter(t2alias))
+ self.assert_compile(
+ t2alias.select().where(t2alias.c.col2 ==
+ vis.traverse(select(['*'],
+ t1.c.col1 == t2.c.col2,
+ from_obj=[t1, t2]).correlate(t2))),
+ 'SELECT t2alias.col1, t2alias.col2, t2alias.col3 '
+ 'FROM table2 AS t2alias WHERE t2alias.col2 = '
+ '(SELECT * FROM table1 AS t1alias WHERE '
+ 't1alias.col1 = t2alias.col2)')
def test_include_exclude(self):
m = MetaData()
- a=Table( 'a',m,
- Column( 'id', Integer, primary_key=True),
- Column( 'xxx_id', Integer,
- ForeignKey( 'a.id', name='adf',use_alter=True )
+ a = Table('a', m,
+ Column('id', Integer, primary_key=True),
+ Column('xxx_id', Integer,
+ ForeignKey('a.id', name='adf', use_alter=True)
)
)
@@ -1167,93 +1275,6 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
'SELECT table1.col1, table1.col2, '
'table1.col3 FROM table1')
- def test_correlation(self):
- s = select([t2], t1.c.col1 == t2.c.col1)
- self.assert_compile(s,
- 'SELECT table2.col1, table2.col2, '
- 'table2.col3 FROM table2, table1 WHERE '
- 'table1.col1 = table2.col1')
- s2 = select([t1], t1.c.col2 == s.c.col2)
- # dont correlate in a FROM entry
- self.assert_compile(s2,
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2, table1 WHERE '
- 'table1.col1 = table2.col1) WHERE '
- 'table1.col2 = col2')
- s3 = s.correlate(None)
- self.assert_compile(select([t1], t1.c.col2 == s3.c.col2),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2, table1 '
- 'WHERE table1.col1 = table2.col1) WHERE '
- 'table1.col2 = col2')
- # dont correlate in a FROM entry
- self.assert_compile(select([t1], t1.c.col2 == s.c.col2),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2, table1 WHERE '
- 'table1.col1 = table2.col1) WHERE '
- 'table1.col2 = col2')
-
- # but correlate in a WHERE entry
- s_w = select([t2.c.col1]).where(t1.c.col1 == t2.c.col1)
- self.assert_compile(select([t1], t1.c.col2 == s_w),
- 'SELECT table1.col1, table1.col2, table1.col3 '
- 'FROM table1 WHERE table1.col2 = '
- '(SELECT table2.col1 FROM table2 '
- 'WHERE table1.col1 = table2.col1)'
- )
-
-
- s4 = s3.correlate(t1)
- self.assert_compile(select([t1], t1.c.col2 == s4.c.col2),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2 WHERE '
- 'table1.col1 = table2.col1) WHERE '
- 'table1.col2 = col2')
-
- self.assert_compile(select([t1], t1.c.col2 == s3.c.col2),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2, table1 '
- 'WHERE table1.col1 = table2.col1) WHERE '
- 'table1.col2 = col2')
-
- self.assert_compile(t1.select().where(t1.c.col1
- == 5).order_by(t1.c.col3),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1 WHERE table1.col1 '
- '= :col1_1 ORDER BY table1.col3')
-
- # dont correlate in FROM
- self.assert_compile(t1.select().select_from(select([t2],
- t2.c.col1
- == t1.c.col1)).order_by(t1.c.col3),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2, table1 WHERE '
- 'table2.col1 = table1.col1) ORDER BY '
- 'table1.col3')
-
- # still works if you actually add that table to correlate()
- s = select([t2], t2.c.col1 == t1.c.col1)
- s = s.correlate(t1).order_by(t2.c.col3)
-
- self.assert_compile(t1.select().select_from(s).order_by(t1.c.col3),
- 'SELECT table1.col1, table1.col2, '
- 'table1.col3 FROM table1, (SELECT '
- 'table2.col1 AS col1, table2.col2 AS col2, '
- 'table2.col3 AS col3 FROM table2 WHERE '
- 'table2.col1 = table1.col1 ORDER BY '
- 'table2.col3) ORDER BY table1.col3')
def test_prefixes(self):
s = t1.select()