@supabase/pg-delta 1.0.0-alpha.20 → 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 -4
- 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/publication/changes/publication.alter.d.ts +1 -1
- 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 +41 -9
- package/dist/core/objects/table/changes/table.alter.d.ts +16 -1
- package/dist/core/objects/table/changes/table.alter.js +39 -6
- package/dist/core/objects/table/table.diff.js +40 -17
- package/dist/core/objects/table/table.model.d.ts +6 -1
- package/dist/core/objects/table/table.model.js +50 -12
- 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-normalization.d.ts +36 -0
- package/dist/core/post-diff-normalization.js +202 -0
- package/dist/core/sort/cycle-breakers.d.ts +15 -0
- package/dist/core/sort/cycle-breakers.js +269 -0
- package/dist/core/sort/sort-changes.js +97 -43
- 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 -3
- package/src/core/catalog.model.ts +20 -8
- package/src/core/expand-replace-dependencies.test.ts +139 -5
- 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/publication/changes/publication.alter.ts +1 -1
- 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 +93 -1
- package/src/core/objects/sequence/sequence.diff.ts +43 -10
- package/src/core/objects/table/changes/table.alter.test.ts +26 -23
- package/src/core/objects/table/changes/table.alter.ts +66 -10
- package/src/core/objects/table/table.diff.test.ts +43 -0
- package/src/core/objects/table/table.diff.ts +52 -23
- package/src/core/objects/table/table.model.test.ts +209 -0
- package/src/core/objects/table/table.model.ts +62 -14
- 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 -160
- package/src/core/post-diff-normalization.ts +260 -0
- package/src/core/sort/cycle-breakers.test.ts +476 -0
- package/src/core/sort/cycle-breakers.ts +311 -0
- package/src/core/sort/sort-changes.ts +135 -50
- package/src/core/sort/utils.ts +38 -0
- package/dist/core/post-diff-cycle-breaking.d.ts +0 -29
- package/dist/core/post-diff-cycle-breaking.js +0 -209
- package/src/core/post-diff-cycle-breaking.ts +0 -317
|
@@ -1,209 +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 { DropTable } from "./objects/table/changes/table.drop.js";
|
|
4
|
-
import { stableId } from "./objects/utils.js";
|
|
5
|
-
function constraintStableId(table, constraintName) {
|
|
6
|
-
return stableId.constraint(table.schema, table.name, constraintName);
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Yield FK constraints on `table` whose referenced table is also dropped in the
|
|
10
|
-
* final plan. Self-references are left alone because the sort phase already
|
|
11
|
-
* handles the resulting self-loop correctly.
|
|
12
|
-
*/
|
|
13
|
-
function* iterCrossDropFkConstraints(table, droppedSet) {
|
|
14
|
-
for (const constraint of table.constraints) {
|
|
15
|
-
if (constraint.constraint_type !== "f")
|
|
16
|
-
continue;
|
|
17
|
-
if (constraint.is_partition_clone)
|
|
18
|
-
continue;
|
|
19
|
-
if (!constraint.foreign_key_schema || !constraint.foreign_key_table) {
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
const referencedId = stableId.table(constraint.foreign_key_schema, constraint.foreign_key_table);
|
|
23
|
-
if (referencedId === table.stableId)
|
|
24
|
-
continue;
|
|
25
|
-
if (!droppedSet.has(referencedId))
|
|
26
|
-
continue;
|
|
27
|
-
yield { constraint, referencedId };
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function isSupersededByTableReplacement(change, replacedTableIds) {
|
|
31
|
-
if (!(change instanceof AlterTableDropColumn) &&
|
|
32
|
-
!(change instanceof AlterTableDropConstraint)) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return replacedTableIds.has(change.table.stableId);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Drop earlier duplicates of `AlterTableAddConstraint` /
|
|
39
|
-
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` targeting
|
|
40
|
-
* replaced tables, keeping only the last occurrence of each
|
|
41
|
-
* `(changeType, table.stableId, constraint.name)`.
|
|
42
|
-
*
|
|
43
|
-
* When `expandReplaceDependencies()` promotes a table to a full
|
|
44
|
-
* `DropTable + CreateTable` pair, it also emits one
|
|
45
|
-
* `AlterTableAddConstraint` (plus optional `VALIDATE CONSTRAINT` /
|
|
46
|
-
* `COMMENT ON CONSTRAINT`) per branch constraint. If `diffTables()` already
|
|
47
|
-
* emitted the same change for a shape flip or a new constraint on that
|
|
48
|
-
* table, the plan ends up with two identical `ALTER TABLE ... ADD
|
|
49
|
-
* CONSTRAINT ...` statements and PostgreSQL fails at apply time with
|
|
50
|
-
* `constraint "..." for relation "..." already exists`. Because
|
|
51
|
-
* `expandReplaceDependencies()` appends its additions after the original
|
|
52
|
-
* `diffTables()` output, the last occurrence is the expansion's emission —
|
|
53
|
-
* keeping it preserves correctness while removing the duplicate.
|
|
54
|
-
*/
|
|
55
|
-
function dropReplacedTableDuplicateConstraintChanges(changes, replacedTableIds) {
|
|
56
|
-
if (replacedTableIds.size === 0)
|
|
57
|
-
return changes;
|
|
58
|
-
const keyFor = (change) => {
|
|
59
|
-
if (!(change instanceof AlterTableAddConstraint) &&
|
|
60
|
-
!(change instanceof AlterTableValidateConstraint) &&
|
|
61
|
-
!(change instanceof CreateCommentOnConstraint)) {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
if (!replacedTableIds.has(change.table.stableId))
|
|
65
|
-
return null;
|
|
66
|
-
const tag = change instanceof AlterTableAddConstraint
|
|
67
|
-
? "add"
|
|
68
|
-
: change instanceof AlterTableValidateConstraint
|
|
69
|
-
? "validate"
|
|
70
|
-
: "comment";
|
|
71
|
-
return `${tag}:${constraintStableId(change.table, change.constraint.name)}`;
|
|
72
|
-
};
|
|
73
|
-
const seen = new Set();
|
|
74
|
-
const reversedKept = [];
|
|
75
|
-
let mutated = false;
|
|
76
|
-
// Walk backwards: the first encounter of each key corresponds to its LAST
|
|
77
|
-
// occurrence in the original order. `expandReplaceDependencies()` appends
|
|
78
|
-
// additions after the original changes, so "last wins" keeps the
|
|
79
|
-
// expansion's emission and drops the earlier diffTables duplicate.
|
|
80
|
-
for (let i = changes.length - 1; i >= 0; i--) {
|
|
81
|
-
const change = changes[i];
|
|
82
|
-
const key = keyFor(change);
|
|
83
|
-
if (key !== null) {
|
|
84
|
-
if (seen.has(key)) {
|
|
85
|
-
mutated = true;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
seen.add(key);
|
|
89
|
-
}
|
|
90
|
-
reversedKept.push(change);
|
|
91
|
-
}
|
|
92
|
-
return mutated ? reversedKept.reverse() : changes;
|
|
93
|
-
}
|
|
94
|
-
function collectExplicitConstraintDropIds(changes) {
|
|
95
|
-
const explicitConstraintDropIds = new Set();
|
|
96
|
-
for (const change of changes) {
|
|
97
|
-
if (!(change instanceof AlterTableDropConstraint))
|
|
98
|
-
continue;
|
|
99
|
-
explicitConstraintDropIds.add(constraintStableId(change.table, change.constraint.name));
|
|
100
|
-
}
|
|
101
|
-
return explicitConstraintDropIds;
|
|
102
|
-
}
|
|
103
|
-
function hasSameEntries(left, right) {
|
|
104
|
-
if (left.size !== right.size)
|
|
105
|
-
return false;
|
|
106
|
-
for (const value of left) {
|
|
107
|
-
if (!right.has(value))
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Normalize change-list cycles that only become apparent after all object
|
|
114
|
-
* diffs have been collected.
|
|
115
|
-
*
|
|
116
|
-
* This pass intentionally handles whole-plan interactions only:
|
|
117
|
-
* - If replace expansion added `DropTable(T)+CreateTable(T)`, targeted
|
|
118
|
-
* `AlterTableDropColumn(T.*)` / `AlterTableDropConstraint(T.*)` changes are
|
|
119
|
-
* redundant and create an unbreakable drop-phase cycle, so we elide them.
|
|
120
|
-
* - When the same `DropTable+CreateTable` pair is present, the expansion
|
|
121
|
-
* also emits one `AlterTableAddConstraint` / `AlterTableValidateConstraint`
|
|
122
|
-
* / `CreateCommentOnConstraint` per branch constraint, which may collide
|
|
123
|
-
* with the same change already emitted by `diffTables()` (for example on a
|
|
124
|
-
* shape flip or a new constraint). We dedupe these keeping only the last
|
|
125
|
-
* occurrence so the expansion's emission survives and the diffTables
|
|
126
|
-
* duplicate is removed.
|
|
127
|
-
* - If two dropped tables reference each other via FK, we insert dedicated
|
|
128
|
-
* `AlterTableDropConstraint` changes and teach the paired `DropTable`
|
|
129
|
-
* changes not to claim those FK stable IDs.
|
|
130
|
-
*
|
|
131
|
-
* Object-local PostgreSQL semantics (for example owned-sequence cascades) stay
|
|
132
|
-
* in the corresponding `diff*` function instead of this pass.
|
|
133
|
-
*/
|
|
134
|
-
export function normalizePostDiffCycles({ changes, mainCatalog, replacedTableIds = new Set(), }) {
|
|
135
|
-
const dedupedChanges = dropReplacedTableDuplicateConstraintChanges(changes, replacedTableIds);
|
|
136
|
-
const structurallyNormalizedChanges = replacedTableIds.size === 0
|
|
137
|
-
? dedupedChanges
|
|
138
|
-
: dedupedChanges.filter((change) => !isSupersededByTableReplacement(change, replacedTableIds));
|
|
139
|
-
const dropTableChanges = structurallyNormalizedChanges.filter((change) => change instanceof DropTable);
|
|
140
|
-
if (dropTableChanges.length < 2) {
|
|
141
|
-
return structurallyNormalizedChanges;
|
|
142
|
-
}
|
|
143
|
-
const droppedSet = new Set(dropTableChanges.map((change) => change.table.stableId));
|
|
144
|
-
const droppedFkTargets = new Map();
|
|
145
|
-
for (const dropTableChange of dropTableChanges) {
|
|
146
|
-
const mainTable = mainCatalog.tables[dropTableChange.table.stableId] ??
|
|
147
|
-
dropTableChange.table;
|
|
148
|
-
const targets = new Set();
|
|
149
|
-
for (const { referencedId } of iterCrossDropFkConstraints(mainTable, droppedSet)) {
|
|
150
|
-
targets.add(referencedId);
|
|
151
|
-
}
|
|
152
|
-
droppedFkTargets.set(mainTable.stableId, targets);
|
|
153
|
-
}
|
|
154
|
-
const explicitConstraintDropIds = collectExplicitConstraintDropIds(structurallyNormalizedChanges);
|
|
155
|
-
const injectedConstraintDropsByTableId = new Map();
|
|
156
|
-
const externallyDroppedConstraintsByTableId = new Map();
|
|
157
|
-
let didMutate = structurallyNormalizedChanges !== changes;
|
|
158
|
-
for (const dropTableChange of dropTableChanges) {
|
|
159
|
-
const mainTable = mainCatalog.tables[dropTableChange.table.stableId] ??
|
|
160
|
-
dropTableChange.table;
|
|
161
|
-
const externallyDroppedConstraints = new Set(dropTableChange.externallyDroppedConstraints);
|
|
162
|
-
for (const { constraint, referencedId } of iterCrossDropFkConstraints(mainTable, droppedSet)) {
|
|
163
|
-
const isMutual = droppedFkTargets.get(referencedId)?.has(mainTable.stableId) === true;
|
|
164
|
-
if (!isMutual)
|
|
165
|
-
continue;
|
|
166
|
-
const droppedConstraintStableId = constraintStableId(mainTable, constraint.name);
|
|
167
|
-
externallyDroppedConstraints.add(constraint.name);
|
|
168
|
-
if (!explicitConstraintDropIds.has(droppedConstraintStableId)) {
|
|
169
|
-
const injectedDrop = new AlterTableDropConstraint({
|
|
170
|
-
table: mainTable,
|
|
171
|
-
constraint,
|
|
172
|
-
});
|
|
173
|
-
const existingDrops = injectedConstraintDropsByTableId.get(mainTable.stableId) ?? [];
|
|
174
|
-
existingDrops.push(injectedDrop);
|
|
175
|
-
injectedConstraintDropsByTableId.set(mainTable.stableId, existingDrops);
|
|
176
|
-
explicitConstraintDropIds.add(droppedConstraintStableId);
|
|
177
|
-
didMutate = true;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (!hasSameEntries(dropTableChange.externallyDroppedConstraints, externallyDroppedConstraints)) {
|
|
181
|
-
externallyDroppedConstraintsByTableId.set(mainTable.stableId, externallyDroppedConstraints);
|
|
182
|
-
didMutate = true;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (!didMutate) {
|
|
186
|
-
return changes;
|
|
187
|
-
}
|
|
188
|
-
const normalizedChanges = [];
|
|
189
|
-
for (const change of structurallyNormalizedChanges) {
|
|
190
|
-
if (!(change instanceof DropTable)) {
|
|
191
|
-
normalizedChanges.push(change);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const injectedConstraintDrops = injectedConstraintDropsByTableId.get(change.table.stableId) ?? [];
|
|
195
|
-
if (injectedConstraintDrops.length > 0) {
|
|
196
|
-
normalizedChanges.push(...injectedConstraintDrops);
|
|
197
|
-
}
|
|
198
|
-
const externallyDroppedConstraints = externallyDroppedConstraintsByTableId.get(change.table.stableId);
|
|
199
|
-
if (!externallyDroppedConstraints) {
|
|
200
|
-
normalizedChanges.push(change);
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
normalizedChanges.push(new DropTable({
|
|
204
|
-
table: change.table,
|
|
205
|
-
externallyDroppedConstraints,
|
|
206
|
-
}));
|
|
207
|
-
}
|
|
208
|
-
return normalizedChanges;
|
|
209
|
-
}
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import type { Catalog } from "./catalog.model.ts";
|
|
2
|
-
import type { Change } from "./change.types.ts";
|
|
3
|
-
import {
|
|
4
|
-
AlterTableAddConstraint,
|
|
5
|
-
AlterTableDropColumn,
|
|
6
|
-
AlterTableDropConstraint,
|
|
7
|
-
AlterTableValidateConstraint,
|
|
8
|
-
} from "./objects/table/changes/table.alter.ts";
|
|
9
|
-
import { CreateCommentOnConstraint } from "./objects/table/changes/table.comment.ts";
|
|
10
|
-
import { DropTable } from "./objects/table/changes/table.drop.ts";
|
|
11
|
-
import { stableId } from "./objects/utils.ts";
|
|
12
|
-
|
|
13
|
-
function constraintStableId(
|
|
14
|
-
table: { schema: string; name: string },
|
|
15
|
-
constraintName: string,
|
|
16
|
-
) {
|
|
17
|
-
return stableId.constraint(table.schema, table.name, constraintName);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Yield FK constraints on `table` whose referenced table is also dropped in the
|
|
22
|
-
* final plan. Self-references are left alone because the sort phase already
|
|
23
|
-
* handles the resulting self-loop correctly.
|
|
24
|
-
*/
|
|
25
|
-
function* iterCrossDropFkConstraints(
|
|
26
|
-
table: Catalog["tables"][string],
|
|
27
|
-
droppedSet: ReadonlySet<string>,
|
|
28
|
-
) {
|
|
29
|
-
for (const constraint of table.constraints) {
|
|
30
|
-
if (constraint.constraint_type !== "f") continue;
|
|
31
|
-
if (constraint.is_partition_clone) continue;
|
|
32
|
-
if (!constraint.foreign_key_schema || !constraint.foreign_key_table) {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const referencedId = stableId.table(
|
|
36
|
-
constraint.foreign_key_schema,
|
|
37
|
-
constraint.foreign_key_table,
|
|
38
|
-
);
|
|
39
|
-
if (referencedId === table.stableId) continue;
|
|
40
|
-
if (!droppedSet.has(referencedId)) continue;
|
|
41
|
-
yield { constraint, referencedId };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function isSupersededByTableReplacement(
|
|
46
|
-
change: Change,
|
|
47
|
-
replacedTableIds: ReadonlySet<string>,
|
|
48
|
-
): boolean {
|
|
49
|
-
if (
|
|
50
|
-
!(change instanceof AlterTableDropColumn) &&
|
|
51
|
-
!(change instanceof AlterTableDropConstraint)
|
|
52
|
-
) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
return replacedTableIds.has(change.table.stableId);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Drop earlier duplicates of `AlterTableAddConstraint` /
|
|
60
|
-
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` targeting
|
|
61
|
-
* replaced tables, keeping only the last occurrence of each
|
|
62
|
-
* `(changeType, table.stableId, constraint.name)`.
|
|
63
|
-
*
|
|
64
|
-
* When `expandReplaceDependencies()` promotes a table to a full
|
|
65
|
-
* `DropTable + CreateTable` pair, it also emits one
|
|
66
|
-
* `AlterTableAddConstraint` (plus optional `VALIDATE CONSTRAINT` /
|
|
67
|
-
* `COMMENT ON CONSTRAINT`) per branch constraint. If `diffTables()` already
|
|
68
|
-
* emitted the same change for a shape flip or a new constraint on that
|
|
69
|
-
* table, the plan ends up with two identical `ALTER TABLE ... ADD
|
|
70
|
-
* CONSTRAINT ...` statements and PostgreSQL fails at apply time with
|
|
71
|
-
* `constraint "..." for relation "..." already exists`. Because
|
|
72
|
-
* `expandReplaceDependencies()` appends its additions after the original
|
|
73
|
-
* `diffTables()` output, the last occurrence is the expansion's emission —
|
|
74
|
-
* keeping it preserves correctness while removing the duplicate.
|
|
75
|
-
*/
|
|
76
|
-
function dropReplacedTableDuplicateConstraintChanges(
|
|
77
|
-
changes: Change[],
|
|
78
|
-
replacedTableIds: ReadonlySet<string>,
|
|
79
|
-
): Change[] {
|
|
80
|
-
if (replacedTableIds.size === 0) return changes;
|
|
81
|
-
|
|
82
|
-
const keyFor = (change: Change): string | null => {
|
|
83
|
-
if (
|
|
84
|
-
!(change instanceof AlterTableAddConstraint) &&
|
|
85
|
-
!(change instanceof AlterTableValidateConstraint) &&
|
|
86
|
-
!(change instanceof CreateCommentOnConstraint)
|
|
87
|
-
) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
if (!replacedTableIds.has(change.table.stableId)) return null;
|
|
91
|
-
const tag =
|
|
92
|
-
change instanceof AlterTableAddConstraint
|
|
93
|
-
? "add"
|
|
94
|
-
: change instanceof AlterTableValidateConstraint
|
|
95
|
-
? "validate"
|
|
96
|
-
: "comment";
|
|
97
|
-
return `${tag}:${constraintStableId(change.table, change.constraint.name)}`;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const seen = new Set<string>();
|
|
101
|
-
const reversedKept: Change[] = [];
|
|
102
|
-
let mutated = false;
|
|
103
|
-
|
|
104
|
-
// Walk backwards: the first encounter of each key corresponds to its LAST
|
|
105
|
-
// occurrence in the original order. `expandReplaceDependencies()` appends
|
|
106
|
-
// additions after the original changes, so "last wins" keeps the
|
|
107
|
-
// expansion's emission and drops the earlier diffTables duplicate.
|
|
108
|
-
for (let i = changes.length - 1; i >= 0; i--) {
|
|
109
|
-
const change = changes[i] as Change;
|
|
110
|
-
const key = keyFor(change);
|
|
111
|
-
if (key !== null) {
|
|
112
|
-
if (seen.has(key)) {
|
|
113
|
-
mutated = true;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
seen.add(key);
|
|
117
|
-
}
|
|
118
|
-
reversedKept.push(change);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return mutated ? reversedKept.reverse() : changes;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function collectExplicitConstraintDropIds(changes: Change[]) {
|
|
125
|
-
const explicitConstraintDropIds = new Set<string>();
|
|
126
|
-
|
|
127
|
-
for (const change of changes) {
|
|
128
|
-
if (!(change instanceof AlterTableDropConstraint)) continue;
|
|
129
|
-
explicitConstraintDropIds.add(
|
|
130
|
-
constraintStableId(change.table, change.constraint.name),
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return explicitConstraintDropIds;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function hasSameEntries(
|
|
138
|
-
left: ReadonlySet<string>,
|
|
139
|
-
right: ReadonlySet<string>,
|
|
140
|
-
): boolean {
|
|
141
|
-
if (left.size !== right.size) return false;
|
|
142
|
-
for (const value of left) {
|
|
143
|
-
if (!right.has(value)) return false;
|
|
144
|
-
}
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Normalize change-list cycles that only become apparent after all object
|
|
150
|
-
* diffs have been collected.
|
|
151
|
-
*
|
|
152
|
-
* This pass intentionally handles whole-plan interactions only:
|
|
153
|
-
* - If replace expansion added `DropTable(T)+CreateTable(T)`, targeted
|
|
154
|
-
* `AlterTableDropColumn(T.*)` / `AlterTableDropConstraint(T.*)` changes are
|
|
155
|
-
* redundant and create an unbreakable drop-phase cycle, so we elide them.
|
|
156
|
-
* - When the same `DropTable+CreateTable` pair is present, the expansion
|
|
157
|
-
* also emits one `AlterTableAddConstraint` / `AlterTableValidateConstraint`
|
|
158
|
-
* / `CreateCommentOnConstraint` per branch constraint, which may collide
|
|
159
|
-
* with the same change already emitted by `diffTables()` (for example on a
|
|
160
|
-
* shape flip or a new constraint). We dedupe these keeping only the last
|
|
161
|
-
* occurrence so the expansion's emission survives and the diffTables
|
|
162
|
-
* duplicate is removed.
|
|
163
|
-
* - If two dropped tables reference each other via FK, we insert dedicated
|
|
164
|
-
* `AlterTableDropConstraint` changes and teach the paired `DropTable`
|
|
165
|
-
* changes not to claim those FK stable IDs.
|
|
166
|
-
*
|
|
167
|
-
* Object-local PostgreSQL semantics (for example owned-sequence cascades) stay
|
|
168
|
-
* in the corresponding `diff*` function instead of this pass.
|
|
169
|
-
*/
|
|
170
|
-
export function normalizePostDiffCycles({
|
|
171
|
-
changes,
|
|
172
|
-
mainCatalog,
|
|
173
|
-
replacedTableIds = new Set<string>(),
|
|
174
|
-
}: {
|
|
175
|
-
changes: Change[];
|
|
176
|
-
mainCatalog: Catalog;
|
|
177
|
-
replacedTableIds?: ReadonlySet<string>;
|
|
178
|
-
}): Change[] {
|
|
179
|
-
const dedupedChanges = dropReplacedTableDuplicateConstraintChanges(
|
|
180
|
-
changes,
|
|
181
|
-
replacedTableIds,
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
const structurallyNormalizedChanges =
|
|
185
|
-
replacedTableIds.size === 0
|
|
186
|
-
? dedupedChanges
|
|
187
|
-
: dedupedChanges.filter(
|
|
188
|
-
(change) => !isSupersededByTableReplacement(change, replacedTableIds),
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const dropTableChanges = structurallyNormalizedChanges.filter(
|
|
192
|
-
(change): change is DropTable => change instanceof DropTable,
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
if (dropTableChanges.length < 2) {
|
|
196
|
-
return structurallyNormalizedChanges;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const droppedSet = new Set(
|
|
200
|
-
dropTableChanges.map((change) => change.table.stableId),
|
|
201
|
-
);
|
|
202
|
-
const droppedFkTargets = new Map<string, Set<string>>();
|
|
203
|
-
|
|
204
|
-
for (const dropTableChange of dropTableChanges) {
|
|
205
|
-
const mainTable =
|
|
206
|
-
mainCatalog.tables[dropTableChange.table.stableId] ??
|
|
207
|
-
dropTableChange.table;
|
|
208
|
-
const targets = new Set<string>();
|
|
209
|
-
|
|
210
|
-
for (const { referencedId } of iterCrossDropFkConstraints(
|
|
211
|
-
mainTable,
|
|
212
|
-
droppedSet,
|
|
213
|
-
)) {
|
|
214
|
-
targets.add(referencedId);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
droppedFkTargets.set(mainTable.stableId, targets);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const explicitConstraintDropIds = collectExplicitConstraintDropIds(
|
|
221
|
-
structurallyNormalizedChanges,
|
|
222
|
-
);
|
|
223
|
-
const injectedConstraintDropsByTableId = new Map<
|
|
224
|
-
string,
|
|
225
|
-
AlterTableDropConstraint[]
|
|
226
|
-
>();
|
|
227
|
-
const externallyDroppedConstraintsByTableId = new Map<
|
|
228
|
-
string,
|
|
229
|
-
ReadonlySet<string>
|
|
230
|
-
>();
|
|
231
|
-
let didMutate = structurallyNormalizedChanges !== changes;
|
|
232
|
-
|
|
233
|
-
for (const dropTableChange of dropTableChanges) {
|
|
234
|
-
const mainTable =
|
|
235
|
-
mainCatalog.tables[dropTableChange.table.stableId] ??
|
|
236
|
-
dropTableChange.table;
|
|
237
|
-
const externallyDroppedConstraints = new Set(
|
|
238
|
-
dropTableChange.externallyDroppedConstraints,
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
for (const { constraint, referencedId } of iterCrossDropFkConstraints(
|
|
242
|
-
mainTable,
|
|
243
|
-
droppedSet,
|
|
244
|
-
)) {
|
|
245
|
-
const isMutual =
|
|
246
|
-
droppedFkTargets.get(referencedId)?.has(mainTable.stableId) === true;
|
|
247
|
-
if (!isMutual) continue;
|
|
248
|
-
|
|
249
|
-
const droppedConstraintStableId = constraintStableId(
|
|
250
|
-
mainTable,
|
|
251
|
-
constraint.name,
|
|
252
|
-
);
|
|
253
|
-
externallyDroppedConstraints.add(constraint.name);
|
|
254
|
-
|
|
255
|
-
if (!explicitConstraintDropIds.has(droppedConstraintStableId)) {
|
|
256
|
-
const injectedDrop = new AlterTableDropConstraint({
|
|
257
|
-
table: mainTable,
|
|
258
|
-
constraint,
|
|
259
|
-
});
|
|
260
|
-
const existingDrops =
|
|
261
|
-
injectedConstraintDropsByTableId.get(mainTable.stableId) ?? [];
|
|
262
|
-
existingDrops.push(injectedDrop);
|
|
263
|
-
injectedConstraintDropsByTableId.set(mainTable.stableId, existingDrops);
|
|
264
|
-
explicitConstraintDropIds.add(droppedConstraintStableId);
|
|
265
|
-
didMutate = true;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (
|
|
270
|
-
!hasSameEntries(
|
|
271
|
-
dropTableChange.externallyDroppedConstraints,
|
|
272
|
-
externallyDroppedConstraints,
|
|
273
|
-
)
|
|
274
|
-
) {
|
|
275
|
-
externallyDroppedConstraintsByTableId.set(
|
|
276
|
-
mainTable.stableId,
|
|
277
|
-
externallyDroppedConstraints,
|
|
278
|
-
);
|
|
279
|
-
didMutate = true;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (!didMutate) {
|
|
284
|
-
return changes;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const normalizedChanges: Change[] = [];
|
|
288
|
-
|
|
289
|
-
for (const change of structurallyNormalizedChanges) {
|
|
290
|
-
if (!(change instanceof DropTable)) {
|
|
291
|
-
normalizedChanges.push(change);
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const injectedConstraintDrops =
|
|
296
|
-
injectedConstraintDropsByTableId.get(change.table.stableId) ?? [];
|
|
297
|
-
if (injectedConstraintDrops.length > 0) {
|
|
298
|
-
normalizedChanges.push(...injectedConstraintDrops);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const externallyDroppedConstraints =
|
|
302
|
-
externallyDroppedConstraintsByTableId.get(change.table.stableId);
|
|
303
|
-
if (!externallyDroppedConstraints) {
|
|
304
|
-
normalizedChanges.push(change);
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
normalizedChanges.push(
|
|
309
|
-
new DropTable({
|
|
310
|
-
table: change.table,
|
|
311
|
-
externallyDroppedConstraints,
|
|
312
|
-
}),
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
return normalizedChanges;
|
|
317
|
-
}
|