]> 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:23:25 +0000 (12:23 -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
(cherry picked from commit 6125dfff91ca0093a00d78804917240617825c44)

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 73a5b84b00fed147cec8512f14acb95d792bd013..f5437f3dba322e117c9600a1e729d841fddd924a 100644 (file)
@@ -386,7 +386,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 surface_column_elements(clause, include_scalar_selects=True):
index 604065c0ba610aa4fbd0de95f711e2c3e49346df..bb8bfafb6eb340e2a14e4ebe08e81823dd3f27e0 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
@@ -362,6 +364,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",)
index ab7f4719807d4b3198bbac4ec9203fb8eaea2554..d7a0b5401ea74ca7a58925d3165c71a8d752760b 100644 (file)
@@ -379,6 +379,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 98b32e8312a364eb44a87b8cd941695825bc1fa4..411b9547a29fc3fed35ebeca39b2e81e6fdeb36e 100644 (file)
@@ -1182,6 +1182,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(