From: Mike Bayer Date: Tue, 13 Aug 2024 13:13:51 +0000 (-0400) Subject: omit mysql8 dupe key alias for INSERT..FROM SELECT X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=63b45202848de0cb3cfd41de130000355cbb88ef;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git omit mysql8 dupe key alias for INSERT..FROM SELECT Fixed issue in MySQL dialect where using INSERT..FROM SELECT in combination with ON DUPLICATE KEY UPDATE would erroneously render on MySQL 8 and above the "AS new" clause, leading to syntax failures. This clause is required on MySQL 8 to follow the VALUES clause if use of the "new" alias is present, however is not permitted to follow a FROM SELECT clause. Fixes: #11731 Change-Id: I254a3db4e9dccd9a76b11fdfe6e38a064ba0b5cf --- diff --git a/doc/build/changelog/unreleased_20/11731.rst b/doc/build/changelog/unreleased_20/11731.rst new file mode 100644 index 0000000000..34ab8b48c5 --- /dev/null +++ b/doc/build/changelog/unreleased_20/11731.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, mysql + :tickets: 11731 + + Fixed issue in MySQL dialect where using INSERT..FROM SELECT in combination + with ON DUPLICATE KEY UPDATE would erroneously render on MySQL 8 and above + the "AS new" clause, leading to syntax failures. This clause is required + on MySQL 8 to follow the VALUES clause if use of the "new" alias is + present, however is not permitted to follow a FROM SELECT clause. + diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index d5db02d278..aa99bf4d68 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1349,7 +1349,7 @@ class MySQLCompiler(compiler.SQLCompiler): clauses = [] - requires_mysql8_alias = ( + requires_mysql8_alias = statement.select is None and ( self.dialect._requires_alias_for_on_duplicate_key ) diff --git a/test/dialect/mysql/test_compiler.py b/test/dialect/mysql/test_compiler.py index 6712300aa4..189390659a 100644 --- a/test/dialect/mysql/test_compiler.py +++ b/test/dialect/mysql/test_compiler.py @@ -1127,6 +1127,31 @@ class InsertOnDuplicateTest(fixtures.TestBase, AssertsCompiledSQL): self.assert_compile(stmt, expected_sql, dialect=dialect) + @testing.variation("version", ["mysql8", "all_others"]) + def test_from_select(self, version: Variation): + stmt = insert(self.table).from_select( + ["id", "bar"], + select(self.table.c.id, literal("bar2")), + ) + stmt = stmt.on_duplicate_key_update( + bar=stmt.inserted.bar, baz=stmt.inserted.baz + ) + + expected_sql = ( + "INSERT INTO foos (id, bar) SELECT foos.id, %s AS anon_1 " + "FROM foos " + "ON DUPLICATE KEY UPDATE bar = VALUES(bar), baz = VALUES(baz)" + ) + if version.all_others: + dialect = None + elif version.mysql8: + dialect = mysql.dialect() + dialect._requires_alias_for_on_duplicate_key = True + else: + version.fail() + + self.assert_compile(stmt, expected_sql, dialect=dialect) + def test_from_literal(self): stmt = insert(self.table).values( [{"id": 1, "bar": "ab"}, {"id": 2, "bar": "b"}] diff --git a/test/dialect/mysql/test_on_duplicate.py b/test/dialect/mysql/test_on_duplicate.py index 5a4e6ca8d5..35aebb470c 100644 --- a/test/dialect/mysql/test_on_duplicate.py +++ b/test/dialect/mysql/test_on_duplicate.py @@ -3,6 +3,8 @@ from sqlalchemy import Column from sqlalchemy import exc from sqlalchemy import func from sqlalchemy import Integer +from sqlalchemy import literal +from sqlalchemy import select from sqlalchemy import String from sqlalchemy import Table from sqlalchemy.dialects.mysql import insert @@ -63,6 +65,22 @@ class OnDuplicateTest(fixtures.TablesTest): [(1, "ab", "bz", False)], ) + def test_on_duplicate_key_from_select(self, connection): + foos = self.tables.foos + conn = connection + conn.execute(insert(foos).values(dict(id=1, bar="b", baz="bz"))) + stmt = insert(foos).from_select( + ["id", "bar", "baz"], + select(foos.c.id, literal("bar2"), literal("baz2")), + ) + stmt = stmt.on_duplicate_key_update(bar=stmt.inserted.bar) + + conn.execute(stmt) + eq_( + conn.execute(foos.select().where(foos.c.id == 1)).fetchall(), + [(1, "bar2", "bz", False)], + ) + def test_on_duplicate_key_update_singlerow(self, connection): foos = self.tables.foos conn = connection