diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index cc03f453..f231a197 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -63,6 +63,7 @@ from sqlalchemy.dialects.postgresql.hstore import HSTORE from sqlalchemy.dialects.postgresql.json import JSON from sqlalchemy.dialects.postgresql.json import JSONB + from sqlalchemy.dialects.postgresql.named_types import DOMAIN from sqlalchemy.sql.elements import ClauseElement from sqlalchemy.sql.elements import ColumnElement from sqlalchemy.sql.elements import quoted_name @@ -515,6 +516,38 @@ def _render_JSONB_type( ), ) + def _render_DOMAIN_type( + self, type_: DOMAIN, autogen_context: AutogenContext + ) -> str: + prefix = _postgresql_autogenerate_prefix(autogen_context) + args = [ + repr(render._ident(type_.name)), + render._repr_type(type_.data_type, autogen_context), + ] + if type_.collation is not None: + args.append("collation=%r" % type_.collation) + if type_.default is not None: + args.append( + "default=%s" + % render._render_potential_expr( + type_.default, autogen_context, wrap_in_element=True + ) + ) + if type_.constraint_name is not None: + args.append("constraint_name=%r" % type_.constraint_name) + if type_.not_null: + args.append("not_null=True") + if type_.check is not None: + args.append( + "check=%s" + % render._render_potential_expr( + type_.check, autogen_context, wrap_in_element=False + ) + ) + if type_.create_type is not True: + args.append("create_type=False") + return "%sDOMAIN(%s)" % (prefix, ", ".join(args)) + class PostgresqlColumnType(AlterColumn): def __init__( diff --git a/docs/build/unreleased/1360.rst b/docs/build/unreleased/1360.rst new file mode 100644 index 00000000..639a0ed7 --- /dev/null +++ b/docs/build/unreleased/1360.rst @@ -0,0 +1,12 @@ +.. change:: + :tags: bug, autogenerate, postgresql + :tickets: 1360 + + Fixed rendering of the PostgreSQL + :class:`~sqlalchemy.dialects.postgresql.DOMAIN` type in autogenerated + migration scripts. Previously the type was rendered using ``repr()``, + which omitted the ``check``, ``not_null``, ``default``, ``collation``, + ``constraint_name`` and ``create_type`` arguments and did not apply a + dialect prefix to the underlying data type. The full set of arguments + is now rendered and the underlying data type is rendered using the + standard type-rendering logic. diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index d0304651..b04ae565 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -1361,6 +1361,45 @@ def test_jsonb_type(self): "postgresql.JSONB(astext_type=sa.Text())", ) + @config.requirements.sqlalchemy_2 + def test_domain_type(self): + """test #1360""" + from sqlalchemy.dialects.postgresql import CITEXT + from sqlalchemy.dialects.postgresql import DOMAIN + + eq_ignore_whitespace( + autogenerate.render._repr_type( + DOMAIN("email", CITEXT(), check=r"value ~ '^my_.*$'"), + self.autogen_context, + ), + "postgresql.DOMAIN('email', postgresql.CITEXT(), " + "check=\"value ~ '^my_.*$'\")", + ) + + @config.requirements.sqlalchemy_2 + def test_domain_type_full(self): + """test #1360""" + from sqlalchemy.dialects.postgresql import DOMAIN + + eq_ignore_whitespace( + autogenerate.render._repr_type( + DOMAIN( + "mydom", + String(), + collation="C", + default="x", + constraint_name="ck", + not_null=True, + check="value > 0", + create_type=False, + ), + self.autogen_context, + ), + "postgresql.DOMAIN('mydom', sa.String(), collation='C', " + "default='x', constraint_name='ck', not_null=True, " + "check='value > 0', create_type=False)", + ) + def test_jsonb_expression_in_index(self): """test #1322"""