Skip to content

DataProvider: AddColumn with inline checkConstraint duplicates check on subsequent migrate #69

@abdushakoor12

Description

@abdushakoor12

Tool: DataProviderMigrate (CLI)
Version: latest as of 2026-05-20 (installed via dotnet tool)
Provider: postgres (Supabase)

Symptom

A migrate --allow-destructive run fails with:

Error: migration apply failed: 42710: constraint "tenant_credit_event_kind_chk" for relation "tenant_credit_event" already exists

…even when starting from a state where the column and the constraint do not yet exist (verified via information_schema.columns + pg_constraint before the run).

Schema

- name: tenant_credit_event
  columns:
    - name: kind
      type: Text
      isNullable: false
      checkConstraint: "kind IN ('topup','spend','refund','adjustment')"

Hypothesis

AddColumnOperation with an inline checkConstraint appears to emit two
DDL statements in the same transaction:

  1. ALTER TABLE … ADD COLUMN kind text NOT NULL (which Postgres auto-names the
    inline constraint, e.g. tenant_credit_event_kind_check)
  2. ALTER TABLE … ADD CONSTRAINT tenant_credit_event_kind_chk CHECK (kind IN (…))

The second emit collides because the inline form was already applied — OR
DataProvider is emitting the named CONSTRAINT twice in a single batch.

Same shape visible on usage_events where prod has both
usage_events_kind_check AND usage_events_kind_chk (and equivalent
duplicates for charged_micro_usd, vendor_micro_usd, quantity). That's
the smoking gun — a previous successful run left one of each pair behind,
proving DataProvider is creating two constraints per column-level
checkConstraint.

Workaround used (prod hotfix)

Manually applied missing schema deltas via raw SQL (per user authorization
in the credit-billing rollout):

  • ALTER TABLE … ADD COLUMN kind text NOT NULL (no inline check)
  • ALTER TABLE … ADD CONSTRAINT tenant_credit_event_kind_chk CHECK … (named, once)
  • Renamed usage_events.ledger_id → credit_event_id
  • DROP TABLE wallet_ledger, DROP TABLE wallet
  • Replaced two combined RLS policies with the four single-op ones declared
    in migrations/schema.yaml.

Ask

Either:

  1. Emit only the named constraint from checkConstraint: and skip the
    inline form, OR
  2. Detect existing equivalent check constraints (same predicate / same
    relation) and treat as a no-op when re-applying.

Cross-refs

  • NAP incident: Nimblesite/NimblesiteAgenticPlatform#126
  • PR that triggered: Nimblesite/NimblesiteAgenticPlatform#125

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions