This is an automated email from the ASF dual-hosted git repository.

arivero pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 376a1f49d3 fix(migrations): fix foreign keys to match FAB 4.6.0 tables 
(#32759)
376a1f49d3 is described below

commit 376a1f49d339c393af678b12ad74ae91a72c94fc
Author: Antonio Rivero <[email protected]>
AuthorDate: Wed Mar 19 22:47:26 2025 +0100

    fix(migrations): fix foreign keys to match FAB 4.6.0 tables (#32759)
---
 superset/migrations/shared/utils.py                |  81 +++++++++++++--
 ...46_32bf93dfe2a4_add_on_cascade_in_fab_tables.py | 111 +++++++++++++++++++++
 2 files changed, 184 insertions(+), 8 deletions(-)

diff --git a/superset/migrations/shared/utils.py 
b/superset/migrations/shared/utils.py
index 0cddd32394..32e7710729 100644
--- a/superset/migrations/shared/utils.py
+++ b/superset/migrations/shared/utils.py
@@ -193,12 +193,16 @@ def has_table(table_name: str) -> bool:
     return table_exists
 
 
-def drop_fks_for_table(table_name: str) -> None:
+def drop_fks_for_table(
+    table_name: str, foreign_key_names: list[str] | None = None
+) -> None:
     """
-    Drop all foreign key constraints for a table if it exist and the database
-    is not sqlite.
+    Drop specific or all foreign key constraints for a table
+    if they exist and the database is not sqlite.
 
-    :param table_name: The table name to drop foreign key constraints for
+    :param table_name: The table name to drop foreign key constraints from
+    :param foreign_key_names: Optional list of specific foreign key names to 
drop.
+    If None is provided, all will be dropped.
     """
     connection = op.get_bind()
     inspector = Inspector.from_engine(connection)
@@ -207,12 +211,19 @@ def drop_fks_for_table(table_name: str) -> None:
         return  # sqlite doesn't like constraints
 
     if has_table(table_name):
-        foreign_keys = inspector.get_foreign_keys(table_name)
-        for fk in foreign_keys:
+        existing_fks = {fk["name"] for fk in 
inspector.get_foreign_keys(table_name)}
+
+        # What to delete based on whether the list was passed
+        if foreign_key_names is not None:
+            foreign_key_names = list(set(foreign_key_names) & existing_fks)
+        else:
+            foreign_key_names = list(existing_fks)
+
+        for fk_name in foreign_key_names:
             logger.info(
-                f"Dropping foreign key {GREEN}{fk['name']}{RESET} from table 
{GREEN}{table_name}{RESET}..."  # noqa: E501
+                f"Dropping foreign key {GREEN}{fk_name}{RESET} from table 
{GREEN}{table_name}{RESET}..."  # noqa: E501
             )
-            op.drop_constraint(fk["name"], table_name, type_="foreignkey")
+            op.drop_constraint(fk_name, table_name, type_="foreignkey")
 
 
 def create_table(table_name: str, *columns: SchemaItem) -> None:
@@ -406,3 +417,57 @@ def drop_index(table_name: str, index_name: str) -> None:
     )
 
     op.drop_index(table_name=table_name, index_name=index_name)
+
+
+def create_fks_for_table(
+    foreign_key_name: str,
+    table_name: str,
+    referenced_table: str,
+    local_cols: list[str],
+    remote_cols: list[str],
+    ondelete: Optional[str] = None,
+) -> None:
+    """
+    Create a foreign key constraint for a table, ensuring compatibility with 
sqlite.
+
+    :param foreign_key_name: Foreign key constraint name.
+    :param table_name: The name of the table where the foreign key will be 
created.
+    :param referenced_table: The table the FK references.
+    :param local_cols: Column names in the current table.
+    :param remote_cols: Column names in the referenced table.
+    :param ondelete: (Optional) The ON DELETE action (e.g., "CASCADE", "SET 
NULL").
+    """
+    connection = op.get_bind()
+
+    if not has_table(table_name):
+        logger.warning(
+            f"Table {LRED}{table_name}{RESET} does not exist. Skipping foreign 
key creation."  # noqa: E501
+        )
+        return
+
+    if isinstance(connection.dialect, SQLiteDialect):
+        # SQLite requires batch mode since ALTER TABLE is limited
+        with op.batch_alter_table(table_name) as batch_op:
+            logger.info(
+                f"Creating foreign key {GREEN}{foreign_key_name}{RESET} on 
table {GREEN}{table_name}{RESET} (SQLite mode)..."  # noqa: E501
+            )
+            batch_op.create_foreign_key(
+                foreign_key_name,
+                referenced_table,
+                local_cols,
+                remote_cols,
+                ondelete=ondelete,
+            )
+    else:
+        # Standard FK creation for other databases
+        logger.info(
+            f"Creating foreign key {GREEN}{foreign_key_name}{RESET} on table 
{GREEN}{table_name}{RESET}..."  # noqa: E501
+        )
+        op.create_foreign_key(
+            foreign_key_name,
+            table_name,
+            referenced_table,
+            local_cols,
+            remote_cols,
+            ondelete=ondelete,
+        )
diff --git 
a/superset/migrations/versions/2025-03-19_17-46_32bf93dfe2a4_add_on_cascade_in_fab_tables.py
 
b/superset/migrations/versions/2025-03-19_17-46_32bf93dfe2a4_add_on_cascade_in_fab_tables.py
new file mode 100644
index 0000000000..e7a9a3d75c
--- /dev/null
+++ 
b/superset/migrations/versions/2025-03-19_17-46_32bf93dfe2a4_add_on_cascade_in_fab_tables.py
@@ -0,0 +1,111 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Add on cascade to foreign keys in ab_permission_view_role and ab_user_role
+
+Revision ID: 32bf93dfe2a4
+Revises: 94e7a3499973
+Create Date: 2025-03-19 17:46:25.702610
+
+"""
+
+from superset.migrations.shared.utils import create_fks_for_table, 
drop_fks_for_table
+
+# revision identifiers, used by Alembic.
+revision = "32bf93dfe2a4"
+down_revision = "94e7a3499973"
+
+
+def upgrade():
+    drop_fks_for_table(
+        "ab_permission_view_role",
+        [
+            "ab_permission_view_role_permission_view_id_fkey",
+            "ab_permission_view_role_role_id_fkey",
+        ],
+    )
+    create_fks_for_table(
+        "ab_permission_view_role_role_id_fkey",
+        "ab_permission_view_role",
+        "ab_role",
+        ["role_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    create_fks_for_table(
+        "ab_permission_view_role_permission_view_id_fkey",
+        "ab_permission_view_role",
+        "ab_permission_view",
+        ["permission_view_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+
+    drop_fks_for_table(
+        "ab_user_role",
+        ["ab_user_role_user_id_fkey", "ab_user_role_role_id_fkey"],
+    )
+    create_fks_for_table(
+        "ab_user_role_user_id_fkey",
+        "ab_user_role",
+        "ab_user",
+        ["user_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+    create_fks_for_table(
+        "ab_user_role_role_id_fkey",
+        "ab_user_role",
+        "ab_role",
+        ["role_id"],
+        ["id"],
+        ondelete="CASCADE",
+    )
+
+
+def downgrade():
+    drop_fks_for_table(
+        "ab_permission_view_role",
+        [
+            "ab_permission_view_role_permission_view_id_fkey",
+            "ab_permission_view_role_role_id_fkey",
+        ],
+    )
+    create_fks_for_table(
+        "ab_permission_view_role_permission_view_id_fkey",
+        "ab_permission_view_role",
+        "ab_permission_view",
+        ["permission_view_id"],
+        ["id"],
+    )
+    create_fks_for_table(
+        "ab_permission_view_role_role_id_fkey",
+        "ab_permission_view_role",
+        "ab_role",
+        ["role_id"],
+        ["id"],
+    )
+
+    drop_fks_for_table(
+        "ab_user_role",
+        ["ab_user_role_user_id_fkey", "ab_user_role_role_id_fkey"],
+    )
+    create_fks_for_table(
+        "ab_user_role_user_id_fkey", "ab_user_role", "ab_user", ["user_id"], 
["id"]
+    )
+    create_fks_for_table(
+        "ab_user_role_role_id_fkey", "ab_user_role", "ab_role", ["role_id"], 
["id"]
+    )

Reply via email to