From 4d08f17bab70e7eeb013d76045732f218159a277 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lo=C3=AFc=20Simon?= Date: Wed, 4 Feb 2026 22:42:10 +0100 Subject: [PATCH] PostgreSQL Insert.on_conflict_do_update: respect compile_kwargs Fixes: #13110 --- doc/build/changelog/unreleased_21/13110.rst | 7 ++++++ lib/sqlalchemy/dialects/postgresql/base.py | 15 ++++++++----- test/dialect/postgresql/test_compiler.py | 25 +++++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 doc/build/changelog/unreleased_21/13110.rst diff --git a/doc/build/changelog/unreleased_21/13110.rst b/doc/build/changelog/unreleased_21/13110.rst new file mode 100644 index 0000000000..c37b8e12fc --- /dev/null +++ b/doc/build/changelog/unreleased_21/13110.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: bug, postgresql + :tickets: 13110 + + Fixed issue where :meth:`_postgresql.Insert.on_conflict_do_update` + parameters were not respecting compilation options such as + ``literal_binds=True``. Pull request courtesy Loïc Simon. diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index e54fd0a658..be50b41d96 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -2370,10 +2370,11 @@ class PGCompiler(compiler.SQLCompiler): for c in clause.inferred_target_elements ) if clause.inferred_target_whereclause is not None: + whereclause_kw = dict(kw) + whereclause_kw.update(include_table=False, use_schema=False) target_text += " WHERE %s" % self.process( clause.inferred_target_whereclause, - include_table=False, - use_schema=False, + **whereclause_kw, ) else: target_text = "" @@ -2400,6 +2401,8 @@ class PGCompiler(compiler.SQLCompiler): insert_statement = self.stack[-1]["selectable"] cols = insert_statement.table.c + set_kw = dict(kw) + set_kw.update(use_schema=False) for c in cols: col_key = c.key @@ -2416,7 +2419,7 @@ class PGCompiler(compiler.SQLCompiler): and value.type._isnull ): value = value._with_binary_element_type(c.type) - value_text = self.process(value.self_group(), use_schema=False) + value_text = self.process(value.self_group(), **set_kw) key_text = self.preparer.quote(c.name) action_set_ops.append("%s = %s" % (key_text, value_text)) @@ -2439,14 +2442,16 @@ class PGCompiler(compiler.SQLCompiler): ) value_text = self.process( coercions.expect(roles.ExpressionElementRole, v), - use_schema=False, + **set_kw, ) action_set_ops.append("%s = %s" % (key_text, value_text)) action_text = ", ".join(action_set_ops) if clause.update_whereclause is not None: + where_kw = dict(kw) + where_kw.update(include_table=True, use_schema=False) action_text += " WHERE %s" % self.process( - clause.update_whereclause, include_table=True, use_schema=False + clause.update_whereclause, **where_kw ) return "ON CONFLICT %s DO UPDATE SET %s" % (target_text, action_text) diff --git a/test/dialect/postgresql/test_compiler.py b/test/dialect/postgresql/test_compiler.py index 45db034042..5329424101 100644 --- a/test/dialect/postgresql/test_compiler.py +++ b/test/dialect/postgresql/test_compiler.py @@ -4103,6 +4103,31 @@ class InsertOnConflictTest( }, ) + def test_on_conflict_literal_binds(self): + i = insert(self.table_with_metadata).values(myid=1, name="foo") + i = i.on_conflict_do_update( + index_elements=["myid"], + set_=OrderedDict( + [("name", "I'm a name"), ("other_param", literal("this too"))] + ), + where=self.table_with_metadata.c.name == "foo", + index_where=self.goofy_index.dialect_options["postgresql"][ + "where" + ], + ) + with expect_warnings( + "Additional column names not matching any column keys" + ): + self.assert_compile( + i, + "INSERT INTO mytable (myid, name) VALUES (1, 'foo')" + " ON CONFLICT (myid) WHERE name > 'm' DO UPDATE" + " SET name = 'I''m a name', other_param = 'this too'" + " WHERE mytable.name = 'foo'", + {}, + literal_binds=True, + ) + class DistinctOnTest( fixtures.MappedTest, -- 2.47.3