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_4_0b2~131^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6125dfff91ca0093a00d78804917240617825c44;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 --- 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 1bea973188..55c17a1931 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -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): diff --git a/test/dialect/mysql/test_for_update.py b/test/dialect/mysql/test_for_update.py index d58c026157..2f51747f51 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 @@ -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") diff --git a/test/dialect/oracle/test_compiler.py b/test/dialect/oracle/test_compiler.py index 869cffe440..20c579f64e 100644 --- a/test/dialect/oracle/test_compiler.py +++ b/test/dialect/oracle/test_compiler.py @@ -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")) diff --git a/test/dialect/postgresql/test_compiler.py b/test/dialect/postgresql/test_compiler.py index a031c3df93..d478393f08 100644 --- a/test/dialect/postgresql/test_compiler.py +++ b/test/dialect/postgresql/test_compiler.py @@ -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(