From 71693c94d52612a5e88128575ff308ee4a923c00 Mon Sep 17 00:00:00 2001 From: Federico Caselli Date: Wed, 22 Feb 2023 21:57:19 +0100 Subject: [PATCH] ExcludeConstraint literal_compile ExcludeConstraint correctly uses literal compile when compiling expression ddl. Fixes: #9349 Change-Id: I11a994ac46556a972afc696a2baad7ddbdd3de97 --- doc/build/changelog/unreleased_20/9349.rst | 6 ++++++ lib/sqlalchemy/dialects/postgresql/base.py | 3 ++- lib/sqlalchemy/dialects/postgresql/ext.py | 15 +++++++-------- test/dialect/postgresql/test_compiler.py | 19 +++++++++++++++++-- 4 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 doc/build/changelog/unreleased_20/9349.rst diff --git a/doc/build/changelog/unreleased_20/9349.rst b/doc/build/changelog/unreleased_20/9349.rst new file mode 100644 index 0000000000..957ac56871 --- /dev/null +++ b/doc/build/changelog/unreleased_20/9349.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: bug, schema, postgresql + :tickets: 9332 + + ExcludeConstraint correctly uses literal compile when compiling + expression ddl. diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 255c72042e..3ba1038026 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -2350,8 +2350,9 @@ class PGDDLCompiler(compiler.DDLCompiler): constraint ) elements = [] + kw["include_table"] = False + kw["literal_binds"] = True for expr, name, op in constraint._render_exprs: - kw["include_table"] = False exclude_element = self.sql_compiler.process(expr, **kw) + ( (" " + constraint.ops[expr.key]) if hasattr(expr, "key") and expr.key in constraint.ops diff --git a/lib/sqlalchemy/dialects/postgresql/ext.py b/lib/sqlalchemy/dialects/postgresql/ext.py index 0f5efb1de9..22604955dc 100644 --- a/lib/sqlalchemy/dialects/postgresql/ext.py +++ b/lib/sqlalchemy/dialects/postgresql/ext.py @@ -164,16 +164,15 @@ class ExcludeConstraint(ColumnCollectionConstraint): :param \*elements: A sequence of two tuples of the form ``(column, operator)`` where - "column" is a SQL expression element or a raw SQL string, most - typically a :class:`_schema.Column` object, - and "operator" is a string - containing the operator to use. In order to specify a column name - when a :class:`_schema.Column` object is not available, - while ensuring + "column" is a SQL expression element or the name of a column as + string, most typically a :class:`_schema.Column` object, + and "operator" is a string containing the operator to use. + In order to specify a column name when a :class:`_schema.Column` + object is not available, while ensuring that any necessary quoting rules take effect, an ad-hoc :class:`_schema.Column` or :func:`_expression.column` - object should be - used. + object should be used. ``column`` may also be a string SQL + expression when passed as :func:`_expression.literal_column` :param name: Optional, the in-database name of this constraint. diff --git a/test/dialect/postgresql/test_compiler.py b/test/dialect/postgresql/test_compiler.py index 080cfb767d..e8bead008f 100644 --- a/test/dialect/postgresql/test_compiler.py +++ b/test/dialect/postgresql/test_compiler.py @@ -1034,7 +1034,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): tbl.append_constraint(cons) self.assert_compile( schema.AddConstraint(cons), - "ALTER TABLE testtbl ADD EXCLUDE USING gist " "(room WITH =)", + "ALTER TABLE testtbl ADD EXCLUDE USING gist (room WITH =)", dialect=postgresql.dialect(), ) @@ -1134,7 +1134,7 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): tbl.append_constraint(cons_copy) self.assert_compile( schema.AddConstraint(cons_copy), - "ALTER TABLE testtbl ADD EXCLUDE USING gist " "(room WITH =)", + "ALTER TABLE testtbl ADD EXCLUDE USING gist (room WITH =)", ) def test_exclude_constraint_copy_where_using(self): @@ -1243,6 +1243,21 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL): dialect=postgresql.dialect(), ) + def test_exclude_constraint_literal_binds(self): + m = MetaData() + tbl = Table("foo", m, Column("x", Integer), Column("y", Integer)) + cons = ExcludeConstraint( + (func.power(tbl.c.x, 42), "="), + (func.int8range(column("x"), "y"), "&&"), + ) + tbl.append_constraint(cons) + self.assert_compile( + schema.AddConstraint(cons), + "ALTER TABLE foo ADD EXCLUDE USING gist " + "(power(x, 42) WITH =, int8range(x, 'y') WITH &&)", + dialect=postgresql.dialect(), + ) + def test_substring(self): self.assert_compile( func.substring("abc", 1, 2), -- 2.47.2