--- /dev/null
+.. 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.
+
+
+.. change::
+ :tags: bug, sqlite
+ :tickets: 13110
+
+ Fixed issue where :meth:`_sqlite.Insert.on_conflict_do_update`
+ parameters were not respecting compilation options such as
+ ``literal_binds=True``. Pull request courtesy Loïc Simon.
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 = ""
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
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))
)
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)
for c in clause.inferred_target_elements
)
if clause.inferred_target_whereclause is not None:
- target_text += " WHERE %s" % self.process(
- clause.inferred_target_whereclause,
+ whereclause_kw = dict(kw)
+ whereclause_kw.update(
include_table=False,
use_schema=False,
literal_execute=True,
)
+ target_text += " WHERE %s" % self.process(
+ clause.inferred_target_whereclause,
+ **whereclause_kw,
+ )
else:
target_text = ""
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
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))
key_text = (
self.preparer.quote(k)
if isinstance(k, str)
- else self.process(k, use_schema=False)
+ else self.process(k, **set_kw)
)
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)
+import contextlib
import random
import re
},
)
+ @testing.variation(
+ "path", ["unknown_columns", "whereclause", "indexwhere"]
+ )
+ def test_on_conflict_literal_binds(self, path: testing.Variation):
+ """test for #13110"""
+
+ i = insert(self.table_with_metadata).values(myid=1, name="foo")
+
+ if path.unknown_columns:
+ i = i.on_conflict_do_update(
+ index_elements=["myid"],
+ set_=OrderedDict(
+ [
+ ("name", "I'm a name"),
+ ("other_param", literal("this too")),
+ ]
+ ),
+ )
+ expected = (
+ "ON CONFLICT (myid) DO UPDATE SET name = "
+ "'I''m a name', other_param = 'this too'"
+ )
+ warnings = expect_warnings(
+ "Additional column names not matching any column keys"
+ )
+ elif path.whereclause:
+ i = i.on_conflict_do_update(
+ index_elements=["myid"],
+ set_={"name": "I'm a name"},
+ where=self.table_with_metadata.c.name == "foo",
+ )
+ expected = (
+ "ON CONFLICT (myid) DO UPDATE SET name = "
+ "'I''m a name' WHERE mytable.name = 'foo'"
+ )
+ warnings = contextlib.nullcontext()
+ elif path.indexwhere:
+ i = i.on_conflict_do_update(
+ index_elements=["myid"],
+ set_={"name": "I'm a name"},
+ index_where=self.goofy_index.dialect_options["postgresql"][
+ "where"
+ ],
+ )
+ warnings = contextlib.nullcontext()
+ expected = (
+ "ON CONFLICT (myid) WHERE name > 'm' "
+ "DO UPDATE SET name = 'I''m a name'"
+ )
+ else:
+ path.fail()
+
+ with warnings:
+ self.assert_compile(
+ i,
+ "INSERT INTO mytable (myid, name) VALUES (1, 'foo')"
+ f" {expected}",
+ {},
+ literal_binds=True,
+ )
+
class DistinctOnTest(
fixtures.MappedTest,
"""SQLite-specific tests."""
+from collections import OrderedDict
+import contextlib
import random
from sqlalchemy import and_
"SET name = excluded.name, login_email = excluded.login_email",
)
+ @testing.variation(
+ "path", ["unknown_columns", "whereclause", "indexwhere"]
+ )
+ def test_on_conflict_literal_binds(self, path: testing.Variation):
+ """test for #13110"""
+
+ metadata = MetaData()
+ table_with_metadata = Table(
+ "mytable",
+ metadata,
+ Column("myid", Integer, primary_key=True),
+ Column("name", String(128)),
+ )
+ goofy_index = Index(
+ "goofy_index",
+ table_with_metadata.c.name,
+ sqlite_where=table_with_metadata.c.name > "m",
+ )
+
+ i = insert(table_with_metadata).values(myid=1, name="foo")
+
+ if path.unknown_columns:
+ i = i.on_conflict_do_update(
+ index_elements=["myid"],
+ set_=OrderedDict(
+ [
+ ("name", "I'm a name"),
+ ("other_param", literal("this too")),
+ ]
+ ),
+ )
+ expected = (
+ "ON CONFLICT (myid) DO UPDATE SET name = "
+ "'I''m a name', other_param = 'this too'"
+ )
+ warnings = testing.expect_warnings(
+ "Additional column names not matching any column keys"
+ )
+ elif path.whereclause:
+ i = i.on_conflict_do_update(
+ index_elements=["myid"],
+ set_={"name": "I'm a name"},
+ where=table_with_metadata.c.name == "foo",
+ )
+ expected = (
+ "ON CONFLICT (myid) DO UPDATE SET name = "
+ "'I''m a name' WHERE mytable.name = 'foo'"
+ )
+ warnings = contextlib.nullcontext()
+ elif path.indexwhere:
+ i = i.on_conflict_do_update(
+ index_elements=["myid"],
+ set_={"name": "I'm a name"},
+ index_where=goofy_index.dialect_options["sqlite"]["where"],
+ )
+ warnings = contextlib.nullcontext()
+ expected = (
+ "ON CONFLICT (myid) WHERE name > 'm' "
+ "DO UPDATE SET name = 'I''m a name'"
+ )
+ else:
+ path.fail()
+
+ with warnings:
+ self.assert_compile(
+ i,
+ "INSERT INTO mytable (myid, name) VALUES (1, 'foo')"
+ f" {expected}",
+ {},
+ literal_binds=True,
+ )
+
@testing.fixture
def users(self):
metadata = MetaData()