Skip to content

fix(migrations): actually drop _customer_location_uc (list == set no-op) - #42642

Merged
rusackas merged 2 commits into
apache:masterfrom
mikebridge:sc-112173-fix-noop-uc-drop
Aug 5, 2026
Merged

fix(migrations): actually drop _customer_location_uc (list == set no-op)#42642
rusackas merged 2 commits into
apache:masterfrom
mikebridge:sc-112173-fix-noop-uc-drop

Conversation

@mikebridge

Copy link
Copy Markdown
Contributor

SUMMARY

Migration df3d7e2eb9a4 (2024) intended to drop the legacy 3-column unique constraint _customer_location_uc (database_id, schema, table_name) from tables, but passed a list to generic_find_uq_constraint_name, whose body compares columns == set(uq["column_names"]). list == set is always False in Python, so the constraint was never found and never dropped — a silent no-op.

Consequences on databases migrated through it:

  • The NULL-leaky 3-column constraint (no catalog leg) is still present, while schemas built from model metadata (create_all, e.g. CI) carry the model's intended 4-column constraint (database_id, catalog, schema, table_name) instead — so CI cannot reproduce production failure shapes.
  • A row keeps occupying (database_id, schema, table_name) across catalogs: creating the same table under a different catalog passes every catalog-aware app-level check and then hits an opaque IntegrityError only on migrated databases.

Two-part fix:

  1. generic_find_uq_constraint_name now coerces its columns argument to a set (parameter widened to Collection[str]), removing the foot-gun for all callers. Existing set-passing callers are unaffected.
  2. A take-2 migration (16755d4ca4ae) re-attempts the drop with exact set matching. It is a harmless no-op where the constraint is already absent, and the model's 4-column constraint can never match a 3-column set comparison, so it is not at risk. Downgrade restores the legacy constraint best-effort (rows written after the drop may legitimately collide across catalogs), mirroring the tolerant try/except posture of the constraint's original creator.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — schema-only change.

