From: Mike Bayer Date: Thu, 26 Nov 2020 17:10:25 +0000 (-0500) Subject: Don't discard leftovers from surface_selectables X-Git-Tag: rel_1_3_21~10^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e1a2610e805a178c4d1ab7505425df20d51b4202;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Don't discard leftovers from surface_selectables 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) --- diff --git a/doc/build/changelog/unreleased_13/5729.rst b/doc/build/changelog/unreleased_13/5729.rst new file mode 100644 index 0000000000..e95cfa8b2b --- /dev/null +++ b/doc/build/changelog/unreleased_13/5729.rst @@ -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. + diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 73a5b84b00..f5437f3dba 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -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): diff --git a/test/dialect/mysql/test_for_update.py b/test/dialect/mysql/test_for_update.py index 604065c0ba..bb8bfafb6e 100644 --- a/test/dialect/mysql/test_for_update.py +++ b/test/dialect/mysql/test_for_update.py @@ -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",) diff --git a/test/dialect/oracle/test_compiler.py b/test/dialect/oracle/test_compiler.py index ab7f471980..d7a0b5401e 100644 --- a/test/dialect/oracle/test_compiler.py +++ b/test/dialect/oracle/test_compiler.py @@ -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")) diff --git a/test/dialect/postgresql/test_compiler.py b/test/dialect/postgresql/test_compiler.py index 98b32e8312..411b9547a2 100644 --- a/test/dialect/postgresql/test_compiler.py +++ b/test/dialect/postgresql/test_compiler.py @@ -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(