--- /dev/null
+.. change::
+ :tags: bug, sql
+ :tickets: 7888
+
+ The FROM clauses that are established on a :func:`_sql.select` construct
+ when using the :meth:`_sql.Select.select_from` method will now render first
+ in the FROM clause of the rendered SELECT, which serves to maintain the
+ ordering of clauses as was passed to the :meth:`_sql.Select.select_from`
+ method itself without being affected by the presence of those clauses also
+ being mentioned in other parts of the query. If other elements of the
+ :class:`_sql.Select` also generate FROM clauses, such as the columns clause
+ or WHERE clause, these will render after the clauses delivered by
+ :meth:`_sql.Select.select_from` assuming they were not explictly passed to
+ :meth:`_sql.Select.select_from` also. This improvement is useful in those
+ cases where a particular database generates a desirable query plan based on
+ a particular ordering of FROM clauses and allows full control over the
+ ordering of FROM clauses.
return self._normalize_froms(
itertools.chain(
+ self.from_clauses,
itertools.chain.from_iterable(
[
element._from_objects
for element in statement._where_criteria
]
),
- self.from_clauses,
),
check_statement=statement,
ambiguous_table_name_map=ambiguous_table_name_map,
self.assert_compile(
A.b_data == "foo",
- "EXISTS (SELECT 1 FROM a, b WHERE a.id = b.a_id "
+ "EXISTS (SELECT 1 FROM b, a WHERE a.id = b.a_id "
"AND b.data = :data_1)",
)
self.assert_compile(
A.b_data_syn == "foo",
- "EXISTS (SELECT 1 FROM a, b WHERE a.id = b.a_id "
+ "EXISTS (SELECT 1 FROM b, a WHERE a.id = b.a_id "
"AND b.data = :data_1)",
)
eq_(
str(A.well_behaved_b_data == 5),
- "EXISTS (SELECT 1 \nFROM a, b \nWHERE "
+ "EXISTS (SELECT 1 \nFROM b, a \nWHERE "
"a.id = b.aid AND b.data = :data_1)",
)
"anon_1.boss_boss_id AS anon_1_boss_boss_id, "
"anon_1.boss_golf_swing AS anon_1_boss_golf_swing, "
"companies.name AS companies_name "
- "FROM (SELECT people.person_id AS people_person_id, "
+ "FROM companies JOIN "
+ "(people LEFT OUTER JOIN engineers "
+ "ON people.person_id = engineers.person_id "
+ "LEFT OUTER JOIN managers "
+ "ON people.person_id = managers.person_id "
+ "LEFT OUTER JOIN boss ON managers.person_id = boss.boss_id) "
+ "ON companies.company_id = people.company_id, "
+ "(SELECT people.person_id AS people_person_id, "
"people.company_id AS people_company_id, "
"people.name AS people_name, people.type AS people_type, "
"engineers.person_id AS engineers_person_id, "
"ON people.person_id = engineers.person_id "
"LEFT OUTER JOIN managers "
"ON people.person_id = managers.person_id LEFT OUTER JOIN boss "
- "ON managers.person_id = boss.boss_id) AS anon_1, "
- "companies JOIN "
- "(people LEFT OUTER JOIN engineers "
- "ON people.person_id = engineers.person_id "
- "LEFT OUTER JOIN managers "
- "ON people.person_id = managers.person_id "
- "LEFT OUTER JOIN boss ON managers.person_id = boss.boss_id) "
- "ON companies.company_id = people.company_id "
+ "ON managers.person_id = boss.boss_id) AS anon_1 "
"WHERE anon_1.people_name = :people_name_1 "
"ORDER BY anon_1.people_person_id",
)
.filter(Child1.left_child2 == c22)
.statement,
"SELECT child2.id, parent.id AS id_1, parent.cls "
- "FROM secondary AS secondary_1, parent "
+ "FROM parent "
"JOIN child2 ON parent.id = child2.id "
- "JOIN secondary AS secondary_2 ON parent.id = "
- "secondary_2.left_id "
+ "JOIN secondary AS secondary_1 ON parent.id = "
+ "secondary_1.left_id "
"JOIN (parent AS parent_1 JOIN child1 AS child1_1 "
"ON parent_1.id = child1_1.id) ON parent_1.id = "
- "secondary_2.right_id "
- "WHERE parent_1.id = secondary_1.right_id "
- "AND :param_1 = secondary_1.left_id",
+ "secondary_1.right_id, secondary AS secondary_2 "
+ "WHERE parent_1.id = secondary_2.right_id "
+ "AND :param_1 = secondary_2.left_id",
)
# non aliased version
.filter(c1.left_child2 == c22)
.statement,
"SELECT child2.id, parent.id AS id_1, parent.cls "
- "FROM secondary AS secondary_1, parent "
+ "FROM parent "
"JOIN child2 ON parent.id = child2.id "
- "JOIN secondary AS secondary_2 ON parent.id = secondary_2.left_id "
+ "JOIN secondary AS secondary_1 ON parent.id = secondary_1.left_id "
"JOIN (parent AS parent_1 JOIN child1 AS child1_1 "
"ON parent_1.id = child1_1.id) ON parent_1.id = "
- "secondary_2.right_id "
- "WHERE parent_1.id = secondary_1.right_id "
- "AND :param_1 = secondary_1.left_id",
+ "secondary_1.right_id, secondary AS secondary_2 "
+ "WHERE parent_1.id = secondary_2.right_id "
+ "AND :param_1 = secondary_2.left_id",
)
def test_query_crit_core_workaround(self):
stmt.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL),
"SELECT child2.id AS child2_id, parent.id AS parent_id, "
"parent.cls AS parent_cls "
- "FROM secondary AS secondary_1, "
+ "FROM "
"parent JOIN child2 ON parent.id = child2.id JOIN secondary AS "
- "secondary_2 ON parent.id = secondary_2.left_id JOIN "
+ "secondary_1 ON parent.id = secondary_1.left_id JOIN "
"(parent AS parent_1 JOIN child1 AS child1_1 "
"ON parent_1.id = child1_1.id) "
- "ON parent_1.id = secondary_2.right_id WHERE "
- "parent_1.id = secondary_1.right_id AND :param_1 = "
- "secondary_1.left_id",
+ "ON parent_1.id = secondary_1.right_id, secondary AS secondary_2 "
+ "WHERE "
+ "parent_1.id = secondary_2.right_id AND :param_1 = "
+ "secondary_2.left_id",
)
def test_eager_join(self):
eq_(stmt.entity_description, expected_entity)
eq_(stmt.returning_column_descriptions, expected_returning)
+ @testing.combinations(
+ (
+ lambda User, Address: select(User.name)
+ .select_from(User, Address)
+ .where(User.id == Address.user_id),
+ "SELECT users.name FROM users, addresses "
+ "WHERE users.id = addresses.user_id",
+ ),
+ (
+ lambda User, Address: select(User.name)
+ .select_from(Address, User)
+ .where(User.id == Address.user_id),
+ "SELECT users.name FROM addresses, users "
+ "WHERE users.id = addresses.user_id",
+ ),
+ )
+ def test_select_from_ordering(self, stmt, expected):
+ User, Address = self.classes("User", "Address")
+
+ lambda_args = dict(
+ User=User,
+ Address=Address,
+ user_table=inspect(User).local_table,
+ )
+
+ stmt = testing.resolve_lambda(stmt, **lambda_args)
+ self.assert_compile(stmt, expected)
+
class ColumnsClauseFromsTest(QueryTest, AssertsCompiledSQL):
__dialect__ = "default"
q = sess.query(User)
with self._expect_implicit_subquery():
- q = sess.query(User).select_from(q.statement)
+ q = sess.query(User).select_from(User, q.statement)
self.assert_compile(
q.filter(User.name == "ed"),
"SELECT users.id AS users_id, users.name AS users_name "
gp.grand_children.filter_by(id=1),
"SELECT child.id AS child_id, child.type AS child_type, "
"child.parent_id AS child_parent_id, subchild.id AS subchild_id "
- "FROM parent, child LEFT OUTER JOIN subchild "
- "ON child.id = subchild.id "
+ "FROM child LEFT OUTER JOIN subchild "
+ "ON child.id = subchild.id, parent "
"WHERE :param_1 = parent.grand_parent_id "
"AND parent.id = child.parent_id AND child.id = :id_1",
{"id_1": 1},
order.items.join(ItemKeyword),
"SELECT items.id AS items_id, "
"items.description AS items_description "
- "FROM order_items, items "
- "JOIN item_keywords ON items.id = item_keywords.item_id "
+ "FROM items "
+ "JOIN item_keywords ON items.id = item_keywords.item_id, "
+ "order_items "
"WHERE :param_1 = order_items.order_id "
"AND items.id = order_items.item_id",
use_default_dialect=True,
.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
.statement,
"SELECT users.id AS users_id, users.name AS users_name "
- "FROM users, "
- "(SELECT users.id AS id, users.name AS name FROM users) AS anon_1",
+ "FROM "
+ "(SELECT users.id AS id, users.name AS name FROM users) "
+ "AS anon_1, users",
)
self.assert_compile(
self.assert_compile(
select(u2),
- "SELECT users_1.id, users_1.name FROM users AS users_1, "
- "(SELECT users.id AS id, users.name AS name FROM users) AS anon_1",
+ "SELECT users_1.id, users_1.name FROM "
+ "(SELECT users.id AS id, users.name AS name FROM users) "
+ "AS anon_1, users AS users_1",
)
def test_multiple_entities(self):
self.assert_compile(
q.filter(User.name == "ed"),
"SELECT users.id AS users_id, users.name AS users_name "
- "FROM users, (SELECT users.id AS id, users.name AS name FROM "
- "users) AS anon_1 WHERE users.name = :name_1",
+ "FROM (SELECT users.id AS id, users.name AS name FROM "
+ "users) AS anon_1, users WHERE users.name = :name_1",
)
def test_anonymous_expression_oldstyle(self):
.filter(ag1.email_address > 5),
"SELECT users.id "
"AS users_id, users.name AS users_name, addresses.email_address "
- "AS addresses_email_address FROM addresses, users JOIN "
+ "AS addresses_email_address FROM users JOIN "
"(SELECT addresses.id AS id, sum(length(addresses.email_address)) "
"AS email_address FROM addresses GROUP BY addresses.user_id) AS "
- "anon_1 ON users.id = addresses.user_id "
+ "anon_1 ON users.id = addresses.user_id, addresses "
"WHERE addresses.email_address > :email_address_1",
)
self.assert_compile(
sess.query(ualias).select_from(ua).filter(ualias.id > ua.id),
"SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
- "FROM users AS users_1, ("
+ "FROM ("
"SELECT users.id AS id, users.name AS name FROM users "
- "WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1 "
+ "WHERE users.id IN (__[POSTCOMPILE_id_1])) AS anon_1, "
+ "users AS users_1 "
"WHERE users_1.id > anon_1.id",
check_post_param={"id_1": [7, 8]},
)
),
"SELECT users.id AS users_id, users.name AS users_name, "
"users_1.id AS users_1_id, "
- "users_1.name AS users_1_name FROM users, users AS users_1 "
- "JOIN orders AS orders_1 ON users_1.id = orders_1.user_id",
+ "users_1.name AS users_1_name FROM users AS users_1 "
+ "JOIN orders AS orders_1 ON users_1.id = orders_1.user_id, users",
use_default_dialect=True,
)
q,
"SELECT users.id AS users_id, users.name AS users_name, "
"users_1.id AS users_1_id, users_1.name AS users_1_name "
- "FROM users AS users_1, "
- "users JOIN addresses ON users.id = addresses.user_id",
+ "FROM "
+ "users JOIN addresses ON users.id = addresses.user_id, "
+ "users AS users_1",
)
q = sess.query(User, u1).select_from(User, u1).join(u1.addresses)
"orders.isopen AS orders_isopen, dingalings.id AS dingalings_id, "
"dingalings.address_id AS dingalings_address_id, "
"dingalings.data AS dingalings_data "
- "FROM dingalings, orders "
+ "FROM orders "
"JOIN addresses AS addresses_1 "
- "ON orders.address_id = addresses_1.id",
+ "ON orders.address_id = addresses_1.id, dingalings",
)
# Dingaling is chosen to join to a1
"orders.isopen AS orders_isopen, dingalings.id AS dingalings_id, "
"dingalings.address_id AS dingalings_address_id, "
"dingalings.data AS dingalings_data "
- "FROM orders, dingalings JOIN addresses AS addresses_1 "
- "ON dingalings.address_id = addresses_1.id",
+ "FROM dingalings JOIN addresses AS addresses_1 "
+ "ON dingalings.address_id = addresses_1.id, orders",
)
def test_clause_present_in_froms_twice_w_onclause(self):
self.assert_compile(
s.query(B).filter(B.d.any(D.id == 1)),
"SELECT b.id AS b_id, b.c_id AS b_c_id FROM b WHERE "
- "EXISTS (SELECT 1 FROM b_d, d WHERE b.id = b_d.bid "
+ "EXISTS (SELECT 1 FROM d, b_d WHERE b.id = b_d.bid "
"AND d.id = b_d.did AND d.id = :id_1)",
)
"SELECT b.id AS b_id, b.c_id AS b_c_id FROM "
"b JOIN b_d AS b_d_1 ON b.id = b_d_1.bid "
"JOIN d ON d.id = b_d_1.did WHERE "
- "EXISTS (SELECT 1 FROM b_d, d WHERE b.id = b_d.bid "
+ "EXISTS (SELECT 1 FROM d, b_d WHERE b.id = b_d.bid "
"AND d.id = b_d.did AND d.id = :id_1)",
)
"FROM b JOIN b_d AS b_d_1 ON b.id = b_d_1.bid "
"JOIN d AS d_1 ON d_1.id = b_d_1.did "
"WHERE EXISTS "
- "(SELECT 1 FROM b_d, d WHERE b.id = b_d.bid "
+ "(SELECT 1 FROM d, b_d WHERE b.id = b_d.bid "
"AND d.id = b_d.did AND d.id = :id_1)",
)
asserter_.assert_(
CompiledSQL(
"SELECT a.id AS a_id FROM a WHERE "
- "EXISTS (SELECT 1 FROM (SELECT a.id AS aid, b.id AS id "
+ "EXISTS (SELECT 1 FROM b, (SELECT a.id AS aid, b.id AS id "
"FROM a JOIN b ON a.b_ids LIKE :id_1 || b.id || :param_1) "
- "AS anon_1, b WHERE a.id = anon_1.aid AND b.id = anon_1.id)",
+ "AS anon_1 WHERE a.id = anon_1.aid AND b.id = anon_1.id)",
params=[],
)
)
dialect=dialect,
)
+ @testing.combinations(
+ (
+ select(table1.c.name)
+ .select_from(table1, table2)
+ .where(table1.c.myid == table2.c.otherid),
+ "SELECT mytable.name FROM mytable, myothertable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table1.c.name)
+ .select_from(table2, table1)
+ .where(table1.c.myid == table2.c.otherid),
+ "SELECT mytable.name FROM myothertable, mytable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table2, table1),
+ "SELECT mytable.name FROM myothertable, mytable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table1, table2),
+ "SELECT mytable.name FROM mytable, myothertable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table3.c.userid, table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table1, table3, table2),
+ "SELECT thirdtable.userid, mytable.name "
+ "FROM mytable, thirdtable, myothertable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table3.c.userid, table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table3, table1, table2),
+ "SELECT thirdtable.userid, mytable.name "
+ "FROM thirdtable, mytable, myothertable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table3.c.userid, table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table1, table2),
+ "SELECT thirdtable.userid, mytable.name "
+ "FROM mytable, myothertable, thirdtable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table3.c.userid, table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table3, table2),
+ "SELECT thirdtable.userid, mytable.name "
+ "FROM thirdtable, myothertable, mytable "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table3.c.userid, table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table3, table2)
+ .join_from(table3, table1, table3.c.userid == table1.c.myid),
+ "SELECT thirdtable.userid, mytable.name "
+ "FROM thirdtable "
+ "JOIN mytable ON thirdtable.userid = mytable.myid, "
+ "myothertable WHERE mytable.myid = myothertable.otherid",
+ ),
+ (
+ select(table3.c.userid, table1.c.name)
+ .where(table1.c.myid == table2.c.otherid)
+ .select_from(table2, table3)
+ .join_from(table3, table1, table3.c.userid == table1.c.myid),
+ "SELECT thirdtable.userid, mytable.name "
+ "FROM myothertable, thirdtable "
+ "JOIN mytable ON thirdtable.userid = mytable.myid "
+ "WHERE mytable.myid = myothertable.otherid",
+ ),
+ )
+ def test_select_from_ordering(self, stmt, expected):
+ self.assert_compile(stmt, expected)
+
def test_from_subquery(self):
"""tests placing select statements in the column clause of
another select, for the
# don't correlate in a FROM list
self.assert_compile(
select(users, s.c.street).select_from(s),
- "SELECT users.user_id, users.user_name, "
- "users.password, s.street FROM users, "
- "(SELECT addresses.street AS street FROM "
- "addresses, users WHERE addresses.user_id = "
- "users.user_id) AS s",
+ "SELECT users.user_id, users.user_name, users.password, s.street "
+ "FROM (SELECT addresses.street AS street FROM addresses, users "
+ "WHERE addresses.user_id = users.user_id) AS s, users",
)
self.assert_compile(
table1.select().where(
s,
"SELECT table1.col1, table1.col2, table1.col3 FROM table1 "
"WHERE table1.col1 = "
- "(SELECT 1 FROM table1, table1 AS table1_1 "
+ "(SELECT 1 FROM table1 AS table1_1, table1 "
"WHERE table1.col1 = table1_1.col1)",
)
s = CloningVisitor().traverse(s)
s,
"SELECT table1.col1, table1.col2, table1.col3 FROM table1 "
"WHERE table1.col1 = "
- "(SELECT 1 FROM table1, table1 AS table1_1 "
+ "(SELECT 1 FROM table1 AS table1_1, table1 "
"WHERE table1.col1 = table1_1.col1)",
)
self.assert_compile(
select_copy,
"SELECT table1.col1, table1.col2, "
- "table1.col3 FROM table1, table2",
+ "table1.col3 FROM table2, table1",
)
self.assert_compile(
self.assert_compile(
stmt,
"SELECT result_elem[:result_elem_1] AS field "
- "FROM json_array_elements("
+ 'FROM "check" AS _check, json_array_elements('
"(SELECT check_inside.response[:response_1] AS anon_1 "
'FROM "check" AS check_inside '
"WHERE check_inside.id = _check.id)"
- ') AS result_elem, "check" AS _check '
+ ") AS result_elem "
"WHERE result_elem[:result_elem_2] = :param_1",
)
[table1.join(table2)],
[table1],
),
- ([table1], [table2], [table1, table2], [table1]),
+ ([table1], [table2], [table2, table1], [table1]),
(
[table1.c.col1, table2.c.col1],
[],
stmt = select(t1, t2, t3, t4).select_from(j2)
self.assert_compile(
stmt,
- "SELECT t1.a, t2.b, t3.c, t4.d FROM t3, "
- "t4 JOIN (t1 JOIN t2 ON t1.a = t3.c) ON t4.d = t2.b",
+ "SELECT t1.a, t2.b, t3.c, t4.d FROM "
+ "t4 JOIN (t1 JOIN t2 ON t1.a = t3.c) ON t4.d = t2.b, t3",
)
stmt = select(t1).select_from(t3).select_from(j2)
select(table1.alias("t"), text("foo.f"))
.where(text("foo.f = t.id"))
.select_from(text("(select f from bar where lala=heyhey) foo")),
- "SELECT t.myid, t.name, t.description, foo.f FROM mytable AS t, "
- "(select f from bar where lala=heyhey) foo WHERE foo.f = t.id",
+ "SELECT t.myid, t.name, t.description, foo.f FROM "
+ "(select f from bar where lala=heyhey) foo, "
+ "mytable AS t WHERE foo.f = t.id",
)
def test_expression_element_role(self):