Skip to content

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

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions