]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
render select froms first
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 20 May 2022 19:56:54 +0000 (15:56 -0400)
committermike bayer <mike_mp@zzzcomputing.com>
Sun, 22 May 2022 19:24:18 +0000 (19:24 +0000)
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.

Fixes: #7888
Change-Id: I740f262a3841f829239011120a59b5e58452db5b

17 files changed:
doc/build/changelog/unreleased_20/7888.rst [new file with mode: 0644]
lib/sqlalchemy/sql/selectable.py
test/ext/test_associationproxy.py
test/orm/inheritance/test_polymorphic_rel.py
test/orm/inheritance/test_relationship.py
test/orm/test_core_compilation.py
test/orm/test_deprecations.py
test/orm/test_dynamic.py
test/orm/test_froms.py
test/orm/test_joins.py
test/orm/test_query.py
test/orm/test_relationships.py
test/sql/test_compiler.py
test/sql/test_external_traversal.py
test/sql/test_functions.py
test/sql/test_selectable.py
test/sql/test_text.py

diff --git a/doc/build/changelog/unreleased_20/7888.rst b/doc/build/changelog/unreleased_20/7888.rst
new file mode 100644 (file)
index 0000000..e5b4f3f
--- /dev/null
@@ -0,0 +1,17 @@
+.. 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.
index 733def3779759cdd72795f28ece80928bfc371cc..53dcf51c773db1739215d6bc08c8b66208009fbf 100644 (file)
@@ -4502,6 +4502,7 @@ class SelectState(util.MemoizedSlots, CompileState):
 
         return self._normalize_froms(
             itertools.chain(
+                self.from_clauses,
                 itertools.chain.from_iterable(
                     [
                         element._from_objects
@@ -4514,7 +4515,6 @@ class SelectState(util.MemoizedSlots, CompileState):
                         for element in statement._where_criteria
                     ]
                 ),
-                self.from_clauses,
             ),
             check_statement=statement,
             ambiguous_table_name_map=ambiguous_table_name_map,
index a99b23338ff3dda97d6f806933ca4f365f129124..67b0c93e0f2f523b345d059953557a5749175a9f 100644 (file)
@@ -3292,7 +3292,7 @@ class ProxyOfSynonymTest(AssertsCompiledSQL, fixtures.DeclarativeMappedTest):
 
         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)",
         )
 
@@ -3340,7 +3340,7 @@ class SynonymOfProxyTest(AssertsCompiledSQL, fixtures.DeclarativeMappedTest):
 
         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)",
         )
 
@@ -3471,7 +3471,7 @@ class ProxyHybridTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
 
         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)",
         )
 
index 5eac274cf4cacaf336e3dab32345ccc4ac2a6d7b..096498d305a2a0365d630d84b58b81faf857f029 100644 (file)
@@ -2206,7 +2206,14 @@ class PolymorphicPolymorphicTest(
             "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, "
@@ -2222,14 +2229,7 @@ class PolymorphicPolymorphicTest(
             "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",
         )
index a686309956fcbf7adb6db81a7e54468e19f8da83..5f68f80341f1fe651ea17abc45f175bd99c7bf45 100644 (file)
@@ -858,15 +858,15 @@ class SelfReferentialM2MTest(fixtures.MappedTest, AssertsCompiledSQL):
                 .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
@@ -876,14 +876,14 @@ class SelfReferentialM2MTest(fixtures.MappedTest, AssertsCompiledSQL):
             .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):
@@ -920,14 +920,15 @@ class SelfReferentialM2MTest(fixtures.MappedTest, AssertsCompiledSQL):
             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):
index 058e1735b634e3423ecfd21bca5eed51605cbdc2..631509ca0ba4ff88c5f99213a99ebb6a7743ded7 100644 (file)
@@ -276,6 +276,34 @@ class SelectableTest(QueryTest, AssertsCompiledSQL):
         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"
index a5506a2967de260427ade26b246f80de3ad2a755..8d396b42e0954deba296811b28a1e6f2df66dfcc 100644 (file)
@@ -476,7 +476,7 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
 
         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 "
index 0e2cb34d281a4ac4305ab041d59d057a88e5a21a..d004998c8071a578681cc2887043c6ffec64bdd3 100644 (file)
@@ -675,8 +675,8 @@ class DynamicTest(_DynamicFixture, _fixtures.FixtureTest, AssertsCompiledSQL):
             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},
