Skip to content

[bugfix] Restore constraints before snapshot conflict updates#806

Open
blurskye wants to merge 4 commits into
xataio:mainfrom
blurskye:fix-snapshot-conflict-update-constraints
Open

[bugfix] Restore constraints before snapshot conflict updates#806
blurskye wants to merge 4 commits into
xataio:mainfrom
blurskye:fix-snapshot-conflict-update-constraints

Conversation

@blurskye
Copy link
Copy Markdown

@blurskye blurskye commented May 9, 2026

Summary

This fixes a snapshot restore ordering issue when using a Postgres target with on_conflict_action: "update".

In that mode, the snapshot data path writes rows with INSERT ... ON CONFLICT (...) DO UPDATE. For that SQL to work, Postgres needs the matching primary key, unique constraint, or unique index to already exist on the target table.

The schema snapshot currently restores tables first, loads data, and only restores indexes/constraints after the data snapshot finishes. That ordering is good for bulk loading, but it breaks the non-bulk conflict-update path because the target table exists without the constraint needed by the ON CONFLICT clause.

For example, this is the shape of the problem:

CREATE TABLE public.snapshot_accounts (
  id bigint NOT NULL,
  email text NOT NULL,
  balance numeric(12,2) NOT NULL
);

INSERT INTO public.snapshot_accounts (id, email, balance)
VALUES (1, 'user-1@example.test', 100.00)
ON CONFLICT (id) DO UPDATE
SET email = EXCLUDED.email,
    balance = EXCLUDED.balance;

Postgres rejects that with:

ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification

The same insert works once the primary key exists:

ALTER TABLE public.snapshot_accounts
ADD CONSTRAINT snapshot_accounts_pkey PRIMARY KEY (id);

Fix

When the snapshot is using the regular Postgres batch writer with on_conflict_action: "update", restore only the constraints/indexes that can be used as conflict targets before the data snapshot runs:

  • primary keys
  • unique constraints
  • unique indexes

Everything else keeps the existing restore order. Foreign keys, regular indexes, triggers, and other constraints still restore after data, so the snapshot does not lose the current bulk-load behavior or start enforcing relationship constraints before all rows are present.

For the other paths, including bulk ingest and on_conflict_action: "nothing", the existing order is unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant