summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2023-03-07 01:11:33 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2023-03-07 01:11:33 +0000
commit370d14b131bf6f595097e37e88f13cf52604c2f4 (patch)
treee092d05792a55bc786a21e50a01faa4903b5f451
parent3cf19f16631e54108e5f519348cba14521b7433e (diff)
parent6f8d9af59aa431aca3be851066c729c52b12a7a2 (diff)
downloadalembic-370d14b131bf6f595097e37e88f13cf52604c2f4.tar.gz
Merge "improve autogen rendering for PG ExcludeConstraint" into main
-rw-r--r--alembic/ddl/postgresql.py15
-rw-r--r--docs/build/unreleased/1184.rst10
-rw-r--r--tests/test_postgresql.py59
3 files changed, 79 insertions, 5 deletions
diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py
index 994d7cb..4ffc2eb 100644
--- a/alembic/ddl/postgresql.py
+++ b/alembic/ddl/postgresql.py
@@ -22,6 +22,7 @@ from sqlalchemy.dialects.postgresql import ExcludeConstraint
from sqlalchemy.dialects.postgresql import INTEGER
from sqlalchemy.schema import CreateIndex
from sqlalchemy.sql.elements import ColumnClause
+from sqlalchemy.sql.elements import TextClause
from sqlalchemy.types import NULLTYPE
from .base import alter_column
@@ -650,7 +651,7 @@ def _exclude_constraint(
args = [
"(%s, %r)"
% (_render_potential_column(sqltext, autogen_context), opstring)
- for sqltext, name, opstring in constraint._render_exprs # type:ignore[attr-defined] # noqa
+ for sqltext, name, opstring in constraint._render_exprs
]
if constraint.where is not None:
args.append(
@@ -667,17 +668,21 @@ def _exclude_constraint(
def _render_potential_column(
- value: Union[ColumnClause, Column], autogen_context: AutogenContext
+ value: Union[ColumnClause, Column, TextClause],
+ autogen_context: AutogenContext,
) -> str:
if isinstance(value, ColumnClause):
- template = "%(prefix)scolumn(%(name)r)"
+ if value.is_literal:
+ # like literal_column("int8range(from, to)") in ExcludeConstraint
+ template = "%(prefix)sliteral_column(%(name)r)"
+ else:
+ template = "%(prefix)scolumn(%(name)r)"
return template % {
"prefix": render._sqlalchemy_autogenerate_prefix(autogen_context),
"name": value.name,
}
-
else:
return render._render_potential_expr(
- value, autogen_context, wrap_in_text=False
+ value, autogen_context, wrap_in_text=isinstance(value, TextClause)
)
diff --git a/docs/build/unreleased/1184.rst b/docs/build/unreleased/1184.rst
new file mode 100644
index 0000000..3d26f3e
--- /dev/null
+++ b/docs/build/unreleased/1184.rst
@@ -0,0 +1,10 @@
+.. change::
+ :tags: bug, postgresql
+ :tickets: 1184
+
+ Fixed issue regarding PostgreSQL :class:`.ExcludeConstraint`, where
+ constraint elements which made use of :func:`.literal_column` could not be
+ rendered for autogenerate. Additionally, using SQLAlchemy 2.0.5 or greater,
+ :func:`.text()` constructs are also supported within PostgreSQL
+ :class:`.ExcludeConstraint` objects for autogenerate render. Pull request
+ courtesy Jan Katins. \ No newline at end of file
diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py
index a8c284d..18f50ce 100644
--- a/tests/test_postgresql.py
+++ b/tests/test_postgresql.py
@@ -28,6 +28,7 @@ from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.sql import column
from sqlalchemy.sql import false
from sqlalchemy.sql import table
+from sqlalchemy.sql.expression import literal_column
from alembic import autogenerate
from alembic import command
@@ -1156,6 +1157,64 @@ class PostgresqlAutogenRenderTest(TestBase):
"name='TExclX'))",
)
+ def test_inline_exclude_constraint_literal_column(self):
+ """test for #1184"""
+
+ autogen_context = self.autogen_context
+
+ m = MetaData()
+ t = Table(
+ "TTable",
+ m,
+ Column("id", String()),
+ ExcludeConstraint(
+ (literal_column("id + 2"), "="), name="TExclID", using="gist"
+ ),
+ )
+
+ op_obj = ops.CreateTableOp.from_table(t)
+
+ eq_ignore_whitespace(
+ autogenerate.render_op_text(autogen_context, op_obj),
+ "op.create_table('TTable',sa.Column('id', sa.String(), "
+ "nullable=True),"
+ "postgresql.ExcludeConstraint((sa.literal_column('id + 2'), '='), "
+ "using='gist', "
+ "name='TExclID'))",
+ )
+
+ @config.requirements.sqlalchemy_2
+ def test_inline_exclude_constraint_text(self):
+ """test for #1184.
+
+ Requires SQLAlchemy 2.0.5 due to issue
+ https://github.com/sqlalchemy/sqlalchemy/issues/9401
+
+ """
+
+ autogen_context = self.autogen_context
+
+ m = MetaData()
+ t = Table(
+ "TTable",
+ m,
+ Column("id", String()),
+ ExcludeConstraint(
+ (text("id + 2"), "="), name="TExclID", using="gist"
+ ),
+ )
+
+ op_obj = ops.CreateTableOp.from_table(t)
+
+ eq_ignore_whitespace(
+ autogenerate.render_op_text(autogen_context, op_obj),
+ "op.create_table('TTable',sa.Column('id', sa.String(), "
+ "nullable=True),"
+ "postgresql.ExcludeConstraint((sa.text('id + 2'), '='), "
+ "using='gist', "
+ "name='TExclID'))",
+ )
+
def test_json_type(self):
eq_ignore_whitespace(
autogenerate.render._repr_type(JSON(), self.autogen_context),