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.
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
Bug report
pg-deltagenerates invalidALTER TYPE ... ADD VALUESQL 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.
To Reproduce
Source database:
Target database:
On current
main(c06f081,@supabase/pg-delta@1.0.0-alpha.30),createPlanproduces one transactional unit:Applying that plan fails immediately:
The failure is deterministic because the first statement tries to insert
abeforeb, butbis also new and has not been added yet.Additional cases tested
These cases were validated against a temporary PostgreSQL 16.13 cluster using
createPlan+applyPlanfromorigin/main:('b')('a', 'b')ADD 'a' BEFORE 'b'('c')('a', 'b', 'c')ADD 'a' BEFORE 'b'; ADD 'b' AFTER 'a'22023('d')('a', 'b', 'c', 'd')ADD 'a' BEFORE 'b'; ADD 'b' AFTER 'a'; ADD 'c' AFTER 'b'22023('a', 'd')('a', 'b', 'c', 'd')ADD 'b' AFTER 'a'; ADD 'c' AFTER 'b'('b')('a', 'b', 'c')ADD 'a' BEFORE 'b'; ADD 'c' AFTER 'b'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-deltashould only use an existing label, or a label already added by an earlier statement, as theBEFORE/AFTERanchor.For the repro above, one valid order is to add the leading run from right to left:
That order was validated directly on PostgreSQL 16.13 and produces the desired final order
a, b, c.Root cause hypothesis
diffEnumLabelssimulates additions left-to-right over the branch order. When the first added label is before another added label,nextBranchis not inworkingLabels, but the code still emitsposition = { before: nextBranch }:That branch is reachable for leading inserted runs such as target labels
a, b, cfrom sourcec, and producesADD VALUE 'a' BEFORE 'b'beforebexists.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
workingLabelsonly.Focused regression coverage should include:
('c'), target('a', 'b', 'c')('d'), target('a', 'b', 'c', 'd')