]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Don't discard leftovers from surface_selectables
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 26 Nov 2020 17:10:25 +0000 (12:10 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 26 Nov 2020 17:21:22 +0000 (12:21 -0500)
Fixed regression introduced in 1.3.2 for the PostgreSQL dialect, also
copied out to the MySQL dialect's feature in 1.3.18, where usage of a non
:class:`_schema.Table` construct such as :func:`_sql.text` as the argument
to :paramref:`_sql.Select.with_for_update.of` would fail to be accommodated
correctly within the PostgreSQL or MySQL compilers.

Fixes: #5729
Change-Id: I265bcc171f0eb865ac3910ee805b162f3b70e2c1

doc/build/changelog/unreleased_13/5729.rst [new file with mode: 0644]
lib/sqlalchemy/sql/util.py
test/dialect/mysql/test_for_update.py
test/dialect/oracle/test_compiler.py
test/dialect/postgresql/test_compiler.py

diff --git a/doc/build/changelog/unreleased_13/5729.rst b/doc/build/changelog/unreleased_13/5729.rst
new file mode 100644 (file)
index 0000000..e95cfa8
--- /dev/null
@@ -0,0 +1,11 @@
+.. change::
+    :tags: bug, postgresql, mysql
+    :tickets: 5729
+    :versions: 1.4.0b2
+
+    Fixed regression introduced in 1.3.2 for the PostgreSQL dialect, also
+    copied out to the MySQL dialect's feature in 1.3.18, where usage of a non
+    :class:`_schema.Table` construct such as :func:`_sql.text` as the argument
+    to :paramref:`_sql.Select.with_for_update.of` would fail to be accommodated
+    correctly within the PostgreSQL or MySQL compilers.
+
index 1bea9731886cceee3ff9fad1939fdeefe9400013..55c17a19318a7350690e0526e7f34327892257ef 100644 (file)
@@ -401,7 +401,12 @@ def surface_selectables_only(clause):
         elif isinstance(elem, FromGrouping):
             stack.append(elem.element)
         elif isinstance(elem, ColumnClause):
-            stack.append(elem.table)
+            if elem.table is not None:
+                stack.append(elem.table)
+            else:
+                yield elem
+        elif elem is not None:
+            yield elem
 
 
 def extract_first_column_annotation(column, annotation_name):
index d58c026157825db64eee3101f29ab816dbfa202b..2f51747f514c905a5dc46a570dfd80bd02ae3731 100644 (file)
@@ -9,8 +9,10 @@ from sqlalchemy import Column
 from sqlalchemy import exc
 from sqlalchemy import ForeignKey
 from sqlalchemy import Integer
+from sqlalchemy import literal_column
 from sqlalchemy import Table
 from sqlalchemy import testing
+from sqlalchemy import text
 from sqlalchemy import update
 from sqlalchemy.dialects.mysql import base as mysql
 from sqlalchemy.exc import ProgrammingError
@@ -367,6 +369,27 @@ class MySQLForUpdateCompileTest(fixtures.TestBase, AssertsCompiledSQL):
             dialect=self.for_update_of_dialect,
         )
 
+    def test_for_update_textual_of(self):
+        self.assert_compile(
+            self.table1.select(self.table1.c.myid == 7).with_for_update(
+                of=text("mytable")
+            ),
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable WHERE mytable.myid = %s "
+            "FOR UPDATE OF mytable",
+            dialect=self.for_update_of_dialect,
+        )
+
+        self.assert_compile(
+            self.table1.select(self.table1.c.myid == 7).with_for_update(
+                of=literal_column("mytable")
+            ),
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable WHERE mytable.myid = %s "
+            "FOR UPDATE OF mytable",
+            dialect=self.for_update_of_dialect,
+        )
+
 
 class SkipLockedTest(fixtures.TablesTest):
     __only_on__ = ("mysql", "mariadb")
index 869cffe440ddaa0043f572604b67f403460c742b..20c579f64ec4798cd3e2814b2d3712f6ba9d43de 100644 (file)
@@ -448,6 +448,24 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
             "mytable_1.myid, mytable_1.name",
         )
 
+        # ensure of=text() for of works
+        self.assert_compile(
+            table1.select(table1.c.myid == 7).with_for_update(
+                read=True, of=text("table1")
+            ),
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE OF table1",
+        )
+
+        # ensure of=literal_column() for of works
+        self.assert_compile(
+            table1.select(table1.c.myid == 7).with_for_update(
+                read=True, of=literal_column("table1")
+            ),
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable WHERE mytable.myid = :myid_1 FOR UPDATE OF table1",
+        )
+
     def test_for_update_of_w_limit_adaption_col_present(self):
         table1 = table("mytable", column("myid"), column("name"))
 
index a031c3df93ea417f681ddc6c62dbb83b56fbb1d5..d478393f08930814e01c502dbdb0808784be3249 100644 (file)
@@ -1222,6 +1222,26 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
             "FOR UPDATE OF mytable_1, table2",
         )
 
+        # ensure of=text() for of works
+        self.assert_compile(
+            table1.select(table1.c.myid == 7).with_for_update(
+                of=text("table1")
+            ),
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable WHERE mytable.myid = %(myid_1)s "
+            "FOR UPDATE OF table1",
+        )
+
+        # ensure literal_column of works
+        self.assert_compile(
+            table1.select(table1.c.myid == 7).with_for_update(
+                of=literal_column("table1")
+            ),
+            "SELECT mytable.myid, mytable.name, mytable.description "
+            "FROM mytable WHERE mytable.myid = %(myid_1)s "
+            "FOR UPDATE OF table1",
+        )
+
     def test_for_update_with_schema(self):
         m = MetaData()
         table1 = Table(