TESTING INSTRUCTIONS

  1. pytest tests/unit_tests/utils/test_core.py -k generic_find_uq — 3 tests: the list-argument regression pin (fails against the pre-fix helper), the set-argument happy path, and the exact-match guarantee (a 3-column lookup never matches the model's 4-column constraint).
  2. Migration verified on SQLite and PostgreSQL: fresh full-chain superset db upgrade (clean), then superset db downgrade (constraint recreated: _customer_location_uc (database_id, schema, table_name)), then superset db upgrade (constraint dropped), with uq_tables_uuid untouched throughout. On a database that still carries the legacy constraint, the upgrade drops it; on one that never had it, the lookup finds nothing and the migration no-ops.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided: single ALTER TABLE ... DROP CONSTRAINT on tables (or a no-op lookup); sub-second on PostgreSQL/MySQL, table-rebuild via batch mode on SQLite. No data movement, no downtime expected.
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

@github-actions github-actions Bot added the risk:db-migration PRs that require a DB migration label Jul 31, 2026
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 33.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.79%. Comparing base (816f37f) to head (a762801).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
superset/utils/core.py 33.33% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42642      +/-   ##
==========================================
- Coverage   65.79%   65.79%   -0.01%     
==========================================
  Files        2842     2842              
  Lines      162127   162128       +1     
  Branches    37158    37158              
==========================================
- Hits       106676   106673       -3     
- Misses      53387    53390       +3     
- Partials     2064     2065       +1     
Flag Coverage Δ
hive 38.07% <33.33%> (-0.01%) ⬇️
mysql 57.90% <33.33%> (-0.01%) ⬇️
postgres 57.95% <33.33%> (-0.01%) ⬇️
presto 40.00% <33.33%> (-0.01%) ⬇️
python 59.32% <33.33%> (-0.01%) ⬇️
sqlite 57.57% <33.33%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Migration df3d7e2eb9a4 intended to drop the legacy 3-column unique
constraint _customer_location_uc (database_id, schema, table_name) from
the tables table, but passed a list to generic_find_uq_constraint_name,
whose body compares columns == set(uq['column_names']). list == set is
always False in Python, so the constraint was never found and never
dropped — a silent no-op.

Databases migrated through it therefore still carry the constraint,
which leaks across catalogs (no catalog leg) and diverges from schemas
built from model metadata, where the model's 4-column constraint exists
instead. A row can occupy (database_id, schema, table_name) across
catalogs, passing every catalog-aware app-level check and then hitting
an opaque IntegrityError only on migrated databases.

Fix in two parts:
- generic_find_uq_constraint_name coerces its columns argument to a
  set, removing the foot-gun for all callers (existing set-passing
  callers unaffected).
- A take-2 migration re-attempts the drop with exact set matching.
  No-op where the constraint is already absent; the model's 4-column
  constraint can never match a 3-column set comparison. Downgrade
  restores the legacy constraint best-effort, mirroring the tolerant
  posture of its original creator.

Verified on SQLite and PostgreSQL: fresh full-chain upgrade, then
downgrade (constraint recreated) and re-upgrade (constraint dropped),
with uq_tables_uuid untouched throughout.

Fixes sc-112173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mikebridge
mikebridge force-pushed the sc-112173-fix-noop-uc-drop branch from 01ee065 to b706770 Compare August 5, 2026 12:37
@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The migration header's Revises field (e7d93a524ff6) does not match the actual down_revision (f3a8c1d2e9b7) defined in the file. To resolve this, update the Revises value in the docstring to match the down_revision identifier.

Revision ID: 16755d4ca4ae
Revises: f3a8c1d2e9b7
Create Date: 2026-07-31 10:00:00.000000

There are no other comments on this pull request to address.

superset/migrations/versions/2026-07-31_10-00_16755d4ca4ae_drop__customer_location_uc_take_2.py

Revision ID: 16755d4ca4ae
Revises: f3a8c1d2e9b7
Create Date: 2026-07-31 10:00:00.000000

The rebase onto current master re-pointed down_revision from
e7d93a524ff6 to f3a8c1d2e9b7 — master gained the reports-retry migration
(apache#42481) claiming the same parent, which left two alembic heads — but
the docstring header still named the old ancestor.

Alembic reads the variable, not the docstring, so the graph was correct
and CI passed. The header was simply lying to the next person to read
it, which is exactly when a migration file gets read: while diagnosing
a broken chain.

Caught by codeant and bito independently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #1185ae

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/migrations/versions/2026-07-31_10-00_16755d4ca4ae_drop__customer_location_uc_take_2.py - 1
    • Alembic chain broken by missing up_revision · Line 35-35
Review Details
  • Files reviewed - 3 · Commit Range: b706770..a762801
    • superset/migrations/versions/2026-07-31_10-00_16755d4ca4ae_drop__customer_location_uc_take_2.py
    • superset/utils/core.py
    • tests/unit_tests/utils/test_core.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikebridge LGTM, thanks for pinning the exact-match guarantee with its own test.

@rusackas
rusackas merged commit e880711 into apache:master Aug 5, 2026
69 checks passed
mikebridge pushed a commit to mikebridge/superset that referenced this pull request Aug 5, 2026
Flips the two soft-delete release defaults for general availability
(sc-111918 stage 2a, FR-003 and FR-005):

  SOFT_DELETE                False -> True
  SOFT_DELETE_PURGE_DRY_RUN  True  -> False

Deleting a dashboard, chart, or dataset now archives it rather than
removing it, and the nightly purge deletes for real once the retention
window elapses instead of only logging would_purge counts.

Both switches are RETAINED, deliberately. FR-003 keeps SOFT_DELETE as
the move-back lever and defers its removal (with the hard-delete
fallback and its both-state tests) to sc-115600 once post-flip
confidence is established; FR-004 requires that fallback to keep being
exercised, because the way back is only real if it stays tested.
FR-005 keeps SOFT_DELETE_PURGE_DRY_RUN as an operational switch, so an
operator can suspend purging without redeploying.

Scope: soft delete only. The versioning flips (VERSION_HISTORY and
ENABLE_VERSIONING_CAPTURE, FR-001/002/008) are a separate branch —
FR-009 permits the two shipping in different releases, and they answer
to different gates: an internal dev@ determination here, a SIP-210 vote
there.

test_default_config_is_safe asserted the pre-flip posture and is
superseded by test_default_config_purges_for_real_after_the_retention_window,
which pins the new defaults with the reasoning for why dry-run was the
introducing release's choice. It is the only test in the unit suite that
the flip breaks — verified by a full run (the 24 firebolt SQL-dialect
failures are a different subsystem and unrelated).

UPDATING.md gains the FR-006 breaking-change entry: what changes, how to
size the first live purge (dry-run for one night and read the counts),
the replaced-CELERY_CONFIG check, and the honest caveat that turning
soft delete back off resurrects archived rows rather than cleanly
reverting. The dry-run paragraph further down, which described dry-run
as the shipped default, is corrected.

docs/static/feature-flags.json is regenerated by the docs-sync hook.

Depends on: apache#42641 (beat-schedule startup warning, FR-010) and
apache#42642 (sc-112173 no-op migration, FR-010) landing first.

Verified: 66/66 across the soft-delete unit suites, 24/24 integration
purge tests, pre-commit green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:draft risk:db-migration PRs that require a DB migration size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants