@supabase/pg-delta 1.0.0-alpha.20 → 1.0.0-alpha.21
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 +0 -1
- package/dist/core/objects/publication/changes/publication.alter.d.ts +1 -1
- package/dist/core/objects/sequence/sequence.diff.js +13 -5
- package/dist/core/objects/table/changes/table.alter.d.ts +4 -0
- package/dist/core/objects/table/changes/table.alter.js +19 -4
- package/dist/core/objects/table/table.diff.js +21 -2
- package/dist/core/objects/table/table.model.js +10 -7
- package/dist/core/post-diff-cycle-breaking.d.ts +21 -21
- package/dist/core/post-diff-cycle-breaking.js +24 -133
- 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/package.json +1 -1
- package/src/core/catalog.diff.ts +0 -1
- package/src/core/expand-replace-dependencies.test.ts +8 -5
- package/src/core/objects/publication/changes/publication.alter.ts +1 -1
- package/src/core/objects/sequence/sequence.diff.test.ts +6 -1
- package/src/core/objects/sequence/sequence.diff.ts +12 -4
- package/src/core/objects/table/changes/table.alter.test.ts +13 -2
- package/src/core/objects/table/changes/table.alter.ts +36 -7
- package/src/core/objects/table/table.diff.test.ts +43 -0
- package/src/core/objects/table/table.diff.ts +28 -4
- package/src/core/objects/table/table.model.ts +10 -7
- package/src/core/post-diff-cycle-breaking.test.ts +0 -156
- package/src/core/post-diff-cycle-breaking.ts +23 -202
- 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
|
@@ -161,7 +161,6 @@ export function diffCatalogs(main, branch, options) {
|
|
|
161
161
|
});
|
|
162
162
|
filteredChanges = normalizePostDiffCycles({
|
|
163
163
|
changes: expandedDependencies.changes,
|
|
164
|
-
mainCatalog: main,
|
|
165
164
|
replacedTableIds: expandedDependencies.replacedTableIds,
|
|
166
165
|
});
|
|
167
166
|
debugCatalog("changes catalog diff: %O", stringifyWithBigInt(filteredChanges, 2));
|
|
@@ -37,7 +37,7 @@ export declare class AlterPublicationAddTables extends AlterPublicationChange {
|
|
|
37
37
|
export declare class AlterPublicationDropTables extends AlterPublicationChange {
|
|
38
38
|
readonly publication: Publication;
|
|
39
39
|
readonly scope: "object";
|
|
40
|
-
|
|
40
|
+
readonly tables: PublicationTableProps[];
|
|
41
41
|
constructor(props: {
|
|
42
42
|
publication: Publication;
|
|
43
43
|
tables: PublicationTableProps[];
|
|
@@ -87,10 +87,7 @@ export function diffSequences(ctx, main, branch, branchTables = {}) {
|
|
|
87
87
|
const branchSequence = branch[sequenceId];
|
|
88
88
|
// Check if non-alterable properties have changed
|
|
89
89
|
// These require dropping and recreating the sequence
|
|
90
|
-
const NON_ALTERABLE_FIELDS = [
|
|
91
|
-
"data_type",
|
|
92
|
-
"persistence",
|
|
93
|
-
];
|
|
90
|
+
const NON_ALTERABLE_FIELDS = ["persistence"];
|
|
94
91
|
const nonAlterablePropsChanged = hasNonAlterableChanges(mainSequence, branchSequence, NON_ALTERABLE_FIELDS);
|
|
95
92
|
if (nonAlterablePropsChanged) {
|
|
96
93
|
// Replace the entire sequence (drop + create)
|
|
@@ -131,7 +128,8 @@ export function diffSequences(ctx, main, branch, branchTables = {}) {
|
|
|
131
128
|
}
|
|
132
129
|
else {
|
|
133
130
|
// Only alterable properties changed - emit ALTER for options/owner
|
|
134
|
-
const optionsChanged = mainSequence.
|
|
131
|
+
const optionsChanged = mainSequence.data_type !== branchSequence.data_type ||
|
|
132
|
+
mainSequence.increment !== branchSequence.increment ||
|
|
135
133
|
mainSequence.minimum_value !== branchSequence.minimum_value ||
|
|
136
134
|
mainSequence.maximum_value !== branchSequence.maximum_value ||
|
|
137
135
|
mainSequence.start_value !== branchSequence.start_value ||
|
|
@@ -139,6 +137,16 @@ export function diffSequences(ctx, main, branch, branchTables = {}) {
|
|
|
139
137
|
mainSequence.cycle_option !== branchSequence.cycle_option;
|
|
140
138
|
if (optionsChanged) {
|
|
141
139
|
const options = [];
|
|
140
|
+
// `AS <type>` must come before any MIN/MAX/RESTART clauses per the
|
|
141
|
+
// PG ALTER SEQUENCE grammar. Valid types are smallint, integer,
|
|
142
|
+
// bigint — the same set CREATE SEQUENCE accepts — so the universe
|
|
143
|
+
// of legal transitions is closed. PG enforces last_value range at
|
|
144
|
+
// apply time when shrinking; that's the desired behavior because
|
|
145
|
+
// the previous Drop+Create path silently reset last_value to 1
|
|
146
|
+
// (data-loss bug, see Sentry SUPABASE-API-7RS).
|
|
147
|
+
if (mainSequence.data_type !== branchSequence.data_type) {
|
|
148
|
+
options.push("AS", branchSequence.data_type);
|
|
149
|
+
}
|
|
142
150
|
if (mainSequence.increment !== branchSequence.increment) {
|
|
143
151
|
options.push("INCREMENT BY", String(branchSequence.increment));
|
|
144
152
|
}
|
|
@@ -250,9 +250,11 @@ export declare class AlterTableDropColumn extends AlterTableChange {
|
|
|
250
250
|
readonly table: Table;
|
|
251
251
|
readonly column: ColumnProps;
|
|
252
252
|
readonly scope: "object";
|
|
253
|
+
readonly omitTableRequirement: boolean;
|
|
253
254
|
constructor(props: {
|
|
254
255
|
table: Table;
|
|
255
256
|
column: ColumnProps;
|
|
257
|
+
omitTableRequirement?: boolean;
|
|
256
258
|
});
|
|
257
259
|
get drops(): `column:${string}.${string}.${string}`[];
|
|
258
260
|
get requires(): (`column:${string}.${string}.${string}` | `table:${string}`)[];
|
|
@@ -264,10 +266,12 @@ export declare class AlterTableDropColumn extends AlterTableChange {
|
|
|
264
266
|
export declare class AlterTableAlterColumnType extends AlterTableChange {
|
|
265
267
|
readonly table: Table;
|
|
266
268
|
readonly column: ColumnProps;
|
|
269
|
+
readonly previousColumn?: ColumnProps;
|
|
267
270
|
readonly scope: "object";
|
|
268
271
|
constructor(props: {
|
|
269
272
|
table: Table;
|
|
270
273
|
column: ColumnProps;
|
|
274
|
+
previousColumn?: ColumnProps;
|
|
271
275
|
});
|
|
272
276
|
get requires(): `column:${string}.${string}.${string}`[];
|
|
273
277
|
serialize(_options?: SerializeOptions): string;
|
|
@@ -386,10 +386,16 @@ export class AlterTableDropColumn extends AlterTableChange {
|
|
|
386
386
|
table;
|
|
387
387
|
column;
|
|
388
388
|
scope = "object";
|
|
389
|
+
// Drop the implicit `requires(table)` edge. Only set by the lazy
|
|
390
|
+
// cycle-breaker for the publication↔column case, where the table survives
|
|
391
|
+
// the migration and the edge is therefore artificial. See
|
|
392
|
+
// `sort/cycle-breakers.ts` for the full justification.
|
|
393
|
+
omitTableRequirement;
|
|
389
394
|
constructor(props) {
|
|
390
395
|
super();
|
|
391
396
|
this.table = props.table;
|
|
392
397
|
this.column = props.column;
|
|
398
|
+
this.omitTableRequirement = props.omitTableRequirement ?? false;
|
|
393
399
|
}
|
|
394
400
|
get drops() {
|
|
395
401
|
return [
|
|
@@ -397,10 +403,8 @@ export class AlterTableDropColumn extends AlterTableChange {
|
|
|
397
403
|
];
|
|
398
404
|
}
|
|
399
405
|
get requires() {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
stableId.column(this.table.schema, this.table.name, this.column.name),
|
|
403
|
-
];
|
|
406
|
+
const colId = stableId.column(this.table.schema, this.table.name, this.column.name);
|
|
407
|
+
return this.omitTableRequirement ? [colId] : [this.table.stableId, colId];
|
|
404
408
|
}
|
|
405
409
|
serialize(_options) {
|
|
406
410
|
return [
|
|
@@ -417,11 +421,13 @@ export class AlterTableDropColumn extends AlterTableChange {
|
|
|
417
421
|
export class AlterTableAlterColumnType extends AlterTableChange {
|
|
418
422
|
table;
|
|
419
423
|
column;
|
|
424
|
+
previousColumn;
|
|
420
425
|
scope = "object";
|
|
421
426
|
constructor(props) {
|
|
422
427
|
super();
|
|
423
428
|
this.table = props.table;
|
|
424
429
|
this.column = props.column;
|
|
430
|
+
this.previousColumn = props.previousColumn;
|
|
425
431
|
}
|
|
426
432
|
get requires() {
|
|
427
433
|
return [
|
|
@@ -429,6 +435,12 @@ export class AlterTableAlterColumnType extends AlterTableChange {
|
|
|
429
435
|
];
|
|
430
436
|
}
|
|
431
437
|
serialize(_options) {
|
|
438
|
+
// previousColumn is optional so direct serializer tests/fixtures can keep
|
|
439
|
+
// emitting canonical ALTER TYPE SQL without forcing a USING expression.
|
|
440
|
+
// When provided, we can detect true type changes and add USING for casts
|
|
441
|
+
// PostgreSQL cannot perform automatically.
|
|
442
|
+
const hasTypeChangedWithPreviousDefinition = this.previousColumn?.data_type_str !== undefined &&
|
|
443
|
+
this.previousColumn.data_type_str !== this.column.data_type_str;
|
|
432
444
|
const parts = [
|
|
433
445
|
"ALTER TABLE",
|
|
434
446
|
`${this.table.schema}.${this.table.name}`,
|
|
@@ -440,6 +452,9 @@ export class AlterTableAlterColumnType extends AlterTableChange {
|
|
|
440
452
|
if (this.column.collation) {
|
|
441
453
|
parts.push("COLLATE", this.column.collation);
|
|
442
454
|
}
|
|
455
|
+
if (hasTypeChangedWithPreviousDefinition) {
|
|
456
|
+
parts.push("USING", `${this.column.name}::${this.column.data_type_str}`);
|
|
457
|
+
}
|
|
443
458
|
return parts.join(" ");
|
|
444
459
|
}
|
|
445
460
|
}
|
|
@@ -451,15 +451,30 @@ export function diffTables(ctx, main, branch) {
|
|
|
451
451
|
const branchCol = branchCols.get(name);
|
|
452
452
|
if (!branchCol)
|
|
453
453
|
continue;
|
|
454
|
+
const columnTypeChanged = mainCol.data_type_str !== branchCol.data_type_str;
|
|
455
|
+
const columnCollationChanged = mainCol.collation !== branchCol.collation;
|
|
456
|
+
const needsDefaultSafeFlow = columnTypeChanged && mainCol.default !== null;
|
|
454
457
|
// TYPE or COLLATION change
|
|
455
|
-
if (
|
|
456
|
-
mainCol.collation !== branchCol.collation) {
|
|
458
|
+
if (columnTypeChanged || columnCollationChanged) {
|
|
457
459
|
// Skip if parent has the same type/collation change
|
|
458
460
|
if (!parentHasSameColumnPropertyChange(name, "type")) {
|
|
461
|
+
if (needsDefaultSafeFlow) {
|
|
462
|
+
changes.push(new AlterTableAlterColumnDropDefault({
|
|
463
|
+
table: branchTable,
|
|
464
|
+
column: branchCol,
|
|
465
|
+
}));
|
|
466
|
+
}
|
|
459
467
|
changes.push(new AlterTableAlterColumnType({
|
|
460
468
|
table: branchTable,
|
|
461
469
|
column: branchCol,
|
|
470
|
+
previousColumn: mainCol,
|
|
462
471
|
}));
|
|
472
|
+
if (needsDefaultSafeFlow && branchCol.default !== null) {
|
|
473
|
+
changes.push(new AlterTableAlterColumnSetDefault({
|
|
474
|
+
table: branchTable,
|
|
475
|
+
column: branchCol,
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
463
478
|
}
|
|
464
479
|
}
|
|
465
480
|
// PostgreSQL rejects SET DEFAULT while the column still has identity metadata,
|
|
@@ -476,6 +491,10 @@ export function diffTables(ctx, main, branch) {
|
|
|
476
491
|
if (mainCol.default !== branchCol.default) {
|
|
477
492
|
// Skip if parent has the same default change
|
|
478
493
|
if (!parentHasSameColumnPropertyChange(name, "default")) {
|
|
494
|
+
if (needsDefaultSafeFlow) {
|
|
495
|
+
// Defaults were already dropped/re-set in the type-change flow above.
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
479
498
|
if (branchCol.default === null) {
|
|
480
499
|
// Drop default value
|
|
481
500
|
changes.push(new AlterTableAlterColumnDropDefault({
|
|
@@ -265,13 +265,16 @@ select
|
|
|
265
265
|
|
|
266
266
|
'key_columns',
|
|
267
267
|
case
|
|
268
|
-
when c.conkey is not null then (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
268
|
+
when c.conkey is not null then coalesce(
|
|
269
|
+
(
|
|
270
|
+
select json_agg(quote_ident(att.attname) order by pk.ordinality)
|
|
271
|
+
from unnest(c.conkey) with ordinality as pk(attnum, ordinality)
|
|
272
|
+
join pg_attribute att
|
|
273
|
+
on att.attrelid = c.conrelid
|
|
274
|
+
and att.attnum = pk.attnum
|
|
275
|
+
and att.attisdropped = false
|
|
276
|
+
),
|
|
277
|
+
'[]'::json
|
|
275
278
|
)
|
|
276
279
|
else '[]'::json
|
|
277
280
|
end,
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import type { Catalog } from "./catalog.model.ts";
|
|
2
1
|
import type { Change } from "./change.types.ts";
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Apply structural rewrites to the change list that are only obvious once
|
|
4
|
+
* every object diff has been collected. This pass does NOT prevent dependency
|
|
5
|
+
* cycles — that responsibility now lives in the sort phase, where
|
|
6
|
+
* `sortPhaseChanges` invokes `tryBreakCycleByChangeInjection` lazily on cycles
|
|
7
|
+
* that edge filtering can't break (FK SCC of dropped tables,
|
|
8
|
+
* AlterPublicationDropTables ↔ AlterTableDropColumn, …).
|
|
6
9
|
*
|
|
7
|
-
*
|
|
8
|
-
* - If replace expansion added `DropTable(T)+CreateTable(T)`, targeted
|
|
9
|
-
* `AlterTableDropColumn(T.*)` / `AlterTableDropConstraint(T.*)` changes are
|
|
10
|
-
* redundant and create an unbreakable drop-phase cycle, so we elide them.
|
|
11
|
-
* - When the same `DropTable+CreateTable` pair is present, the expansion
|
|
12
|
-
* also emits one `AlterTableAddConstraint` / `AlterTableValidateConstraint`
|
|
13
|
-
* / `CreateCommentOnConstraint` per branch constraint, which may collide
|
|
14
|
-
* with the same change already emitted by `diffTables()` (for example on a
|
|
15
|
-
* shape flip or a new constraint). We dedupe these keeping only the last
|
|
16
|
-
* occurrence so the expansion's emission survives and the diffTables
|
|
17
|
-
* duplicate is removed.
|
|
18
|
-
* - If two dropped tables reference each other via FK, we insert dedicated
|
|
19
|
-
* `AlterTableDropConstraint` changes and teach the paired `DropTable`
|
|
20
|
-
* changes not to claim those FK stable IDs.
|
|
10
|
+
* Concretely, this pass:
|
|
21
11
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
12
|
+
* - Prunes `AlterTableDropColumn(T.*)` / `AlterTableDropConstraint(T.*)`
|
|
13
|
+
* changes that are made redundant by an expansion-emitted
|
|
14
|
+
* `DropTable(T) + CreateTable(T)` pair. Without this, the apply phase
|
|
15
|
+
* would try to drop a column that no longer exists in the freshly
|
|
16
|
+
* recreated table.
|
|
17
|
+
* - Dedupes duplicate `AlterTableAddConstraint` /
|
|
18
|
+
* `AlterTableValidateConstraint` / `CreateCommentOnConstraint` changes
|
|
19
|
+
* produced when `diffTables()` and `expandReplaceDependencies()` both
|
|
20
|
+
* emit the same constraint operation for a replaced table. Last write
|
|
21
|
+
* wins so the expansion's emission survives.
|
|
22
|
+
*
|
|
23
|
+
* Object-local PostgreSQL semantics (for example owned-sequence cascades)
|
|
24
|
+
* stay in the corresponding `diff*` function instead of this pass.
|
|
24
25
|
*/
|
|
25
|
-
export declare function normalizePostDiffCycles({ changes,
|
|
26
|
+
export declare function normalizePostDiffCycles({ changes, replacedTableIds, }: {
|
|
26
27
|
changes: Change[];
|
|
27
|
-
mainCatalog: Catalog;
|
|
28
28
|
replacedTableIds?: ReadonlySet<string>;
|
|
29
29
|
}): Change[];
|
|
@@ -1,32 +1,9 @@
|
|
|
1
1
|
import { AlterTableAddConstraint, AlterTableDropColumn, AlterTableDropConstraint, AlterTableValidateConstraint, } from "./objects/table/changes/table.alter.js";
|
|
2
2
|
import { CreateCommentOnConstraint } from "./objects/table/changes/table.comment.js";
|
|
3
|
-
import { DropTable } from "./objects/table/changes/table.drop.js";
|
|
4
3
|
import { stableId } from "./objects/utils.js";
|
|
5
4
|
function constraintStableId(table, constraintName) {
|
|
6
5
|
return stableId.constraint(table.schema, table.name, constraintName);
|
|
7
6
|
}
|
|
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
7
|
function isSupersededByTableReplacement(change, replacedTableIds) {
|
|
31
8
|
if (!(change instanceof AlterTableDropColumn) &&
|
|
32
9
|
!(change instanceof AlterTableDropConstraint)) {
|
|
@@ -91,119 +68,33 @@ function dropReplacedTableDuplicateConstraintChanges(changes, replacedTableIds)
|
|
|
91
68
|
}
|
|
92
69
|
return mutated ? reversedKept.reverse() : changes;
|
|
93
70
|
}
|
|
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
71
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
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, …).
|
|
115
78
|
*
|
|
116
|
-
*
|
|
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.
|
|
79
|
+
* Concretely, this pass:
|
|
130
80
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
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.
|
|
133
94
|
*/
|
|
134
|
-
export function normalizePostDiffCycles({ changes,
|
|
95
|
+
export function normalizePostDiffCycles({ changes, replacedTableIds = new Set(), }) {
|
|
135
96
|
const dedupedChanges = dropReplacedTableDuplicateConstraintChanges(changes, replacedTableIds);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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;
|
|
97
|
+
if (replacedTableIds.size === 0)
|
|
98
|
+
return dedupedChanges;
|
|
99
|
+
return dedupedChanges.filter((change) => !isSupersededByTableReplacement(change, replacedTableIds));
|
|
209
100
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Change } from "../change.types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Try to break an unbreakable cycle by INJECTING NEW CHANGES or REWRITING
|
|
4
|
+
* existing ones (rather than removing graph edges).
|
|
5
|
+
*
|
|
6
|
+
* Called by `sortPhaseChanges` when its edge-removal cycle handler has seen
|
|
7
|
+
* the same cycle twice — i.e. weak-edge filtering exhausted itself but the
|
|
8
|
+
* cycle is still there. At that point we know the cycle is composed of
|
|
9
|
+
* "hard" edges (explicit `requires` or pg_depend rows) that can only be
|
|
10
|
+
* broken by changing the change list itself.
|
|
11
|
+
*
|
|
12
|
+
* Returns a rewritten `phaseChanges` array, or `null` if no breaker matches
|
|
13
|
+
* (in which case the caller throws the existing CycleError).
|
|
14
|
+
*/
|
|
15
|
+
export declare function tryBreakCycleByChangeInjection(cycleNodeIndexes: readonly number[], phaseChanges: readonly Change[]): Change[] | null;
|