From 667e2e6d62dcdff8e08eec6dd76c25815dfc03cd Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 22 Dec 2022 18:14:31 -0500 Subject: [PATCH] expand out Index if passed to "constraint" Fixed bug where the PostgreSQL :paramref:`_postgresql.OnConflictClause.constraint` parameter would accept an :class:`.Index` object, however would not expand this index out into its individual index expressions, instead rendering its name in an ON CONFLICT ON CONSTRAINT clause, which is not accepted by PostgreSQL; the "constraint name" form only accepts unique or exclude constraint names. The parameter continues to accept the index but now expands it out into its component expressions for the render. Fixes: #9023 Change-Id: I6baf243e26bfe578bf3f193c162dd7a623b6ede9 (cherry picked from commit 5cc18bb80077e98418b4a8066c0bc628209f3ada) --- doc/build/changelog/unreleased_14/9023.rst | 12 +++++++ lib/sqlalchemy/dialects/postgresql/dml.py | 2 +- test/dialect/postgresql/test_on_conflict.py | 39 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 doc/build/changelog/unreleased_14/9023.rst diff --git a/doc/build/changelog/unreleased_14/9023.rst b/doc/build/changelog/unreleased_14/9023.rst new file mode 100644 index 0000000000..d17a0cc983 --- /dev/null +++ b/doc/build/changelog/unreleased_14/9023.rst @@ -0,0 +1,12 @@ +.. change:: + :tags: bug, postgresql + :tickets: 9023 + + Fixed bug where the PostgreSQL + :paramref:`_postgresql.OnConflictClause.constraint` parameter would accept + an :class:`.Index` object, however would not expand this index out into its + individual index expressions, instead rendering its name in an ON CONFLICT + ON CONSTRAINT clause, which is not accepted by PostgreSQL; the "constraint + name" form only accepts unique or exclude constraint names. The parameter + continues to accept the index but now expands it out into its component + expressions for the render. diff --git a/lib/sqlalchemy/dialects/postgresql/dml.py b/lib/sqlalchemy/dialects/postgresql/dml.py index b483774db3..e7b126b3eb 100644 --- a/lib/sqlalchemy/dialects/postgresql/dml.py +++ b/lib/sqlalchemy/dialects/postgresql/dml.py @@ -188,7 +188,7 @@ class OnConflictClause(ClauseElement): if constraint is not None: if not isinstance(constraint, util.string_types) and isinstance( constraint, - (schema.Index, schema.Constraint, ext.ExcludeConstraint), + (schema.Constraint, ext.ExcludeConstraint), ): constraint = getattr(constraint, "name") or constraint diff --git a/test/dialect/postgresql/test_on_conflict.py b/test/dialect/postgresql/test_on_conflict.py index 508f691c51..ab46342f5f 100644 --- a/test/dialect/postgresql/test_on_conflict.py +++ b/test/dialect/postgresql/test_on_conflict.py @@ -675,6 +675,45 @@ class OnConflictTest(fixtures.TablesTest): [(1, "name1", "mail2@gmail.com", "unique_name")], ) + def test_on_conflict_do_update_constraint_can_be_index(self, connection): + """test #9023""" + + users = self.tables.users_xtra + + connection.execute( + insert(users), + dict( + id=1, + name="name1", + login_email="mail1@gmail.com", + lets_index_this="unique_name", + ), + ) + + i = insert(users) + i = i.on_conflict_do_update( + constraint=self.unique_partial_index, + set_=dict( + name=i.excluded.name, login_email=i.excluded.login_email + ), + ) + + connection.execute( + i, + [ + dict( + name="name1", + login_email="mail2@gmail.com", + lets_index_this="unique_name", + ) + ], + ) + + eq_( + connection.execute(users.select()).fetchall(), + [(1, "name1", "mail2@gmail.com", "unique_name")], + ) + def test_on_conflict_do_update_no_row_actually_affected(self, connection): users = self.tables.users_xtra -- 2.47.2