Skip to content

pg-delta: multiple leading enum values generate ADD VALUE before a non-existent label #304

Description

@xmh1011

Bug report

pg-delta generates invalid ALTER TYPE ... ADD VALUE SQL when a target enum inserts a contiguous run of two or more new values before the first existing label.

This is different from #262: it is not about using a newly added enum value in dependent DDL before a commit. The generated enum DDL itself references an anchor label that does not exist yet.

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

To Reproduce

Source database:

CREATE SCHEMA probe;
CREATE TYPE probe.status AS ENUM ('c');

Target database:

CREATE SCHEMA probe;
CREATE TYPE probe.status AS ENUM ('a', 'b', 'c');

On current main (c06f081, @supabase/pg-delta@1.0.0-alpha.30), createPlan produces one transactional unit:

ALTER TYPE probe.status ADD VALUE 'a' BEFORE 'b';
ALTER TYPE probe.status ADD VALUE 'b' AFTER 'a';

Applying that plan fails immediately:

ERROR 22023: "b" is not an existing enum label

The failure is deterministic because the first statement tries to insert a before b, but b is also new and has not been added yet.

Additional cases tested

These cases were validated against a temporary PostgreSQL 16.13 cluster using createPlan + applyPlan from origin/main:

Source labels Target labels Generated shape Result
('b') ('a', 'b') ADD 'a' BEFORE 'b' applies
('c') ('a', 'b', 'c') ADD 'a' BEFORE 'b'; ADD 'b' AFTER 'a' fails, 22023
('d') ('a', 'b', 'c', 'd') ADD 'a' BEFORE 'b'; ADD 'b' AFTER 'a'; ADD 'c' AFTER 'b' fails, 22023
('a', 'd') ('a', 'b', 'c', 'd') ADD 'b' AFTER 'a'; ADD 'c' AFTER 'b' applies
('b') ('a', 'b', 'c') ADD 'a' BEFORE 'b'; ADD 'c' AFTER 'b' applies

So the problematic shape is specifically a contiguous inserted segment at the start of the enum where the segment length is at least two.

Expected behavior

pg-delta should only use an existing label, or a label already added by an earlier statement, as the BEFORE/AFTER anchor.

For the repro above, one valid order is to add the leading run from right to left:

ALTER TYPE probe.status ADD VALUE 'b' BEFORE 'c';
ALTER TYPE probe.status ADD VALUE 'a' BEFORE 'b';

That order was validated directly on PostgreSQL 16.13 and produces the desired final order a, b, c.

Root cause hypothesis

diffEnumLabels simulates additions left-to-right over the branch order. When the first added label is before another added label, nextBranch is not in workingLabels, but the code still emits position = { before: nextBranch }:

} else if (nextBranch) {
  // nextBranch exists but not in workingLabels yet (shouldn't happen in normal flow)
  position = { before: nextBranch };
  workingLabels.push(newValue);
}

That branch is reachable for leading inserted runs such as target labels a, b, c from source c, and produces ADD VALUE 'a' BEFORE 'b' before b exists.

Possible fix direction

When a newly inserted run appears before the first existing label, emit that run from right to left against the nearest existing right-side anchor, or otherwise choose anchors from the current workingLabels only.

Focused regression coverage should include:

  • failing case: source ('c'), target ('a', 'b', 'c')
  • failing case: source ('d'), target ('a', 'b', 'c', 'd')
  • passing controls: one leading value, multiple middle values after an existing value, and leading plus trailing values

Metadata

Metadata

Assignees

No one assigned

    Labels

    supabase/pg-toolbelt🐛 BugDefects, regressions, or unintended behavior that need fixing

    Type

    No type

    Fields

    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