]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
apply PG ddl paren rules to index expressions
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 12 Oct 2023 17:29:58 +0000 (13:29 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 12 Oct 2023 17:30:33 +0000 (13:30 -0400)
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
alembic/autogenerate/render.py
alembic/ddl/postgresql.py
docs/build/unreleased/1322.rst [new file with mode: 0644]
tests/test_postgresql.py

index fa24c397fda6423d414d387a2424e03dcf8da2f8..1f4bcf898b6dd8b6ed9a52299a6766f12ca1e705 100644 (file)
@@ -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
     ]
 
index d5594a13535549986c5a170afe75c217b2361919..949e256260b44b8306535f77762662d67974864f 100644 (file)
@@ -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 (file)
index 0000000..dc5bf6c
--- /dev/null
@@ -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.
index 9a62c4f2d10df629c5cfaf9344def2759ae08abd..5c5e4f3a4f7a9d269df6bf5c71f5c941f94c7f4f 100644 (file)
@@ -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()