@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
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
type PrivilegeProps,
|
|
12
12
|
privilegePropsSchema,
|
|
13
13
|
} from "../base.privilege-diff.ts";
|
|
14
|
+
import {
|
|
15
|
+
type ExtractRetryOptions,
|
|
16
|
+
extractWithDefinitionRetry,
|
|
17
|
+
} from "../extract-with-retry.ts";
|
|
14
18
|
import { ReplicaIdentitySchema } from "../table/table.model.ts";
|
|
15
19
|
|
|
16
20
|
const viewPropsSchema = z.object({
|
|
@@ -34,6 +38,15 @@ const viewPropsSchema = z.object({
|
|
|
34
38
|
privileges: z.array(privilegePropsSchema),
|
|
35
39
|
});
|
|
36
40
|
|
|
41
|
+
// pg_get_viewdef(oid) can return NULL when the underlying view (or its
|
|
42
|
+
// pg_rewrite row) is dropped between catalog scan and resolution, or under
|
|
43
|
+
// transient catalog state during recovery. An unreadable view cannot be
|
|
44
|
+
// diffed, so we accept NULL here and filter the row out at extraction time
|
|
45
|
+
// rather than crashing the whole catalog parse with a ZodError.
|
|
46
|
+
const viewRowSchema = viewPropsSchema.extend({
|
|
47
|
+
definition: z.string().nullable(),
|
|
48
|
+
});
|
|
49
|
+
|
|
37
50
|
type ViewPrivilegeProps = PrivilegeProps;
|
|
38
51
|
export type ViewProps = z.infer<typeof viewPropsSchema>;
|
|
39
52
|
|
|
@@ -126,8 +139,16 @@ export class View extends BasePgModel implements TableLikeObject {
|
|
|
126
139
|
}
|
|
127
140
|
}
|
|
128
141
|
|
|
129
|
-
export async function extractViews(
|
|
130
|
-
|
|
142
|
+
export async function extractViews(
|
|
143
|
+
pool: Pool,
|
|
144
|
+
options?: ExtractRetryOptions,
|
|
145
|
+
): Promise<View[]> {
|
|
146
|
+
const viewRows = await extractWithDefinitionRetry({
|
|
147
|
+
label: "views",
|
|
148
|
+
options,
|
|
149
|
+
hasNullDefinition: (row) => row.definition === null,
|
|
150
|
+
query: async () => {
|
|
151
|
+
const result = await pool.query<ViewProps>(sql`
|
|
131
152
|
with extension_oids as (
|
|
132
153
|
select
|
|
133
154
|
objid
|
|
@@ -254,9 +275,11 @@ group by
|
|
|
254
275
|
order by
|
|
255
276
|
v.schema, v.name
|
|
256
277
|
`);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
278
|
+
return result.rows.map((row: unknown) => viewRowSchema.parse(row));
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
const validatedRows = viewRows.filter(
|
|
282
|
+
(row): row is ViewProps => row.definition !== null,
|
|
260
283
|
);
|
|
261
284
|
return validatedRows.map((row: ViewProps) => new View(row));
|
|
262
285
|
}
|
package/src/core/plan/create.ts
CHANGED
|
@@ -100,7 +100,9 @@ export async function createPlan(
|
|
|
100
100
|
}
|
|
101
101
|
const resolved = await resolvePool(input, label);
|
|
102
102
|
pools.push(resolved);
|
|
103
|
-
return extractCatalog(resolved.pool
|
|
103
|
+
return extractCatalog(resolved.pool, {
|
|
104
|
+
extractRetries: options.extractRetries,
|
|
105
|
+
});
|
|
104
106
|
};
|
|
105
107
|
|
|
106
108
|
const pools: Array<{ pool: Pool; shouldClose: boolean }> = [];
|
package/src/core/plan/types.ts
CHANGED
|
@@ -165,4 +165,12 @@ export interface CreatePlanOptions {
|
|
|
165
165
|
* the output must be self-contained and not rely on statement execution order.
|
|
166
166
|
*/
|
|
167
167
|
skipDefaultPrivilegeSubtraction?: boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Number of retry attempts for catalog extractors when `pg_get_*def()`
|
|
170
|
+
* returns NULL for at least one row (a transient race with concurrent DDL).
|
|
171
|
+
* Total attempts is `extractRetries + 1`. When undefined, the value is read
|
|
172
|
+
* from the `PGDELTA_EXTRACT_RETRIES` environment variable, falling back to
|
|
173
|
+
* a default of 1 (i.e. the first attempt plus one retry, 2 attempts total).
|
|
174
|
+
*/
|
|
175
|
+
extractRetries?: number;
|
|
168
176
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { Catalog, createEmptyCatalog } from "./catalog.model.ts";
|
|
3
2
|
import type { Change } from "./change.types.ts";
|
|
3
|
+
import { CreateIndex } from "./objects/index/changes/index.create.ts";
|
|
4
|
+
import { DropIndex } from "./objects/index/changes/index.drop.ts";
|
|
5
|
+
import { Index, type IndexProps } from "./objects/index/index.model.ts";
|
|
4
6
|
import {
|
|
5
7
|
AlterTableAddConstraint,
|
|
6
8
|
AlterTableChangeOwner,
|
|
@@ -15,8 +17,7 @@ import { CreateTable } from "./objects/table/changes/table.create.ts";
|
|
|
15
17
|
import { DropTable } from "./objects/table/changes/table.drop.ts";
|
|
16
18
|
import { GrantTablePrivileges } from "./objects/table/changes/table.privilege.ts";
|
|
17
19
|
import { Table } from "./objects/table/table.model.ts";
|
|
18
|
-
import {
|
|
19
|
-
import { normalizePostDiffCycles } from "./post-diff-cycle-breaking.ts";
|
|
20
|
+
import { normalizePostDiffChanges } from "./post-diff-normalization.ts";
|
|
20
21
|
|
|
21
22
|
const baseTableProps = {
|
|
22
23
|
schema: "public",
|
|
@@ -61,150 +62,8 @@ function integerColumn(name: string, position: number) {
|
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
describe("
|
|
65
|
-
test("injects explicit FK drops for mutually dependent dropped tables", async () => {
|
|
66
|
-
const baseline = await createEmptyCatalog(170000, "postgres");
|
|
67
|
-
const tableA = new Table({
|
|
68
|
-
...baseTableProps,
|
|
69
|
-
name: "a",
|
|
70
|
-
columns: [
|
|
71
|
-
{ ...integerColumn("id", 1), not_null: true },
|
|
72
|
-
integerColumn("b_id", 2),
|
|
73
|
-
],
|
|
74
|
-
constraints: [
|
|
75
|
-
{
|
|
76
|
-
name: "a_b_fkey",
|
|
77
|
-
constraint_type: "f",
|
|
78
|
-
deferrable: false,
|
|
79
|
-
initially_deferred: false,
|
|
80
|
-
validated: true,
|
|
81
|
-
is_local: true,
|
|
82
|
-
no_inherit: false,
|
|
83
|
-
is_temporal: false,
|
|
84
|
-
is_partition_clone: false,
|
|
85
|
-
parent_constraint_schema: null,
|
|
86
|
-
parent_constraint_name: null,
|
|
87
|
-
parent_table_schema: null,
|
|
88
|
-
parent_table_name: null,
|
|
89
|
-
key_columns: ["b_id"],
|
|
90
|
-
foreign_key_columns: ["id"],
|
|
91
|
-
foreign_key_table: "b",
|
|
92
|
-
foreign_key_schema: "public",
|
|
93
|
-
foreign_key_table_is_partition: false,
|
|
94
|
-
foreign_key_parent_schema: null,
|
|
95
|
-
foreign_key_parent_table: null,
|
|
96
|
-
foreign_key_effective_schema: "public",
|
|
97
|
-
foreign_key_effective_table: "b",
|
|
98
|
-
on_update: "a",
|
|
99
|
-
on_delete: "a",
|
|
100
|
-
match_type: "s",
|
|
101
|
-
check_expression: null,
|
|
102
|
-
owner: "postgres",
|
|
103
|
-
definition: "FOREIGN KEY (b_id) REFERENCES public.b(id)",
|
|
104
|
-
comment: null,
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
});
|
|
108
|
-
const tableB = new Table({
|
|
109
|
-
...baseTableProps,
|
|
110
|
-
name: "b",
|
|
111
|
-
columns: [
|
|
112
|
-
{ ...integerColumn("id", 1), not_null: true },
|
|
113
|
-
integerColumn("a_id", 2),
|
|
114
|
-
],
|
|
115
|
-
constraints: [
|
|
116
|
-
{
|
|
117
|
-
name: "b_a_fkey",
|
|
118
|
-
constraint_type: "f",
|
|
119
|
-
deferrable: false,
|
|
120
|
-
initially_deferred: false,
|
|
121
|
-
validated: true,
|
|
122
|
-
is_local: true,
|
|
123
|
-
no_inherit: false,
|
|
124
|
-
is_temporal: false,
|
|
125
|
-
is_partition_clone: false,
|
|
126
|
-
parent_constraint_schema: null,
|
|
127
|
-
parent_constraint_name: null,
|
|
128
|
-
parent_table_schema: null,
|
|
129
|
-
parent_table_name: null,
|
|
130
|
-
key_columns: ["a_id"],
|
|
131
|
-
foreign_key_columns: ["id"],
|
|
132
|
-
foreign_key_table: "a",
|
|
133
|
-
foreign_key_schema: "public",
|
|
134
|
-
foreign_key_table_is_partition: false,
|
|
135
|
-
foreign_key_parent_schema: null,
|
|
136
|
-
foreign_key_parent_table: null,
|
|
137
|
-
foreign_key_effective_schema: "public",
|
|
138
|
-
foreign_key_effective_table: "a",
|
|
139
|
-
on_update: "a",
|
|
140
|
-
on_delete: "a",
|
|
141
|
-
match_type: "s",
|
|
142
|
-
check_expression: null,
|
|
143
|
-
owner: "postgres",
|
|
144
|
-
definition: "FOREIGN KEY (a_id) REFERENCES public.a(id)",
|
|
145
|
-
comment: null,
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
});
|
|
149
|
-
const mainCatalog = new Catalog({
|
|
150
|
-
...baseline,
|
|
151
|
-
tables: {
|
|
152
|
-
[tableA.stableId]: tableA,
|
|
153
|
-
[tableB.stableId]: tableB,
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
const changes: Change[] = [
|
|
157
|
-
new DropTable({ table: tableA }),
|
|
158
|
-
new DropTable({ table: tableB }),
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
const normalized = normalizePostDiffCycles({
|
|
162
|
-
changes,
|
|
163
|
-
mainCatalog,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const explicitConstraintDrops = normalized.filter(
|
|
167
|
-
(change) => change instanceof AlterTableDropConstraint,
|
|
168
|
-
);
|
|
169
|
-
expect(explicitConstraintDrops).toHaveLength(2);
|
|
170
|
-
|
|
171
|
-
const normalizedDropTableA = normalized.find(
|
|
172
|
-
(change) =>
|
|
173
|
-
change instanceof DropTable &&
|
|
174
|
-
change.table.stableId === tableA.stableId,
|
|
175
|
-
);
|
|
176
|
-
const normalizedDropTableB = normalized.find(
|
|
177
|
-
(change) =>
|
|
178
|
-
change instanceof DropTable &&
|
|
179
|
-
change.table.stableId === tableB.stableId,
|
|
180
|
-
);
|
|
181
|
-
if (!(normalizedDropTableA instanceof DropTable)) {
|
|
182
|
-
throw new Error("expected normalized DropTable(public.a)");
|
|
183
|
-
}
|
|
184
|
-
if (!(normalizedDropTableB instanceof DropTable)) {
|
|
185
|
-
throw new Error("expected normalized DropTable(public.b)");
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
expect(
|
|
189
|
-
normalizedDropTableA.externallyDroppedConstraints.has("a_b_fkey"),
|
|
190
|
-
).toBe(true);
|
|
191
|
-
expect(
|
|
192
|
-
normalizedDropTableB.externallyDroppedConstraints.has("b_a_fkey"),
|
|
193
|
-
).toBe(true);
|
|
194
|
-
expect(
|
|
195
|
-
normalizedDropTableA.requires.includes(
|
|
196
|
-
stableId.constraint("public", "a", "a_b_fkey"),
|
|
197
|
-
),
|
|
198
|
-
).toBe(false);
|
|
199
|
-
expect(
|
|
200
|
-
normalizedDropTableB.requires.includes(
|
|
201
|
-
stableId.constraint("public", "b", "b_a_fkey"),
|
|
202
|
-
),
|
|
203
|
-
).toBe(false);
|
|
204
|
-
});
|
|
205
|
-
|
|
65
|
+
describe("normalizePostDiffChanges", () => {
|
|
206
66
|
test("prunes same-table drop-column and drop-constraint ALTERs for replaced tables only", async () => {
|
|
207
|
-
const baseline = await createEmptyCatalog(170000, "postgres");
|
|
208
67
|
const mainChildren = new Table({
|
|
209
68
|
...baseTableProps,
|
|
210
69
|
name: "children",
|
|
@@ -292,14 +151,9 @@ describe("normalizePostDiffCycles", () => {
|
|
|
292
151
|
preExistingReplicaIdentity,
|
|
293
152
|
preExistingGrant,
|
|
294
153
|
];
|
|
295
|
-
const mainCatalog = new Catalog({
|
|
296
|
-
...baseline,
|
|
297
|
-
tables: { [mainChildren.stableId]: mainChildren },
|
|
298
|
-
});
|
|
299
154
|
|
|
300
|
-
const normalized =
|
|
155
|
+
const normalized = normalizePostDiffChanges({
|
|
301
156
|
changes,
|
|
302
|
-
mainCatalog,
|
|
303
157
|
replacedTableIds: new Set([mainChildren.stableId]),
|
|
304
158
|
});
|
|
305
159
|
|
|
@@ -322,7 +176,6 @@ describe("normalizePostDiffCycles", () => {
|
|
|
322
176
|
});
|
|
323
177
|
|
|
324
178
|
test("dedupes duplicate constraint Add/Validate/Comment on replaced tables keeping last occurrence", async () => {
|
|
325
|
-
const baseline = await createEmptyCatalog(170000, "postgres");
|
|
326
179
|
const branchChildren = new Table({
|
|
327
180
|
...baseTableProps,
|
|
328
181
|
name: "children",
|
|
@@ -423,14 +276,8 @@ describe("normalizePostDiffCycles", () => {
|
|
|
423
276
|
expansionComment,
|
|
424
277
|
];
|
|
425
278
|
|
|
426
|
-
const
|
|
427
|
-
...baseline,
|
|
428
|
-
tables: { [branchChildren.stableId]: branchChildren },
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
const normalized = normalizePostDiffCycles({
|
|
279
|
+
const normalized = normalizePostDiffChanges({
|
|
432
280
|
changes,
|
|
433
|
-
mainCatalog,
|
|
434
281
|
replacedTableIds: new Set([branchChildren.stableId]),
|
|
435
282
|
});
|
|
436
283
|
|
|
@@ -456,4 +303,165 @@ describe("normalizePostDiffCycles", () => {
|
|
|
456
303
|
),
|
|
457
304
|
).toHaveLength(1);
|
|
458
305
|
});
|
|
306
|
+
|
|
307
|
+
describe("restoreReplicaIdentityAfterIndexReplace", () => {
|
|
308
|
+
const baseIndexProps: IndexProps = {
|
|
309
|
+
schema: "public",
|
|
310
|
+
table_name: "replicated",
|
|
311
|
+
name: "tenant_idx",
|
|
312
|
+
storage_params: [],
|
|
313
|
+
statistics_target: [],
|
|
314
|
+
index_type: "btree",
|
|
315
|
+
tablespace: null,
|
|
316
|
+
is_unique: true,
|
|
317
|
+
is_primary: false,
|
|
318
|
+
is_exclusion: false,
|
|
319
|
+
nulls_not_distinct: false,
|
|
320
|
+
immediate: true,
|
|
321
|
+
is_clustered: false,
|
|
322
|
+
is_replica_identity: true,
|
|
323
|
+
key_columns: [],
|
|
324
|
+
column_collations: [],
|
|
325
|
+
operator_classes: [],
|
|
326
|
+
column_options: [],
|
|
327
|
+
index_expressions: null,
|
|
328
|
+
partial_predicate: null,
|
|
329
|
+
table_relkind: "r",
|
|
330
|
+
is_owned_by_constraint: false,
|
|
331
|
+
is_partitioned_index: false,
|
|
332
|
+
is_index_partition: false,
|
|
333
|
+
parent_index_name: null,
|
|
334
|
+
definition: "CREATE UNIQUE INDEX tenant_idx ON public.replicated (a)",
|
|
335
|
+
comment: null,
|
|
336
|
+
owner: "postgres",
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
function makeBranchTable(replicaIdentityIndex: string | null) {
|
|
340
|
+
return new Table({
|
|
341
|
+
...baseTableProps,
|
|
342
|
+
name: "replicated",
|
|
343
|
+
replica_identity: replicaIdentityIndex ? "i" : "d",
|
|
344
|
+
replica_identity_index: replicaIdentityIndex,
|
|
345
|
+
columns: [
|
|
346
|
+
{ ...integerColumn("id", 1), not_null: true },
|
|
347
|
+
integerColumn("a", 2),
|
|
348
|
+
],
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
test("re-emits ALTER TABLE … REPLICA IDENTITY USING INDEX after a DropIndex+CreateIndex pair", () => {
|
|
353
|
+
const branchTable = makeBranchTable("tenant_idx");
|
|
354
|
+
const oldIndex = new Index(baseIndexProps);
|
|
355
|
+
const newIndex = new Index({
|
|
356
|
+
...baseIndexProps,
|
|
357
|
+
definition:
|
|
358
|
+
"CREATE UNIQUE INDEX tenant_idx ON public.replicated (a, id)",
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const changes: Change[] = [
|
|
362
|
+
new DropIndex({ index: oldIndex }),
|
|
363
|
+
new CreateIndex({ index: newIndex, indexableObject: branchTable }),
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
const normalized = normalizePostDiffChanges({
|
|
367
|
+
changes,
|
|
368
|
+
branchTables: { [branchTable.stableId]: branchTable },
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
expect(normalized.map((c) => c.constructor.name)).toEqual([
|
|
372
|
+
"DropIndex",
|
|
373
|
+
"CreateIndex",
|
|
374
|
+
"AlterTableSetReplicaIdentity",
|
|
375
|
+
]);
|
|
376
|
+
|
|
377
|
+
const inserted = normalized[2] as AlterTableSetReplicaIdentity;
|
|
378
|
+
expect(inserted.mode).toBe("i");
|
|
379
|
+
expect(inserted.indexName).toBe("tenant_idx");
|
|
380
|
+
expect(inserted.requires).toEqual([
|
|
381
|
+
"table:public.replicated",
|
|
382
|
+
"index:public.replicated.tenant_idx",
|
|
383
|
+
]);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("does not double-emit when diffTables already produced an AlterTableSetReplicaIdentity for the same table", () => {
|
|
387
|
+
const branchTable = makeBranchTable("tenant_idx");
|
|
388
|
+
const oldIndex = new Index(baseIndexProps);
|
|
389
|
+
const newIndex = new Index({
|
|
390
|
+
...baseIndexProps,
|
|
391
|
+
definition:
|
|
392
|
+
"CREATE UNIQUE INDEX tenant_idx ON public.replicated (a, id)",
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const changes: Change[] = [
|
|
396
|
+
new DropIndex({ index: oldIndex }),
|
|
397
|
+
new CreateIndex({ index: newIndex, indexableObject: branchTable }),
|
|
398
|
+
new AlterTableSetReplicaIdentity({
|
|
399
|
+
table: branchTable,
|
|
400
|
+
mode: "i",
|
|
401
|
+
indexName: "tenant_idx",
|
|
402
|
+
}),
|
|
403
|
+
];
|
|
404
|
+
|
|
405
|
+
const normalized = normalizePostDiffChanges({
|
|
406
|
+
changes,
|
|
407
|
+
branchTables: { [branchTable.stableId]: branchTable },
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
expect(
|
|
411
|
+
normalized.filter((c) => c instanceof AlterTableSetReplicaIdentity),
|
|
412
|
+
).toHaveLength(1);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test("ignores DropIndex without a matching CreateIndex (pure drop)", () => {
|
|
416
|
+
// Pure drop: the user removed the index entirely. The table.diff path is
|
|
417
|
+
// responsible for emitting the corresponding REPLICA IDENTITY DEFAULT.
|
|
418
|
+
// The post-diff pass must not synthesize a USING INDEX setter for an
|
|
419
|
+
// index that no longer exists.
|
|
420
|
+
const branchTable = makeBranchTable(null);
|
|
421
|
+
const oldIndex = new Index(baseIndexProps);
|
|
422
|
+
|
|
423
|
+
const changes: Change[] = [new DropIndex({ index: oldIndex })];
|
|
424
|
+
|
|
425
|
+
const normalized = normalizePostDiffChanges({
|
|
426
|
+
changes,
|
|
427
|
+
branchTables: { [branchTable.stableId]: branchTable },
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
expect(
|
|
431
|
+
normalized.filter((c) => c instanceof AlterTableSetReplicaIdentity),
|
|
432
|
+
).toHaveLength(0);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test("ignores indexes that are not the table's replica identity", () => {
|
|
436
|
+
// The table has replica_identity = 'd', so even if some other index is
|
|
437
|
+
// being replaced, no setter should be injected.
|
|
438
|
+
const branchTable = makeBranchTable(null);
|
|
439
|
+
const otherIndex = new Index({
|
|
440
|
+
...baseIndexProps,
|
|
441
|
+
name: "some_other_idx",
|
|
442
|
+
is_replica_identity: false,
|
|
443
|
+
definition: "CREATE INDEX some_other_idx ON public.replicated (a)",
|
|
444
|
+
});
|
|
445
|
+
const newOtherIndex = new Index({
|
|
446
|
+
...baseIndexProps,
|
|
447
|
+
name: "some_other_idx",
|
|
448
|
+
is_replica_identity: false,
|
|
449
|
+
definition: "CREATE INDEX some_other_idx ON public.replicated (a, id)",
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const changes: Change[] = [
|
|
453
|
+
new DropIndex({ index: otherIndex }),
|
|
454
|
+
new CreateIndex({ index: newOtherIndex, indexableObject: branchTable }),
|
|
455
|
+
];
|
|
456
|
+
|
|
457
|
+
const normalized = normalizePostDiffChanges({
|
|
458
|
+
changes,
|
|
459
|
+
branchTables: { [branchTable.stableId]: branchTable },
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
expect(
|
|
463
|
+
normalized.filter((c) => c instanceof AlterTableSetReplicaIdentity),
|
|
464
|
+
).toHaveLength(0);
|
|
465
|
+
});
|
|
466
|
+
});
|
|
459
467
|
});
|