aminghadersohi commented on code in PR #44184:
URL: https://github.com/apache/superset/pull/44184#discussion_r4028740640


##########
superset/commands/soft_delete_collisions.py:
##########
@@ -0,0 +1,166 @@
+# 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.
+"""Translate unique-slot collisions with SOFT-DELETED rows into guidance.
+
+Soft delete keeps rows in the database, so database-level unique
+constraints still see them. On dialects where the constraint was replaced
+by a partial index (``WHERE deleted_at IS NULL`` -- PostgreSQL and
+MySQL 8.0+ for ``dashboards.slug``), a deleted row no longer reserves its
+slot; on the full-constraint dialects (SQLite, MariaDB, MySQL < 8.0) an
+insert colliding with a soft-deleted row still dies at flush with a raw
+``IntegrityError``. Datasets keep a full constraint everywhere and refuse
+at validation instead.
+
+These helpers turn both shapes into an actionable 422: which deleted
+object holds the slot, that an owner can restore it (Settings ▸ Recently
+Archived, or the restore endpoint), or that a different identifier can be
+chosen. A collision NOT caused by a soft-deleted row is never masked --
+callers re-raise the original error when no deleted holder exists.
+
+Adding the next soft-deletable entity: use
+:func:`find_soft_deleted_slot_holder` with the entity's unique criteria
+and raise the entity's own guidance ``ValidationError`` beside its
+existing "already exists" error.
+"""
+
+from __future__ import annotations
+
+import logging
+import re
+from typing import Any, TYPE_CHECKING
+
+logger = logging.getLogger(__name__)
+
+#: Slug uniqueness objects. ``idx_unique_slug`` is the original full constraint
+#: (migration 1a48a5411020); ``ix_dashboards_active_slug`` is the 
live-rows-only
+#: index that replaced it on PostgreSQL / MySQL 8.0.13+ (9e1f3b8c4d2a).
+_SLUG_UNIQUE_NAMES: frozenset[str] = frozenset(
+    {"idx_unique_slug", "ix_dashboards_active_slug"}
+)
+
+if TYPE_CHECKING:
+    from flask_appbuilder import Model
+
+
+def _is_dashboard_slug_uniqueness_error(cause: Exception) -> bool:
+    """Identify dashboard slug uniqueness violations from driver 
diagnostics."""
+    # Wrapper text includes SQL and user-supplied values, not just the error.
+    orig: Exception | None = getattr(cause, "orig", None)
+    if orig is None:
+        return False
+
+    pgcode: str | None = getattr(orig, "pgcode", None) or getattr(
+        orig, "sqlstate", None
+    )
+    if pgcode is not None:
+        constraint_name: str | None = getattr(
+            getattr(orig, "diag", None), "constraint_name", None
+        )
+        return pgcode == "23505" and constraint_name in _SLUG_UNIQUE_NAMES

Review Comment:
   PyMySQL 1.2.0 (pinned) and mysql-connector set `.sqlstate` on the DBAPI 
exception, so `orig` takes this PostgreSQL branch, `'23000' != '23505'` → 
False, and the MySQL branch below is unreachable. Measured on live MySQL 8. 
Gating on `diag` (psycopg-only) preserves every current verdict:
   ```suggestion
       diag: Any | None = getattr(orig, "diag", None)
       if diag is not None:
           pgcode: str | None = getattr(orig, "pgcode", None) or getattr(
               orig, "sqlstate", None
           )
           constraint_name: str | None = getattr(diag, "constraint_name", None)
           return pgcode == "23505" and constraint_name in _SLUG_UNIQUE_NAMES
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to