@@ -727,8 +727,9 @@ class DynamicTest(_DynamicFixture, _fixtures.FixtureTest, AssertsCompiledSQL):
             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,
index 01a8698a4a88dedabe9ba8e3815562c2dea513a7..30a4c54dd014122dbb55c2fdbf7ba32167fbcb4e 100644 (file)
@@ -349,8 +349,9 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
             .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(
@@ -771,8 +772,9 @@ class EntityFromSubqueryTest(QueryTest, AssertsCompiledSQL):
 
         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):
@@ -863,8 +865,8 @@ class ColumnAccessTest(QueryTest, AssertsCompiledSQL):
         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):
@@ -2818,10 +2820,10 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             .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",
         )
 
@@ -2948,9 +2950,10 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         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]},
         )
index 43a34eae4c00eab2f0ed9b9cbc5c7a4bdeab8122..3120a160dacdfa3870c5f59ae9e0e3c57cd9b3d4 100644 (file)
@@ -881,8 +881,8 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             ),
             "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,
         )
 
@@ -1041,8 +1041,9 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             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)
@@ -1185,9 +1186,9 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             "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
@@ -1199,8 +1200,8 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             "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):
index d9013b2c4a186767ead1a925e6148f4fa7834bc4..f621f264d86187f3ec5cbc6b355bd670ae015a71 100644 (file)
@@ -3851,7 +3851,7 @@ class HasAnyTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
         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)",
         )
 
@@ -3873,7 +3873,7 @@ class HasAnyTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
             "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)",
         )
 
@@ -3913,7 +3913,7 @@ class HasAnyTest(fixtures.DeclarativeMappedTest, AssertsCompiledSQL):
             "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)",
         )
 
index b483bf6953d1fc0167df248aba786f839bef4222..fd4230e150c322020768bc9c81de8b94668c8627 100644 (file)
@@ -6549,9 +6549,9 @@ class SecondaryIncludesLocalColsTest(fixtures.MappedTest):
         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=[],
             )
         )
index d9f137349c75a18fcdbb2c7c9ae8d08ba59c2fe5..4e8e2ac139a361d1f5c89052aae338dda9d48a8f 100644 (file)
@@ -550,6 +550,91 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
             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
@@ -1265,11 +1350,9 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
         # 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(
index 30d25be90dcfaf0f0a54afa4b6f2d086f9d4628c..13116225cbbafcfb7d00a47490fb26d806be2a90 100644 (file)
@@ -1282,7 +1282,7 @@ class ClauseTest(fixtures.TestBase, AssertsCompiledSQL):
             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)
@@ -1290,7 +1290,7 @@ class ClauseTest(fixtures.TestBase, AssertsCompiledSQL):
             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)",
         )
 
@@ -2645,7 +2645,7 @@ class SelectTest(fixtures.TestBase, AssertsCompiledSQL):
         self.assert_compile(
             select_copy,
             "SELECT table1.col1, table1.col2, "
-            "table1.col3 FROM table1, table2",
+            "table1.col3 FROM table2, table1",
         )
 
         self.assert_compile(
index 693a41c3acc06723014d67d18120c699bb72c4dc..6c00660ffea9658c1979804ebc9421450f5bffed 100644 (file)
@@ -1671,11 +1671,11 @@ class TableValuedCompileTest(fixtures.TestBase, AssertsCompiledSQL):
         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",
         )
 
index 7fa39825c4911df1991d28965fddfff403bfd55b..d05d7ad8b1e0dc93d250413501dc04fafadc671d 100644 (file)
@@ -816,7 +816,7 @@ class SelectableTest(
             [table1.join(table2)],
             [table1],
         ),
-        ([table1], [table2], [table1, table2], [table1]),
+        ([table1], [table2], [table2, table1], [table1]),
         (
             [table1.c.col1, table2.c.col1],
             [],
@@ -924,8 +924,8 @@ class SelectableTest(
         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)
index 0f645a2d2315a5ebf9ba48ec9a1b3a3415d90d7e..6acc97c9caad6093cae8295323fae31d69fb6e6a 100644 (file)
@@ -179,8 +179,9 @@ class SelectCompositionTest(fixtures.TestBase, AssertsCompiledSQL):
             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):