From: Mike Bayer Date: Thu, 12 Oct 2023 17:29:58 +0000 (-0400) Subject: apply PG ddl paren rules to index expressions X-Git-Tag: rel_1_12_1~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5a95c35910185c5b1d53324d5e95b290bdac2446;p=thirdparty%2Fsqlalchemy%2Falembic.git apply PG ddl paren rules to index expressions Fixed autogen render issue where expressions inside of indexes for PG need to be double-parenthesized, meaning a single parens must be present within the generated ``text()`` construct. Change-Id: Iaf88ce28612707994a4753ee57384a8bb26f1133 Fixes: #1322 --- diff --git a/alembic/autogenerate/render.py b/alembic/autogenerate/render.py index fa24c397..1f4bcf89 100644 --- a/alembic/autogenerate/render.py +++ b/alembic/autogenerate/render.py @@ -543,8 +543,10 @@ def _ident(name: Optional[Union[quoted_name, str]]) -> Optional[str]: def _render_potential_expr( value: Any, autogen_context: AutogenContext, + *, wrap_in_text: bool = True, is_server_default: bool = False, + is_index: bool = False, ) -> str: if isinstance(value, sql.ClauseElement): if wrap_in_text: @@ -555,7 +557,7 @@ def _render_potential_expr( return template % { "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), "sql": autogen_context.migration_context.impl.render_ddl_sql_expr( - value, is_server_default=is_server_default + value, is_server_default=is_server_default, is_index=is_index ), } @@ -569,7 +571,7 @@ def _get_index_rendered_expressions( return [ repr(_ident(getattr(exp, "name", None))) if isinstance(exp, sa_schema.Column) - else _render_potential_expr(exp, autogen_context) + else _render_potential_expr(exp, autogen_context, is_index=True) for exp in idx.expressions ] diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index d5594a13..949e2562 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -351,6 +351,29 @@ class PostgresqlImpl(DefaultImpl): compile_kwargs={"literal_binds": True, "include_table": False}, ).string + def render_ddl_sql_expr( + self, + expr: ClauseElement, + is_server_default: bool = False, + is_index: bool = False, + **kw: Any, + ) -> str: + """Render a SQL expression that is typically a server default, + index expression, etc. + + """ + + # apply self_group to index expressions; + # see https://github.com/sqlalchemy/sqlalchemy/blob/ + # 82fa95cfce070fab401d020c6e6e4a6a96cc2578/ + # lib/sqlalchemy/dialects/postgresql/base.py#L2261 + if is_index and not isinstance(expr, ColumnClause): + expr = expr.self_group() + + return super().render_ddl_sql_expr( + expr, is_server_default=is_server_default, is_index=is_index, **kw + ) + def render_type( self, type_: TypeEngine, autogen_context: AutogenContext ) -> Union[str, Literal[False]]: diff --git a/docs/build/unreleased/1322.rst b/docs/build/unreleased/1322.rst new file mode 100644 index 00000000..dc5bf6ce --- /dev/null +++ b/docs/build/unreleased/1322.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: bug, postgresql + :tickets: 1322 + + Fixed autogen render issue where expressions inside of indexes for PG need + to be double-parenthesized, meaning a single parens must be present within + the generated ``text()`` construct. diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index 9a62c4f2..5c5e4f3a 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -1299,6 +1299,22 @@ class PostgresqlAutogenRenderTest(TestBase): "postgresql.JSONB(astext_type=sa.Text())", ) + def test_jsonb_expression_in_index(self): + """test #1322""" + + m = MetaData() + t = Table("tbl", m, Column("c", JSONB())) + idx = Index("my_idx", t.c.c["foo"].astext) + + eq_ignore_whitespace( + autogenerate.render.render_op_text( + self.autogen_context, + ops.CreateIndexOp.from_index(idx), + ), + "op.create_index('my_idx', 'tbl', " + "[sa.text(\"(c ->> 'foo')\")], unique=False)", + ) + @config.requirements.nulls_not_distinct_sa def test_render_unique_nulls_not_distinct_constraint(self): m = MetaData()