@supabase/pg-delta 1.0.0-alpha.21 → 1.0.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/catalog.diff.js +4 -3
- package/dist/core/catalog.model.d.ts +8 -1
- package/dist/core/catalog.model.js +9 -8
- package/dist/core/expand-replace-dependencies.js +23 -0
- package/dist/core/objects/extract-with-retry.d.ts +36 -0
- package/dist/core/objects/extract-with-retry.js +51 -0
- package/dist/core/objects/index/index.diff.js +0 -1
- package/dist/core/objects/index/index.model.d.ts +2 -3
- package/dist/core/objects/index/index.model.js +17 -6
- package/dist/core/objects/materialized-view/materialized-view.model.d.ts +2 -1
- package/dist/core/objects/materialized-view/materialized-view.model.js +20 -4
- package/dist/core/objects/procedure/procedure.model.d.ts +2 -1
- package/dist/core/objects/procedure/procedure.model.js +20 -4
- package/dist/core/objects/rls-policy/rls-policy.diff.js +13 -1
- package/dist/core/objects/rule/rule.model.d.ts +2 -1
- package/dist/core/objects/rule/rule.model.js +20 -3
- package/dist/core/objects/sequence/sequence.diff.d.ts +2 -1
- package/dist/core/objects/sequence/sequence.diff.js +28 -4
- package/dist/core/objects/table/changes/table.alter.d.ts +12 -1
- package/dist/core/objects/table/changes/table.alter.js +20 -2
- package/dist/core/objects/table/table.diff.js +19 -15
- package/dist/core/objects/table/table.model.d.ts +6 -1
- package/dist/core/objects/table/table.model.js +40 -5
- package/dist/core/objects/trigger/trigger.model.d.ts +2 -1
- package/dist/core/objects/trigger/trigger.model.js +20 -4
- package/dist/core/objects/utils.d.ts +1 -0
- package/dist/core/objects/utils.js +3 -0
- package/dist/core/objects/view/view.model.d.ts +2 -1
- package/dist/core/objects/view/view.model.js +20 -4
- package/dist/core/plan/create.js +3 -1
- package/dist/core/plan/types.d.ts +8 -0
- package/dist/core/{post-diff-cycle-breaking.d.ts → post-diff-normalization.d.ts} +8 -1
- package/dist/core/post-diff-normalization.js +202 -0
- package/dist/core/sort/cycle-breakers.js +1 -1
- package/dist/core/sort/utils.d.ts +10 -0
- package/dist/core/sort/utils.js +28 -0
- package/package.json +1 -1
- package/src/core/catalog.diff.ts +4 -2
- package/src/core/catalog.model.ts +20 -8
- package/src/core/expand-replace-dependencies.test.ts +131 -0
- package/src/core/expand-replace-dependencies.ts +24 -0
- package/src/core/objects/extract-with-retry.test.ts +143 -0
- package/src/core/objects/extract-with-retry.ts +87 -0
- package/src/core/objects/index/index.diff.ts +0 -1
- package/src/core/objects/index/index.model.test.ts +37 -1
- package/src/core/objects/index/index.model.ts +25 -6
- package/src/core/objects/materialized-view/materialized-view.model.test.ts +93 -0
- package/src/core/objects/materialized-view/materialized-view.model.ts +27 -4
- package/src/core/objects/procedure/procedure.model.test.ts +117 -0
- package/src/core/objects/procedure/procedure.model.ts +28 -5
- package/src/core/objects/rls-policy/rls-policy.diff.ts +19 -1
- package/src/core/objects/rule/rule.model.test.ts +99 -0
- package/src/core/objects/rule/rule.model.ts +28 -4
- package/src/core/objects/sequence/sequence.diff.test.ts +87 -0
- package/src/core/objects/sequence/sequence.diff.ts +31 -6
- package/src/core/objects/table/changes/table.alter.test.ts +13 -21
- package/src/core/objects/table/changes/table.alter.ts +30 -3
- package/src/core/objects/table/table.diff.ts +24 -19
- package/src/core/objects/table/table.model.test.ts +209 -0
- package/src/core/objects/table/table.model.ts +52 -7
- package/src/core/objects/trigger/trigger.model.test.ts +113 -0
- package/src/core/objects/trigger/trigger.model.ts +28 -5
- package/src/core/objects/utils.ts +3 -0
- package/src/core/objects/view/view.model.test.ts +90 -0
- package/src/core/objects/view/view.model.ts +28 -5
- package/src/core/plan/create.ts +3 -1
- package/src/core/plan/types.ts +8 -0
- package/src/core/{post-diff-cycle-breaking.test.ts → post-diff-normalization.test.ts} +168 -4
- package/src/core/post-diff-normalization.ts +260 -0
- package/src/core/sort/cycle-breakers.ts +1 -1
- package/src/core/sort/utils.ts +38 -0
- package/dist/core/post-diff-cycle-breaking.js +0 -100
- package/src/core/post-diff-cycle-breaking.ts +0 -138
package/src/core/sort/utils.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { Change } from "../change.types.ts";
|
|
2
|
+
import {
|
|
3
|
+
AlterTableAlterColumnDropDefault,
|
|
4
|
+
AlterTableAlterColumnDropIdentity,
|
|
5
|
+
AlterTableAlterColumnType,
|
|
6
|
+
} from "../objects/table/changes/table.alter.ts";
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* Execution phases for changes.
|
|
@@ -30,6 +35,16 @@ export function isMetadataStableId(stableId: string): boolean {
|
|
|
30
35
|
* - ALTER operations with scope="privilege" → create_alter_object phase (metadata changes)
|
|
31
36
|
* - ALTER operations that drop actual objects → drop phase (destructive ALTER)
|
|
32
37
|
* - ALTER operations that don't drop objects → create_alter_object phase (non-destructive ALTER)
|
|
38
|
+
*
|
|
39
|
+
* Dependency-breaking ALTERs that remove a `pg_depend` edge to another
|
|
40
|
+
* object that may be dropped in the same plan (for example
|
|
41
|
+
* `ALTER COLUMN ... DROP DEFAULT` releasing a sequence reference, or
|
|
42
|
+
* `ALTER COLUMN ... TYPE <built-in>` releasing a user-defined type
|
|
43
|
+
* reference) are routed to the drop phase. The drop phase sorts in reverse
|
|
44
|
+
* dependency order using the main catalog, so the catalog edges already
|
|
45
|
+
* in `pg_depend` order the ALTER before any dependent `DROP TYPE` /
|
|
46
|
+
* `DROP SEQUENCE` / `DROP FUNCTION` and PostgreSQL no longer rejects the
|
|
47
|
+
* drop with error 2BP01.
|
|
33
48
|
*/
|
|
34
49
|
export function getExecutionPhase(change: Change): Phase {
|
|
35
50
|
// DROP operations always go to drop phase
|
|
@@ -60,6 +75,29 @@ export function getExecutionPhase(change: Change): Phase {
|
|
|
60
75
|
return "drop";
|
|
61
76
|
}
|
|
62
77
|
|
|
78
|
+
// Dependency-breaking column ALTERs that release a pg_depend edge.
|
|
79
|
+
// Routing these to the drop phase lets the existing catalog dependency
|
|
80
|
+
// edges (column → sequence, column → identity sequence) order them
|
|
81
|
+
// before the matching DROP statement.
|
|
82
|
+
if (
|
|
83
|
+
change instanceof AlterTableAlterColumnDropDefault ||
|
|
84
|
+
change instanceof AlterTableAlterColumnDropIdentity
|
|
85
|
+
) {
|
|
86
|
+
return "drop";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ALTER COLUMN ... TYPE only safely runs in the drop phase when the
|
|
90
|
+
// target type is built-in. For user-defined target types we cannot tell
|
|
91
|
+
// here whether the type is created in the same plan, and the create
|
|
92
|
+
// happens in create_alter phase, so we keep the alter in that phase to
|
|
93
|
+
// preserve the create-then-alter ordering.
|
|
94
|
+
if (
|
|
95
|
+
change instanceof AlterTableAlterColumnType &&
|
|
96
|
+
!change.column.is_custom_type
|
|
97
|
+
) {
|
|
98
|
+
return "drop";
|
|
99
|
+
}
|
|
100
|
+
|
|
63
101
|
// Non-destructive ALTER (ADD COLUMN, GRANT, etc.) → create_alter phase
|
|
64
102
|
return "create_alter_object";
|
|
65
103
|
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { AlterTableAddConstraint, AlterTableDropColumn, AlterTableDropConstraint, AlterTableValidateConstraint, } from "./objects/table/changes/table.alter.js";
|
|
2
|
-
import { CreateCommentOnConstraint } from "./objects/table/changes/table.comment.js";
|
|
3
|
-
import { stableId } from "./objects/utils.js";
|
|
4
|
-
function constraintStableId(table, constraintName) {
|
|
5
|
-
return stableId.constraint(table.schema, table.name, constraintName);
|
|
6
|
-
}
|
|
7
|
-
function isSupersededByTableReplacement(change, replacedTableIds) {
|
|
8
|
-
if (!(change instanceof AlterTableDropColumn) &&
|
|
9
|
-
!(change instanceof AlterTableDropConstraint)) {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
return replacedTableIds.has(change.table.stableId);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Drop earlier duplicates of `AlterTableAddConstraint` /
|
|
16
|
-
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` targeting
|
|
17
|
-
* replaced tables, keeping only the last occurrence of each
|
|
18
|
-
* `(changeType, table.stableId, constraint.name)`.
|
|
19
|
-
*
|
|
20
|
-
* When `expandReplaceDependencies()` promotes a table to a full
|
|
21
|
-
* `DropTable + CreateTable` pair, it also emits one
|
|
22
|
-
* `AlterTableAddConstraint` (plus optional `VALIDATE CONSTRAINT` /
|
|
23
|
-
* `COMMENT ON CONSTRAINT`) per branch constraint. If `diffTables()` already
|
|
24
|
-
* emitted the same change for a shape flip or a new constraint on that
|
|
25
|
-
* table, the plan ends up with two identical `ALTER TABLE ... ADD
|
|
26
|
-
* CONSTRAINT ...` statements and PostgreSQL fails at apply time with
|
|
27
|
-
* `constraint "..." for relation "..." already exists`. Because
|
|
28
|
-
* `expandReplaceDependencies()` appends its additions after the original
|
|
29
|
-
* `diffTables()` output, the last occurrence is the expansion's emission —
|
|
30
|
-
* keeping it preserves correctness while removing the duplicate.
|
|
31
|
-
*/
|
|
32
|
-
function dropReplacedTableDuplicateConstraintChanges(changes, replacedTableIds) {
|
|
33
|
-
if (replacedTableIds.size === 0)
|
|
34
|
-
return changes;
|
|
35
|
-
const keyFor = (change) => {
|
|
36
|
-
if (!(change instanceof AlterTableAddConstraint) &&
|
|
37
|
-
!(change instanceof AlterTableValidateConstraint) &&
|
|
38
|
-
!(change instanceof CreateCommentOnConstraint)) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
if (!replacedTableIds.has(change.table.stableId))
|
|
42
|
-
return null;
|
|
43
|
-
const tag = change instanceof AlterTableAddConstraint
|
|
44
|
-
? "add"
|
|
45
|
-
: change instanceof AlterTableValidateConstraint
|
|
46
|
-
? "validate"
|
|
47
|
-
: "comment";
|
|
48
|
-
return `${tag}:${constraintStableId(change.table, change.constraint.name)}`;
|
|
49
|
-
};
|
|
50
|
-
const seen = new Set();
|
|
51
|
-
const reversedKept = [];
|
|
52
|
-
let mutated = false;
|
|
53
|
-
// Walk backwards: the first encounter of each key corresponds to its LAST
|
|
54
|
-
// occurrence in the original order. `expandReplaceDependencies()` appends
|
|
55
|
-
// additions after the original changes, so "last wins" keeps the
|
|
56
|
-
// expansion's emission and drops the earlier diffTables duplicate.
|
|
57
|
-
for (let i = changes.length - 1; i >= 0; i--) {
|
|
58
|
-
const change = changes[i];
|
|
59
|
-
const key = keyFor(change);
|
|
60
|
-
if (key !== null) {
|
|
61
|
-
if (seen.has(key)) {
|
|
62
|
-
mutated = true;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
seen.add(key);
|
|
66
|
-
}
|
|
67
|
-
reversedKept.push(change);
|
|
68
|
-
}
|
|
69
|
-
return mutated ? reversedKept.reverse() : changes;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Apply structural rewrites to the change list that are only obvious once
|
|
73
|
-
* every object diff has been collected. This pass does NOT prevent dependency
|
|
74
|
-
* cycles — that responsibility now lives in the sort phase, where
|
|
75
|
-
* `sortPhaseChanges` invokes `tryBreakCycleByChangeInjection` lazily on cycles
|
|
76
|
-
* that edge filtering can't break (FK SCC of dropped tables,
|
|
77
|
-
* AlterPublicationDropTables ↔ AlterTableDropColumn, …).
|
|
78
|
-
*
|
|
79
|
-
* Concretely, this pass:
|
|
80
|
-
*
|
|
81
|
-
* - Prunes `AlterTableDropColumn(T.*)` / `AlterTableDropConstraint(T.*)`
|
|
82
|
-
* changes that are made redundant by an expansion-emitted
|
|
83
|
-
* `DropTable(T) + CreateTable(T)` pair. Without this, the apply phase
|
|
84
|
-
* would try to drop a column that no longer exists in the freshly
|
|
85
|
-
* recreated table.
|
|
86
|
-
* - Dedupes duplicate `AlterTableAddConstraint` /
|
|
87
|
-
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` changes
|
|
88
|
-
* produced when `diffTables()` and `expandReplaceDependencies()` both
|
|
89
|
-
* emit the same constraint operation for a replaced table. Last write
|
|
90
|
-
* wins so the expansion's emission survives.
|
|
91
|
-
*
|
|
92
|
-
* Object-local PostgreSQL semantics (for example owned-sequence cascades)
|
|
93
|
-
* stay in the corresponding `diff*` function instead of this pass.
|
|
94
|
-
*/
|
|
95
|
-
export function normalizePostDiffCycles({ changes, replacedTableIds = new Set(), }) {
|
|
96
|
-
const dedupedChanges = dropReplacedTableDuplicateConstraintChanges(changes, replacedTableIds);
|
|
97
|
-
if (replacedTableIds.size === 0)
|
|
98
|
-
return dedupedChanges;
|
|
99
|
-
return dedupedChanges.filter((change) => !isSupersededByTableReplacement(change, replacedTableIds));
|
|
100
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import type { Change } from "./change.types.ts";
|
|
2
|
-
import {
|
|
3
|
-
AlterTableAddConstraint,
|
|
4
|
-
AlterTableDropColumn,
|
|
5
|
-
AlterTableDropConstraint,
|
|
6
|
-
AlterTableValidateConstraint,
|
|
7
|
-
} from "./objects/table/changes/table.alter.ts";
|
|
8
|
-
import { CreateCommentOnConstraint } from "./objects/table/changes/table.comment.ts";
|
|
9
|
-
import { stableId } from "./objects/utils.ts";
|
|
10
|
-
|
|
11
|
-
function constraintStableId(
|
|
12
|
-
table: { schema: string; name: string },
|
|
13
|
-
constraintName: string,
|
|
14
|
-
) {
|
|
15
|
-
return stableId.constraint(table.schema, table.name, constraintName);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function isSupersededByTableReplacement(
|
|
19
|
-
change: Change,
|
|
20
|
-
replacedTableIds: ReadonlySet<string>,
|
|
21
|
-
): boolean {
|
|
22
|
-
if (
|
|
23
|
-
!(change instanceof AlterTableDropColumn) &&
|
|
24
|
-
!(change instanceof AlterTableDropConstraint)
|
|
25
|
-
) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
return replacedTableIds.has(change.table.stableId);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Drop earlier duplicates of `AlterTableAddConstraint` /
|
|
33
|
-
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` targeting
|
|
34
|
-
* replaced tables, keeping only the last occurrence of each
|
|
35
|
-
* `(changeType, table.stableId, constraint.name)`.
|
|
36
|
-
*
|
|
37
|
-
* When `expandReplaceDependencies()` promotes a table to a full
|
|
38
|
-
* `DropTable + CreateTable` pair, it also emits one
|
|
39
|
-
* `AlterTableAddConstraint` (plus optional `VALIDATE CONSTRAINT` /
|
|
40
|
-
* `COMMENT ON CONSTRAINT`) per branch constraint. If `diffTables()` already
|
|
41
|
-
* emitted the same change for a shape flip or a new constraint on that
|
|
42
|
-
* table, the plan ends up with two identical `ALTER TABLE ... ADD
|
|
43
|
-
* CONSTRAINT ...` statements and PostgreSQL fails at apply time with
|
|
44
|
-
* `constraint "..." for relation "..." already exists`. Because
|
|
45
|
-
* `expandReplaceDependencies()` appends its additions after the original
|
|
46
|
-
* `diffTables()` output, the last occurrence is the expansion's emission —
|
|
47
|
-
* keeping it preserves correctness while removing the duplicate.
|
|
48
|
-
*/
|
|
49
|
-
function dropReplacedTableDuplicateConstraintChanges(
|
|
50
|
-
changes: Change[],
|
|
51
|
-
replacedTableIds: ReadonlySet<string>,
|
|
52
|
-
): Change[] {
|
|
53
|
-
if (replacedTableIds.size === 0) return changes;
|
|
54
|
-
|
|
55
|
-
const keyFor = (change: Change): string | null => {
|
|
56
|
-
if (
|
|
57
|
-
!(change instanceof AlterTableAddConstraint) &&
|
|
58
|
-
!(change instanceof AlterTableValidateConstraint) &&
|
|
59
|
-
!(change instanceof CreateCommentOnConstraint)
|
|
60
|
-
) {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
if (!replacedTableIds.has(change.table.stableId)) return null;
|
|
64
|
-
const tag =
|
|
65
|
-
change instanceof AlterTableAddConstraint
|
|
66
|
-
? "add"
|
|
67
|
-
: change instanceof AlterTableValidateConstraint
|
|
68
|
-
? "validate"
|
|
69
|
-
: "comment";
|
|
70
|
-
return `${tag}:${constraintStableId(change.table, change.constraint.name)}`;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const seen = new Set<string>();
|
|
74
|
-
const reversedKept: Change[] = [];
|
|
75
|
-
let mutated = false;
|
|
76
|
-
|
|
77
|
-
// Walk backwards: the first encounter of each key corresponds to its LAST
|
|
78
|
-
// occurrence in the original order. `expandReplaceDependencies()` appends
|
|
79
|
-
// additions after the original changes, so "last wins" keeps the
|
|
80
|
-
// expansion's emission and drops the earlier diffTables duplicate.
|
|
81
|
-
for (let i = changes.length - 1; i >= 0; i--) {
|
|
82
|
-
const change = changes[i] as Change;
|
|
83
|
-
const key = keyFor(change);
|
|
84
|
-
if (key !== null) {
|
|
85
|
-
if (seen.has(key)) {
|
|
86
|
-
mutated = true;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
seen.add(key);
|
|
90
|
-
}
|
|
91
|
-
reversedKept.push(change);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return mutated ? reversedKept.reverse() : changes;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Apply structural rewrites to the change list that are only obvious once
|
|
99
|
-
* every object diff has been collected. This pass does NOT prevent dependency
|
|
100
|
-
* cycles — that responsibility now lives in the sort phase, where
|
|
101
|
-
* `sortPhaseChanges` invokes `tryBreakCycleByChangeInjection` lazily on cycles
|
|
102
|
-
* that edge filtering can't break (FK SCC of dropped tables,
|
|
103
|
-
* AlterPublicationDropTables ↔ AlterTableDropColumn, …).
|
|
104
|
-
*
|
|
105
|
-
* Concretely, this pass:
|
|
106
|
-
*
|
|
107
|
-
* - Prunes `AlterTableDropColumn(T.*)` / `AlterTableDropConstraint(T.*)`
|
|
108
|
-
* changes that are made redundant by an expansion-emitted
|
|
109
|
-
* `DropTable(T) + CreateTable(T)` pair. Without this, the apply phase
|
|
110
|
-
* would try to drop a column that no longer exists in the freshly
|
|
111
|
-
* recreated table.
|
|
112
|
-
* - Dedupes duplicate `AlterTableAddConstraint` /
|
|
113
|
-
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` changes
|
|
114
|
-
* produced when `diffTables()` and `expandReplaceDependencies()` both
|
|
115
|
-
* emit the same constraint operation for a replaced table. Last write
|
|
116
|
-
* wins so the expansion's emission survives.
|
|
117
|
-
*
|
|
118
|
-
* Object-local PostgreSQL semantics (for example owned-sequence cascades)
|
|
119
|
-
* stay in the corresponding `diff*` function instead of this pass.
|
|
120
|
-
*/
|
|
121
|
-
export function normalizePostDiffCycles({
|
|
122
|
-
changes,
|
|
123
|
-
replacedTableIds = new Set<string>(),
|
|
124
|
-
}: {
|
|
125
|
-
changes: Change[];
|
|
126
|
-
replacedTableIds?: ReadonlySet<string>;
|
|
127
|
-
}): Change[] {
|
|
128
|
-
const dedupedChanges = dropReplacedTableDuplicateConstraintChanges(
|
|
129
|
-
changes,
|
|
130
|
-
replacedTableIds,
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
if (replacedTableIds.size === 0) return dedupedChanges;
|
|
134
|
-
|
|
135
|
-
return dedupedChanges.filter(
|
|
136
|
-
(change) => !isSupersededByTableReplacement(change, replacedTableIds),
|
|
137
|
-
);
|
|
138
|
-
}
|