okmodel 0.3.0 → 0.4.0
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/README.md +182 -22
- package/dist/contracts/catalog/document.d.ts +6 -2
- package/dist/dialects/pg/ext/citext.js +1 -1
- package/dist/dialects/pg/ext/index.js +1 -1
- package/dist/dialects/pg/ext/pg-trgm.js +1 -1
- package/dist/dialects/pg/index.js +3 -3
- package/dist/dialects/pg/role/sql.d.ts +11 -4
- package/dist/dialects/pg/table.d.ts +17 -1
- package/dist/dialects/pg/view/index.js +1 -1
- package/dist/dialects/pg/view/scratch.d.ts +3 -2
- package/dist/runtime/catalog-cache.d.ts +17 -0
- package/dist/runtime/compat-words.d.ts +23 -0
- package/dist/runtime/drift-detail.d.ts +20 -4
- package/dist/runtime/drift.d.ts +4 -2
- package/dist/runtime/pg/bun.js +2 -2
- package/dist/runtime/pg/pg.js +2 -2
- package/dist/runtime/pg/pglite.js +2 -2
- package/dist/runtime/pg/postgresjs.js +2 -2
- package/dist/runtime/traits/index.js +5 -4
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/validate/index.js +2 -2
- package/dist/shared/2321avmn.js +418 -0
- package/dist/shared/{x6wysh83.js → 3p4b1fg0.js} +3 -5
- package/dist/shared/{wekze34d.js → 41kv2xdm.js} +74 -2
- package/dist/shared/{fs0ex00p.js → 4ef7mmqf.js} +2 -2
- package/dist/shared/5mtf6cj3.js +94 -0
- package/dist/shared/{999ade2q.js → 5vdm4sgp.js} +1 -1
- package/dist/shared/{qybh48nd.js → 6693j9rs.js} +1 -1
- package/dist/shared/{bqha5cg6.js → 6p5k20sm.js} +2 -2
- package/dist/shared/7nk68fj8.js +751 -0
- package/dist/shared/{ne02ng5c.js → 84ptn35q.js} +7 -4
- package/dist/shared/{t616ynfn.js → 8jb4ymh2.js} +59 -12
- package/dist/shared/{th14ygv2.js → 8ktwd3rh.js} +29 -1
- package/dist/shared/9yn6a2vd.js +11 -0
- package/dist/shared/ady1ms74.js +72 -0
- package/dist/shared/{scwxrpx2.js → c5g81ngb.js} +82 -15
- package/dist/shared/{6bazvqwm.js → fb00y8fp.js} +2 -2
- package/dist/shared/{jkc6ws1p.js → g7ez901n.js} +83 -2
- package/dist/shared/{kgnc8grt.js → gdh9mrtp.js} +1 -1
- package/dist/shared/hqjsxnz7.js +13 -0
- package/dist/shared/htnaj77v.js +17 -0
- package/dist/shared/{mt5wswjz.js → jgxzjzcz.js} +1 -1
- package/dist/shared/{6w5j844a.js → jsd07prz.js} +2 -2
- package/dist/shared/{m78s7898.js → kafaa5zv.js} +1 -1
- package/dist/shared/kj680nxv.js +19 -0
- package/dist/shared/{k34e0xf2.js → kkzwp3d2.js} +8 -8
- package/dist/shared/{2ssqrv66.js → n5y9zyj1.js} +1029 -174
- package/dist/shared/{ty3v3hfk.js → nxb42q7h.js} +2 -2
- package/dist/shared/{5nmdenb6.js → q6zsyh31.js} +1003 -40
- package/dist/shared/{fptpmvxp.js → q795afwa.js} +26 -21
- package/dist/shared/{3ekj9x92.js → qn0srwwq.js} +1 -1
- package/dist/shared/s4mb6a4b.js +545 -0
- package/dist/shared/{01yr20s5.js → sf8m6tfy.js} +2 -2
- package/dist/shared/{bryqk9k9.js → sr5ms7b3.js} +1 -1
- package/dist/shared/{meck53jv.js → tny2t3sm.js} +6 -5
- package/dist/shared/{p24x2334.js → v7tpnhnz.js} +1 -1
- package/dist/shared/{xw1d69vd.js → wqem4jed.js} +6 -6
- package/dist/shared/{49ypnfwt.js → xghkt7a9.js} +50 -28
- package/dist/shared/{7fvtrkwr.js → ycatkxmq.js} +1 -1
- package/dist/shared/ygem90rn.js +900 -0
- package/dist/shared/{1tfdfvhh.js → yjw02enp.js} +6 -4
- package/dist/shared/{ydaz1sv3.js → zg24x0hm.js} +1 -1
- package/dist/tooling/cli.js +46 -17
- package/dist/tooling/migrate/apply.d.ts +59 -5
- package/dist/tooling/migrate/backfill.d.ts +136 -0
- package/dist/tooling/migrate/check.d.ts +65 -0
- package/dist/tooling/migrate/classify.d.ts +108 -0
- package/dist/tooling/migrate/commands.d.ts +2 -2
- package/dist/tooling/migrate/config.d.ts +22 -0
- package/dist/tooling/migrate/doctor.d.ts +3 -0
- package/dist/tooling/migrate/drift.d.ts +18 -2
- package/dist/tooling/migrate/estimate.d.ts +50 -0
- package/dist/tooling/migrate/extensions.d.ts +2 -0
- package/dist/tooling/migrate/files.d.ts +12 -0
- package/dist/tooling/migrate/index.d.ts +3 -2
- package/dist/tooling/migrate/index.js +67 -1
- package/dist/tooling/migrate/lint-rules.d.ts +51 -0
- package/dist/tooling/migrate/lint.d.ts +71 -0
- package/dist/tooling/migrate/managed.d.ts +1 -1
- package/dist/tooling/migrate/plan.d.ts +64 -9
- package/dist/tooling/migrate/project.d.ts +42 -7
- package/dist/tooling/migrate/provision.d.ts +18 -0
- package/dist/tooling/migrate/reference.d.ts +40 -0
- package/dist/tooling/migrate/runner.d.ts +108 -0
- package/dist/tooling/migrate/safe.d.ts +77 -0
- package/dist/tooling/migrate/seed.d.ts +20 -0
- package/dist/tooling/migrate/snapshot.d.ts +78 -0
- package/dist/tooling/migrate/status.d.ts +25 -6
- package/dist/tooling/testing/factories.d.ts +117 -0
- package/dist/tooling/testing/facts.d.ts +72 -0
- package/dist/tooling/testing/index.d.ts +89 -0
- package/dist/tooling/testing/index.js +8 -0
- package/dist/tooling/testing/isolation.d.ts +30 -0
- package/dist/tooling/testing/random.d.ts +40 -0
- package/dist/tooling/testing/record.d.ts +38 -0
- package/package.json +6 -2
- package/dist/shared/dbjej1tw.js +0 -456
- package/dist/shared/g4f5kcx9.js +0 -37
- package/dist/shared/qbxzvwwc.js +0 -88
- package/dist/shared/zwmqxb0g.js +0 -79
- package/dist/shared/{xva8v7qk.js → awrsqrn7.js} +3 -3
- package/dist/shared/{75xkjv0t.js → fa9vt245.js} +0 -0
- package/dist/shared/{m9tv7xn7.js → radd3z4g.js} +3 -3
- /package/dist/shared/{0jc3ffzf.js → zvbjc949.js} +0 -0
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
creationOrder2,
|
|
3
3
|
renameColumn2,
|
|
4
4
|
renameTable2
|
|
5
|
-
} from "./
|
|
5
|
+
} from "./8jb4ymh2.js";
|
|
6
6
|
import {
|
|
7
7
|
quoteIdent,
|
|
8
8
|
ownedByView,
|
|
@@ -15,16 +15,16 @@ import {
|
|
|
15
15
|
identitySequence,
|
|
16
16
|
createTableSql,
|
|
17
17
|
qualify
|
|
18
|
-
} from "./
|
|
18
|
+
} from "./41kv2xdm.js";
|
|
19
19
|
import {
|
|
20
20
|
isExactVersion,
|
|
21
21
|
compareExtensionVersions,
|
|
22
22
|
sameExtensionDefinition
|
|
23
|
-
} from "./
|
|
23
|
+
} from "./8ktwd3rh.js";
|
|
24
24
|
import {
|
|
25
25
|
isDomain,
|
|
26
26
|
sameEnumLabels
|
|
27
|
-
} from "./
|
|
27
|
+
} from "./g7ez901n.js";
|
|
28
28
|
import {
|
|
29
29
|
quoteLiteral
|
|
30
30
|
} from "./94ez8kzh.js";
|
|
@@ -41,9 +41,74 @@ import {
|
|
|
41
41
|
OkmError2
|
|
42
42
|
} from "./52szzakm.js";
|
|
43
43
|
|
|
44
|
+
// src/tooling/migrate/type-name.ts
|
|
45
|
+
function canonicalTypeName(typeName) {
|
|
46
|
+
const suffix = arraySuffix(typeName);
|
|
47
|
+
const scalar = suffix.length === 0 ? typeName : typeName.slice(0, -suffix.length);
|
|
48
|
+
return canonicalScalar(scalar) + suffix;
|
|
49
|
+
}
|
|
50
|
+
function arraySuffix(typeName) {
|
|
51
|
+
let end = typeName.length;
|
|
52
|
+
let suffix = "";
|
|
53
|
+
while (end >= 2 && typeName.endsWith("[]", end)) {
|
|
54
|
+
suffix = `[]${suffix}`;
|
|
55
|
+
end -= 2;
|
|
56
|
+
}
|
|
57
|
+
return suffix;
|
|
58
|
+
}
|
|
59
|
+
function canonicalScalar(typeName) {
|
|
60
|
+
const varying = /^(?:varchar|character varying)(\(\d+\))$/.exec(typeName);
|
|
61
|
+
if (varying !== null)
|
|
62
|
+
return `character varying${varying[1] ?? ""}`;
|
|
63
|
+
const fixed = /^(?:char|character)(\(\d+\))$/.exec(typeName);
|
|
64
|
+
if (fixed !== null)
|
|
65
|
+
return `character${fixed[1] ?? ""}`;
|
|
66
|
+
const timed = /^(timestamp with time zone|timestamp without time zone|time with time zone|time without time zone|timestamptz|timestamp|timetz|time)(\(\d+\))?$/.exec(typeName);
|
|
67
|
+
if (timed !== null)
|
|
68
|
+
return canonicalTime(timed[1] ?? "", timed[2] ?? "");
|
|
69
|
+
const numeric = /^(?:decimal|numeric)(\(\d+(?:,\d+)?\))?$/.exec(typeName);
|
|
70
|
+
if (numeric !== null)
|
|
71
|
+
return `numeric${numeric[1] ?? ""}`;
|
|
72
|
+
switch (typeName) {
|
|
73
|
+
case "int":
|
|
74
|
+
case "int4":
|
|
75
|
+
case "integer":
|
|
76
|
+
return "integer";
|
|
77
|
+
case "int8":
|
|
78
|
+
case "bigint":
|
|
79
|
+
return "bigint";
|
|
80
|
+
case "int2":
|
|
81
|
+
case "smallint":
|
|
82
|
+
return "smallint";
|
|
83
|
+
case "bool":
|
|
84
|
+
case "boolean":
|
|
85
|
+
return "boolean";
|
|
86
|
+
case "float8":
|
|
87
|
+
case "double precision":
|
|
88
|
+
return "double precision";
|
|
89
|
+
case "float4":
|
|
90
|
+
case "real":
|
|
91
|
+
return "real";
|
|
92
|
+
default:
|
|
93
|
+
return typeName;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function canonicalTime(name, precision) {
|
|
97
|
+
if (name === "timestamptz" || name === "timestamp with time zone") {
|
|
98
|
+
return `timestamp${precision} with time zone`;
|
|
99
|
+
}
|
|
100
|
+
if (name === "timestamp" || name === "timestamp without time zone") {
|
|
101
|
+
return `timestamp${precision} without time zone`;
|
|
102
|
+
}
|
|
103
|
+
if (name === "timetz" || name === "time with time zone") {
|
|
104
|
+
return `time${precision} with time zone`;
|
|
105
|
+
}
|
|
106
|
+
return `time${precision} without time zone`;
|
|
107
|
+
}
|
|
108
|
+
|
|
44
109
|
// src/dialects/pg/role/sql.ts
|
|
45
110
|
var PRIVILEGE = /^(select|insert|update|delete|execute|usage)$/;
|
|
46
|
-
function privilegeSql(before, after, schema) {
|
|
111
|
+
function privilegeSql(before, after, schema, recreated = new Set) {
|
|
47
112
|
const beforeBy = index(before);
|
|
48
113
|
const afterBy = index(after);
|
|
49
114
|
const revoke = [];
|
|
@@ -53,10 +118,12 @@ function privilegeSql(before, after, schema) {
|
|
|
53
118
|
for (const [key, object] of beforeBy) {
|
|
54
119
|
if (afterBy.has(key))
|
|
55
120
|
continue;
|
|
56
|
-
if (object.kind === "grant")
|
|
57
|
-
revoke.push(grantStatement(object, schema, true));
|
|
58
|
-
|
|
59
|
-
|
|
121
|
+
if (object.kind === "grant") {
|
|
122
|
+
revoke.push({ sql: grantStatement(object, schema, true), kind: "revoke" });
|
|
123
|
+
}
|
|
124
|
+
if (object.kind === "defaultPrivilege") {
|
|
125
|
+
revoke.push({ sql: defaultStatement(object, true), kind: "revoke-default" });
|
|
126
|
+
}
|
|
60
127
|
}
|
|
61
128
|
for (const [key, object] of afterBy) {
|
|
62
129
|
const previous = beforeBy.get(key);
|
|
@@ -64,22 +131,28 @@ function privilegeSql(before, after, schema) {
|
|
|
64
131
|
if (object.owner !== "managed")
|
|
65
132
|
continue;
|
|
66
133
|
if (previous === undefined) {
|
|
67
|
-
roles.push(createRoleSql(object));
|
|
134
|
+
roles.push({ sql: createRoleSql(object), kind: "create-role" });
|
|
68
135
|
continue;
|
|
69
136
|
}
|
|
70
137
|
if (previous.kind !== "role" || previous.owner !== "managed")
|
|
71
138
|
continue;
|
|
72
139
|
if (previous.definition.login !== object.definition.login || previous.definition.inherit !== object.definition.inherit) {
|
|
73
|
-
roles.push(alterRoleSql(object));
|
|
140
|
+
roles.push({ sql: alterRoleSql(object), kind: "alter-role" });
|
|
74
141
|
}
|
|
75
142
|
continue;
|
|
76
143
|
}
|
|
144
|
+
if (object.kind === "grant") {
|
|
145
|
+
const target = grantTargetKey(object.identity.object);
|
|
146
|
+
if (previous !== undefined && (target === undefined || !recreated.has(target)))
|
|
147
|
+
continue;
|
|
148
|
+
grant.push({ sql: grantStatement(object, schema, false), kind: "grant" });
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
77
151
|
if (previous !== undefined)
|
|
78
152
|
continue;
|
|
79
|
-
if (object.kind === "defaultPrivilege")
|
|
80
|
-
defaults.push(defaultStatement(object, false));
|
|
81
|
-
|
|
82
|
-
grant.push(grantStatement(object, schema, false));
|
|
153
|
+
if (object.kind === "defaultPrivilege") {
|
|
154
|
+
defaults.push({ sql: defaultStatement(object, false), kind: "grant-default" });
|
|
155
|
+
}
|
|
83
156
|
}
|
|
84
157
|
return { revoke, prepare: [...roles, ...defaults], grant };
|
|
85
158
|
}
|
|
@@ -130,6 +203,24 @@ function defaultStatement(object, revoke) {
|
|
|
130
203
|
const namespace = object.identity.namespace.form === "static" ? object.identity.namespace.name : "public";
|
|
131
204
|
return `alter default privileges for role ${quoteIdent(object.identity.forRole)} in schema ${quoteIdent(namespace)} ${verb} ${privilege} on ${objectKindSql(object.identity.objectKind)} ${direction} ${quoteIdent(object.identity.grantee)}`;
|
|
132
205
|
}
|
|
206
|
+
function grantTargetKey(object) {
|
|
207
|
+
if (object.kind === "table" || object.kind === "view" || object.kind === "materializedView" || object.kind === "sequence") {
|
|
208
|
+
return identityKey2({ kind: object.kind, namespace: object.namespace, name: object.name });
|
|
209
|
+
}
|
|
210
|
+
if (object.kind !== "function")
|
|
211
|
+
return;
|
|
212
|
+
const open = object.name.lastIndexOf("(");
|
|
213
|
+
if (open < 0 || !object.name.endsWith(")"))
|
|
214
|
+
return;
|
|
215
|
+
const name = object.name.slice(0, open);
|
|
216
|
+
const args = object.name.slice(open + 1, -1);
|
|
217
|
+
return identityKey2({
|
|
218
|
+
kind: "function",
|
|
219
|
+
namespace: object.namespace,
|
|
220
|
+
name,
|
|
221
|
+
argTypes: args.length === 0 ? [] : args.split(",")
|
|
222
|
+
});
|
|
223
|
+
}
|
|
133
224
|
function grantTarget(object, schema) {
|
|
134
225
|
const name = object.namespace.form === "static" ? object.namespace.name : schema;
|
|
135
226
|
if (object.kind === "sequence")
|
|
@@ -165,6 +256,247 @@ function assertPrivilege(privilege) {
|
|
|
165
256
|
}
|
|
166
257
|
}
|
|
167
258
|
|
|
259
|
+
// src/tooling/migrate/backfill.ts
|
|
260
|
+
var DEFAULT_BACKFILL_BATCH_SIZE = 1000;
|
|
261
|
+
var DEFAULT_BACKFILL_PAUSE_MS = 0;
|
|
262
|
+
var DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS = 30000;
|
|
263
|
+
var HEADER = /^-- backfill table=(\S+) key=(\S+) batch=(\d+)$/;
|
|
264
|
+
var CAST_TYPE = /^[A-Za-z_][\w$ ]*(?:\([^)]*\))?(?:\[\])*$/;
|
|
265
|
+
function batchSizeField(batchSize) {
|
|
266
|
+
return batchSize === undefined ? {} : { batchSize };
|
|
267
|
+
}
|
|
268
|
+
function resolveBatchSize(requested) {
|
|
269
|
+
const batch = requested ?? DEFAULT_BACKFILL_BATCH_SIZE;
|
|
270
|
+
if (!Number.isInteger(batch) || batch < 1) {
|
|
271
|
+
throw new OkmError2("invalid", "backfill.batchSize must be an integer of at least 1.", {
|
|
272
|
+
fix: {
|
|
273
|
+
summary: "Set defineConfig({ backfill: { batchSize } }) to a positive integer."
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return batch;
|
|
278
|
+
}
|
|
279
|
+
function formatBackfillHeader(spec) {
|
|
280
|
+
return `-- backfill table=${spec.table} key=${spec.key.join(",")} batch=${String(spec.batch)}`;
|
|
281
|
+
}
|
|
282
|
+
function parseBackfillHeader(line) {
|
|
283
|
+
if (!line.startsWith("-- backfill "))
|
|
284
|
+
return;
|
|
285
|
+
const match = HEADER.exec(line);
|
|
286
|
+
const table = match?.[1];
|
|
287
|
+
const keyText = match?.[2];
|
|
288
|
+
const batchText = match?.[3];
|
|
289
|
+
const key = keyText === undefined ? [] : splitKey(keyText);
|
|
290
|
+
const batch = batchText === undefined ? Number.NaN : Number(batchText);
|
|
291
|
+
if (table === undefined || key.length === 0 || !Number.isInteger(batch) || batch < 1) {
|
|
292
|
+
throw new OkmError2("invalid", "A backfill header needs table=<table> key=<col> batch=<n>.", {
|
|
293
|
+
fix: {
|
|
294
|
+
summary: "Write -- backfill table=<table> key=<col[,col]> batch=<n> with a batch of at least 1."
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return { table, key, batch };
|
|
299
|
+
}
|
|
300
|
+
function primaryKeyColumns(catalogs, table) {
|
|
301
|
+
for (const source of catalogs) {
|
|
302
|
+
const columns = keyOn(source, table);
|
|
303
|
+
if (columns !== undefined)
|
|
304
|
+
return columns;
|
|
305
|
+
}
|
|
306
|
+
throw new OkmError2("OKM1546", `Table ${table} has no primary key, so it cannot be backfilled.`, {
|
|
307
|
+
fix: {
|
|
308
|
+
summary: "Add a primary key. A backfill walks that key and does not scan the table."
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
function keyRangePredicate(columns) {
|
|
313
|
+
for (const column of columns)
|
|
314
|
+
assertCastType(column.dataType);
|
|
315
|
+
const lower = compareKey(columns, "$1", ">");
|
|
316
|
+
const upper = compareKey(columns, "$2", "<=");
|
|
317
|
+
return `($1::text is null or ${lower}) and ($2::text is null or ${upper})`;
|
|
318
|
+
}
|
|
319
|
+
function aboutBatchCount(rows, batch) {
|
|
320
|
+
if (!Number.isFinite(rows) || batch < 1)
|
|
321
|
+
return 0;
|
|
322
|
+
const rounded = Math.max(0, Math.round(rows));
|
|
323
|
+
if (rounded === 0)
|
|
324
|
+
return 0;
|
|
325
|
+
return Math.ceil(rounded / batch);
|
|
326
|
+
}
|
|
327
|
+
function keyOn(source, table) {
|
|
328
|
+
const constraint = source.objects.find((object) => object.kind === "constraint" && object.definition.constraintKind === "primaryKey" && object.identity.parent.name === table);
|
|
329
|
+
if (constraint === undefined || constraint.kind !== "constraint")
|
|
330
|
+
return;
|
|
331
|
+
const columns = [];
|
|
332
|
+
for (const name of constraint.definition.columns) {
|
|
333
|
+
const column = source.objects.find((object) => object.kind === "column" && object.identity.parent.name === table && object.identity.name === name);
|
|
334
|
+
if (column === undefined || column.kind !== "column") {
|
|
335
|
+
throw new OkmError2("OKM1546", `Table ${table} primary key column ${name} is not in the catalog.`, { fix: { summary: "Keep the primary key columns on the table." } });
|
|
336
|
+
}
|
|
337
|
+
columns.push({ quoted: quoteIdent(name), dataType: column.definition.dataType });
|
|
338
|
+
}
|
|
339
|
+
if (columns.length === 0)
|
|
340
|
+
return;
|
|
341
|
+
return columns;
|
|
342
|
+
}
|
|
343
|
+
function boundaryQuery(table, columns) {
|
|
344
|
+
for (const column of columns)
|
|
345
|
+
checkedCast(column.dataType);
|
|
346
|
+
const order = columns.map((column) => column.quoted).join(", ");
|
|
347
|
+
const first = columns[0];
|
|
348
|
+
const projected = columns.length === 1 && first !== undefined ? `${first.quoted}::text` : `jsonb_build_array(${columns.map((column) => `${column.quoted}::text`).join(", ")})::text`;
|
|
349
|
+
const lower = `($1::text is null or ${compareKey(columns, "$1", ">")})`;
|
|
350
|
+
return `select ${projected} as okm_key from ${table} where ${lower} order by ${order} offset $2::bigint limit 1`;
|
|
351
|
+
}
|
|
352
|
+
function checkedCast(dataType) {
|
|
353
|
+
assertCastType(dataType);
|
|
354
|
+
return dataType;
|
|
355
|
+
}
|
|
356
|
+
function compareKey(columns, param, operator) {
|
|
357
|
+
const first = columns[0];
|
|
358
|
+
if (first === undefined)
|
|
359
|
+
return "false";
|
|
360
|
+
if (columns.length === 1) {
|
|
361
|
+
return `${first.quoted} ${operator} ${param}::${first.dataType}`;
|
|
362
|
+
}
|
|
363
|
+
const left = columns.map((column) => column.quoted).join(", ");
|
|
364
|
+
const right = columns.map((column, index) => `((${param}::jsonb)->>${String(index)})::${column.dataType}`).join(", ");
|
|
365
|
+
return `(${left}) ${operator} (${right})`;
|
|
366
|
+
}
|
|
367
|
+
function assertCastType(dataType) {
|
|
368
|
+
if (CAST_TYPE.test(dataType))
|
|
369
|
+
return;
|
|
370
|
+
throw new OkmError2("OKM1546", `Primary key type ${dataType} cannot be used as a backfill bound.`, {
|
|
371
|
+
fix: {
|
|
372
|
+
summary: "Use an orderable scalar key. A backfill casts each bound to the key type."
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
function splitKey(text) {
|
|
377
|
+
const names = [];
|
|
378
|
+
let index = 0;
|
|
379
|
+
while (index < text.length) {
|
|
380
|
+
if (text[index] === '"') {
|
|
381
|
+
let value = "";
|
|
382
|
+
index += 1;
|
|
383
|
+
let closed = false;
|
|
384
|
+
while (index < text.length) {
|
|
385
|
+
const char = text[index];
|
|
386
|
+
if (char === '"' && text[index + 1] === '"') {
|
|
387
|
+
value += '"';
|
|
388
|
+
index += 2;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (char === '"') {
|
|
392
|
+
index += 1;
|
|
393
|
+
closed = true;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
value += char ?? "";
|
|
397
|
+
index += 1;
|
|
398
|
+
}
|
|
399
|
+
if (!closed)
|
|
400
|
+
return [];
|
|
401
|
+
names.push(quoteIdent(value));
|
|
402
|
+
if (text[index] === ",")
|
|
403
|
+
index += 1;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const comma = text.indexOf(",", index);
|
|
407
|
+
const end = comma === -1 ? text.length : comma;
|
|
408
|
+
const raw = text.slice(index, end);
|
|
409
|
+
if (raw.length === 0)
|
|
410
|
+
return [];
|
|
411
|
+
names.push(quoteIdent(raw));
|
|
412
|
+
index = comma === -1 ? text.length : comma + 1;
|
|
413
|
+
}
|
|
414
|
+
return names;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/tooling/migrate/classify.ts
|
|
418
|
+
var STEP_CLASS = {
|
|
419
|
+
"create-table": "expand",
|
|
420
|
+
"drop-table": "contract",
|
|
421
|
+
"add-column": "expand",
|
|
422
|
+
"add-column-required": "contract",
|
|
423
|
+
"drop-column": "contract",
|
|
424
|
+
"set-column-type": "contract",
|
|
425
|
+
"set-not-null": "contract",
|
|
426
|
+
"drop-not-null": "expand",
|
|
427
|
+
"set-default": "expand",
|
|
428
|
+
"drop-default": "contract",
|
|
429
|
+
"add-identity": "expand",
|
|
430
|
+
"drop-identity": "contract",
|
|
431
|
+
"set-identity-always": "contract",
|
|
432
|
+
"set-identity-by-default": "expand",
|
|
433
|
+
"add-constraint": "expand",
|
|
434
|
+
"validate-constraint": "expand",
|
|
435
|
+
"drop-constraint": "contract",
|
|
436
|
+
"drop-not-null-check": "expand",
|
|
437
|
+
"rename-constraint": "contract",
|
|
438
|
+
"widen-check": "expand",
|
|
439
|
+
"narrow-check": "contract",
|
|
440
|
+
"create-index": "expand",
|
|
441
|
+
"drop-index": "contract",
|
|
442
|
+
"rename-index": "contract",
|
|
443
|
+
"create-sequence": "expand",
|
|
444
|
+
"drop-sequence": "contract",
|
|
445
|
+
"create-enum": "expand",
|
|
446
|
+
"recreate-enum": "contract",
|
|
447
|
+
"add-enum-value": "expand",
|
|
448
|
+
"rename-enum": "contract",
|
|
449
|
+
"drop-enum": "contract",
|
|
450
|
+
"set-enum-column": "contract",
|
|
451
|
+
"create-domain": "expand",
|
|
452
|
+
"drop-domain": "contract",
|
|
453
|
+
"add-domain-check": "expand",
|
|
454
|
+
"validate-domain-check": "expand",
|
|
455
|
+
"drop-domain-check": "contract",
|
|
456
|
+
"rename-domain-check": "expand",
|
|
457
|
+
"create-extension": "expand",
|
|
458
|
+
"drop-extension": "contract",
|
|
459
|
+
"update-extension": "expand",
|
|
460
|
+
"move-extension": "contract",
|
|
461
|
+
"create-function": "expand",
|
|
462
|
+
"drop-function": "contract",
|
|
463
|
+
"replace-function": "expand",
|
|
464
|
+
"create-trigger": "expand",
|
|
465
|
+
"drop-trigger": "contract",
|
|
466
|
+
"create-view": "expand",
|
|
467
|
+
"drop-view": "contract",
|
|
468
|
+
"replace-view": "expand",
|
|
469
|
+
"create-matview": "expand",
|
|
470
|
+
"drop-matview": "contract",
|
|
471
|
+
"refresh-matview": "expand",
|
|
472
|
+
"create-role": "expand",
|
|
473
|
+
"alter-role": "contract",
|
|
474
|
+
grant: "expand",
|
|
475
|
+
revoke: "contract",
|
|
476
|
+
"grant-default": "expand",
|
|
477
|
+
"revoke-default": "contract",
|
|
478
|
+
"rename-table": "contract",
|
|
479
|
+
"rename-column": "contract",
|
|
480
|
+
"rename-sequence": "contract",
|
|
481
|
+
"backfill-expand": "expand",
|
|
482
|
+
"backfill-contract": "contract",
|
|
483
|
+
"raw-sql": "unclassified"
|
|
484
|
+
};
|
|
485
|
+
var STEP_KINDS = Object.keys(STEP_CLASS);
|
|
486
|
+
function classOf(kind) {
|
|
487
|
+
return STEP_CLASS[kind];
|
|
488
|
+
}
|
|
489
|
+
function strictestClass(classes) {
|
|
490
|
+
if (classes.includes("contract"))
|
|
491
|
+
return "contract";
|
|
492
|
+
if (classes.includes("unclassified"))
|
|
493
|
+
return "unclassified";
|
|
494
|
+
return "expand";
|
|
495
|
+
}
|
|
496
|
+
function isStepKind(value) {
|
|
497
|
+
return Object.hasOwn(STEP_CLASS, value);
|
|
498
|
+
}
|
|
499
|
+
|
|
168
500
|
// src/tooling/migrate/extensions.ts
|
|
169
501
|
var CREATE_EXTENSION = /^\s*create\s+extension\s+(?:"([^"]+)"|([A-Za-z_][\w$]*))/i;
|
|
170
502
|
var UPDATE_EXTENSION = /^\s*alter\s+extension\s+(?:"([^"]+)"|([A-Za-z_][\w$]*))\s+update\s+to\s+'([^']*)'/i;
|
|
@@ -255,13 +587,7 @@ function alterExtension(before, after) {
|
|
|
255
587
|
}
|
|
256
588
|
});
|
|
257
589
|
}
|
|
258
|
-
steps.push({
|
|
259
|
-
sql: `alter extension ${name} set schema ${quoteIdent(after.definition.schema)}`,
|
|
260
|
-
class: "contract",
|
|
261
|
-
action: "ddl",
|
|
262
|
-
lock: "ACCESS EXCLUSIVE",
|
|
263
|
-
transactional: true
|
|
264
|
-
});
|
|
590
|
+
steps.push(extensionStep(`alter extension ${name} set schema ${quoteIdent(after.definition.schema)}`, "move-extension"));
|
|
265
591
|
}
|
|
266
592
|
const previous = before.definition.version;
|
|
267
593
|
const next = after.definition.version;
|
|
@@ -274,15 +600,27 @@ function alterExtension(before, after) {
|
|
|
274
600
|
}
|
|
275
601
|
});
|
|
276
602
|
}
|
|
277
|
-
steps.push({
|
|
278
|
-
|
|
279
|
-
|
|
603
|
+
steps.push(extensionStep(`alter extension ${name} update to ${quoteLiteral(next)}`, "update-extension", {
|
|
604
|
+
path: "unverified"
|
|
605
|
+
}));
|
|
606
|
+
return steps;
|
|
607
|
+
}
|
|
608
|
+
function extensionStep(sql, kind, extra) {
|
|
609
|
+
const classification = classOf(kind);
|
|
610
|
+
if (classification === "unclassified") {
|
|
611
|
+
throw new OkmError2("invalid", `Extension step ${kind} has no class.`, {
|
|
612
|
+
fix: { summary: "Classify the extension step as expand or contract." }
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
sql,
|
|
617
|
+
kind,
|
|
618
|
+
class: classification,
|
|
280
619
|
action: "ddl",
|
|
281
620
|
lock: "ACCESS EXCLUSIVE",
|
|
282
621
|
transactional: true,
|
|
283
|
-
path:
|
|
284
|
-
}
|
|
285
|
-
return steps;
|
|
622
|
+
...extra?.path !== undefined ? { path: extra.path } : {}
|
|
623
|
+
};
|
|
286
624
|
}
|
|
287
625
|
async function availableExtensions(connection, names) {
|
|
288
626
|
const listed = await connection.execute(`select name from pg_available_extensions where name in (${names.map((_, index) => `$${String(index + 1)}`).join(", ")})`, names);
|
|
@@ -306,7 +644,7 @@ async function updatePath(connection, name, source, target) {
|
|
|
306
644
|
}
|
|
307
645
|
|
|
308
646
|
// src/tooling/migrate/managed.ts
|
|
309
|
-
var MANAGED_TABLES = new Set(["okm_meta", "okm_history"]);
|
|
647
|
+
var MANAGED_TABLES = new Set(["okm_meta", "okm_history", "okm_backfill"]);
|
|
310
648
|
function omitManagedObjects(source) {
|
|
311
649
|
let dropped = false;
|
|
312
650
|
const objects = [];
|
|
@@ -348,72 +686,293 @@ function ownedByMigration(object) {
|
|
|
348
686
|
}
|
|
349
687
|
}
|
|
350
688
|
function managedSequence(name) {
|
|
351
|
-
return name === "okm_meta" || name === "okm_history" || name.startsWith("okm_meta_") || name.startsWith("okm_history_");
|
|
689
|
+
return name === "okm_meta" || name === "okm_history" || name === "okm_backfill" || name.startsWith("okm_meta_") || name.startsWith("okm_history_") || name.startsWith("okm_backfill_");
|
|
352
690
|
}
|
|
353
691
|
|
|
354
|
-
// src/tooling/migrate/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
692
|
+
// src/tooling/migrate/safe.ts
|
|
693
|
+
var ACCESS = "ACCESS EXCLUSIVE";
|
|
694
|
+
var SHARE_UPDATE = "SHARE UPDATE EXCLUSIVE";
|
|
695
|
+
var ROW = "ROW EXCLUSIVE";
|
|
696
|
+
var VOLATILE_BUILTIN = new Set([
|
|
697
|
+
"clock_timestamp",
|
|
698
|
+
"gen_random_uuid",
|
|
699
|
+
"nextval",
|
|
700
|
+
"random",
|
|
701
|
+
"random_normal",
|
|
702
|
+
"setval",
|
|
703
|
+
"timeofday",
|
|
704
|
+
"uuidv7"
|
|
705
|
+
]);
|
|
706
|
+
var STABLE_BUILTIN = new Set([
|
|
707
|
+
"current_catalog",
|
|
708
|
+
"current_date",
|
|
709
|
+
"current_role",
|
|
710
|
+
"current_schema",
|
|
711
|
+
"current_time",
|
|
712
|
+
"current_timestamp",
|
|
713
|
+
"current_user",
|
|
714
|
+
"localtime",
|
|
715
|
+
"localtimestamp",
|
|
716
|
+
"now",
|
|
717
|
+
"session_user",
|
|
718
|
+
"statement_timestamp",
|
|
719
|
+
"transaction_timestamp"
|
|
720
|
+
]);
|
|
721
|
+
function stepsForExistingTable(object, schema, catalogs, batchSize) {
|
|
722
|
+
if (object.kind === "index")
|
|
723
|
+
return [concurrentIndexStep(object, schema)];
|
|
724
|
+
if (object.kind === "constraint")
|
|
725
|
+
return constraintSteps(object, schema);
|
|
726
|
+
if (object.kind === "column" && volatileDefault(object.definition.defaultExpression, catalogs)) {
|
|
727
|
+
return volatileColumnSteps(object, schema, catalogs, batchSize);
|
|
728
|
+
}
|
|
729
|
+
return;
|
|
359
730
|
}
|
|
360
|
-
function
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
731
|
+
function notNullSteps(column, schema, setKind, catalogs) {
|
|
732
|
+
const tableName = column.identity.parent.name;
|
|
733
|
+
const columnName = column.identity.name;
|
|
734
|
+
const table = qualify(schema, tableName);
|
|
735
|
+
const quotedColumn = quoteIdent(columnName);
|
|
736
|
+
const constraint = quoteIdent(notNullConstraintName(tableName, columnName, catalogs));
|
|
737
|
+
return [
|
|
738
|
+
{
|
|
739
|
+
sql: `alter table ${table} add constraint ${constraint} check (${quotedColumn} is not null) not valid`,
|
|
740
|
+
kind: "add-constraint",
|
|
741
|
+
action: "ddl",
|
|
742
|
+
lock: ACCESS,
|
|
743
|
+
transactional: true
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
sql: `alter table ${table} validate constraint ${constraint}`,
|
|
747
|
+
kind: "validate-constraint",
|
|
748
|
+
action: "ddl",
|
|
749
|
+
lock: SHARE_UPDATE,
|
|
750
|
+
transactional: true
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
sql: `alter table ${table} alter column ${quotedColumn} set not null`,
|
|
754
|
+
kind: setKind,
|
|
755
|
+
action: "ddl",
|
|
756
|
+
lock: ACCESS,
|
|
757
|
+
transactional: true
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
sql: `alter table ${table} drop constraint ${constraint}`,
|
|
761
|
+
kind: "drop-not-null-check",
|
|
762
|
+
action: "ddl",
|
|
763
|
+
lock: ACCESS,
|
|
764
|
+
transactional: true
|
|
765
|
+
}
|
|
766
|
+
];
|
|
767
|
+
}
|
|
768
|
+
function concurrentIndexSql(sql) {
|
|
769
|
+
const unique = "create unique index ";
|
|
770
|
+
const plain = "create index ";
|
|
771
|
+
if (sql.startsWith(unique))
|
|
772
|
+
return `create unique index concurrently ${sql.slice(unique.length)}`;
|
|
773
|
+
if (sql.startsWith(plain))
|
|
774
|
+
return `create index concurrently ${sql.slice(plain.length)}`;
|
|
775
|
+
return sql;
|
|
776
|
+
}
|
|
777
|
+
function concurrentDropIndexSql(sql) {
|
|
778
|
+
const prefix = "drop index ";
|
|
779
|
+
if (!sql.startsWith(prefix))
|
|
780
|
+
return sql;
|
|
781
|
+
return `drop index concurrently ${sql.slice(prefix.length)}`;
|
|
782
|
+
}
|
|
783
|
+
function volatileDefault(expression, catalogs) {
|
|
784
|
+
if (expression === undefined)
|
|
785
|
+
return false;
|
|
786
|
+
const known = catalogVolatility(catalogs);
|
|
787
|
+
for (const name of functionCalls(expression)) {
|
|
788
|
+
const lower = name.toLowerCase();
|
|
789
|
+
if (VOLATILE_BUILTIN.has(lower))
|
|
790
|
+
return true;
|
|
791
|
+
if (STABLE_BUILTIN.has(lower))
|
|
792
|
+
continue;
|
|
793
|
+
if (known.get(lower) === "volatile")
|
|
794
|
+
return true;
|
|
366
795
|
}
|
|
367
|
-
return
|
|
796
|
+
return false;
|
|
368
797
|
}
|
|
369
|
-
function
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
798
|
+
function concurrentIndexStep(object, schema) {
|
|
799
|
+
const plain = createObjectSql(object, schema) ?? "";
|
|
800
|
+
return {
|
|
801
|
+
sql: concurrentIndexSql(plain),
|
|
802
|
+
kind: "create-index",
|
|
803
|
+
action: "ddl",
|
|
804
|
+
lock: SHARE_UPDATE,
|
|
805
|
+
transactional: false
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
function constraintSteps(object, schema) {
|
|
809
|
+
const kind = object.definition.constraintKind;
|
|
810
|
+
if (kind === "unique" || kind === "primaryKey")
|
|
811
|
+
return usingIndexSteps(object, schema);
|
|
812
|
+
return notValidSteps(object, schema);
|
|
813
|
+
}
|
|
814
|
+
function notValidSteps(object, schema) {
|
|
815
|
+
const plain = createObjectSql(object, schema);
|
|
816
|
+
if (plain === undefined)
|
|
817
|
+
return [];
|
|
818
|
+
const table = qualify(schema, object.identity.parent.name);
|
|
819
|
+
const name = quoteIdent(object.identity.name);
|
|
820
|
+
const referenced = object.definition.references;
|
|
821
|
+
const referencedLock = referenced === undefined ? undefined : qualify(schema, referenced.parent.name);
|
|
822
|
+
return [
|
|
823
|
+
{
|
|
824
|
+
sql: `${plain} not valid`,
|
|
825
|
+
kind: "add-constraint",
|
|
826
|
+
action: "ddl",
|
|
827
|
+
lock: referencedLock === undefined ? ACCESS : `${ACCESS}; SHARE ROW EXCLUSIVE on ${referencedLock}`,
|
|
828
|
+
transactional: true
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
sql: `alter table ${table} validate constraint ${name}`,
|
|
832
|
+
kind: "validate-constraint",
|
|
833
|
+
action: "ddl",
|
|
834
|
+
lock: referencedLock === undefined ? SHARE_UPDATE : `${SHARE_UPDATE}; ROW SHARE on ${referencedLock}`,
|
|
835
|
+
transactional: true
|
|
836
|
+
}
|
|
837
|
+
];
|
|
838
|
+
}
|
|
839
|
+
function usingIndexSteps(object, schema) {
|
|
840
|
+
const table = qualify(schema, object.identity.parent.name);
|
|
841
|
+
const name = quoteIdent(object.identity.name);
|
|
842
|
+
const columns = object.definition.columns.map((column) => quoteIdent(column)).join(", ");
|
|
843
|
+
const nulls = object.definition.nullsNotDistinct ? " nulls not distinct" : "";
|
|
844
|
+
const verb = object.definition.constraintKind === "primaryKey" ? "primary key" : "unique";
|
|
845
|
+
const defer = object.definition.deferrable ? ` deferrable initially ${object.definition.initially}` : "";
|
|
846
|
+
return [
|
|
847
|
+
{
|
|
848
|
+
sql: `create unique index concurrently ${name} on ${table} (${columns})${nulls}`,
|
|
849
|
+
kind: "create-index",
|
|
850
|
+
action: "ddl",
|
|
851
|
+
lock: SHARE_UPDATE,
|
|
852
|
+
transactional: false
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
sql: `alter table ${table} add constraint ${name} ${verb} using index ${name}${defer}`,
|
|
856
|
+
kind: "add-constraint",
|
|
857
|
+
action: "ddl",
|
|
858
|
+
lock: ACCESS,
|
|
859
|
+
transactional: true
|
|
860
|
+
}
|
|
861
|
+
];
|
|
862
|
+
}
|
|
863
|
+
function volatileColumnSteps(column, schema, catalogs, batchSize) {
|
|
864
|
+
const tableName = column.identity.parent.name;
|
|
865
|
+
const table = qualify(schema, tableName);
|
|
866
|
+
const name = quoteIdent(column.identity.name);
|
|
867
|
+
const collate = column.definition.collation === undefined ? "" : ` collate ${quoteIdent(column.definition.collation)}`;
|
|
868
|
+
const expression = column.definition.defaultExpression ?? "";
|
|
869
|
+
const key = primaryKeyColumns(catalogs, tableName);
|
|
870
|
+
const backfill = {
|
|
871
|
+
table,
|
|
872
|
+
key: key.map((item) => item.quoted),
|
|
873
|
+
batch: batchSize
|
|
874
|
+
};
|
|
875
|
+
const steps = [
|
|
876
|
+
{
|
|
877
|
+
sql: `alter table ${table} add column ${name} ${column.definition.dataType}${collate}`,
|
|
878
|
+
kind: "add-column",
|
|
879
|
+
action: "ddl",
|
|
880
|
+
lock: ACCESS,
|
|
881
|
+
transactional: true
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
sql: `alter table ${table} alter column ${name} set default ${expression}`,
|
|
885
|
+
kind: "set-default",
|
|
886
|
+
action: "ddl",
|
|
887
|
+
lock: ACCESS,
|
|
888
|
+
transactional: true
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
sql: `update ${table} set ${name} = ${expression} where ${name} is null and ${keyRangePredicate(key)}`,
|
|
892
|
+
kind: "backfill-expand",
|
|
893
|
+
action: "backfill",
|
|
894
|
+
lock: ROW,
|
|
895
|
+
transactional: false,
|
|
896
|
+
backfill
|
|
897
|
+
}
|
|
898
|
+
];
|
|
899
|
+
if (!column.definition.nullable) {
|
|
900
|
+
steps.push(...notNullSteps(column, schema, "add-column", catalogs));
|
|
404
901
|
}
|
|
902
|
+
return steps;
|
|
405
903
|
}
|
|
406
|
-
function
|
|
407
|
-
|
|
408
|
-
|
|
904
|
+
function notNullConstraintName(table, column, catalogs) {
|
|
905
|
+
const taken = new Set;
|
|
906
|
+
for (const source of catalogs) {
|
|
907
|
+
for (const object of source.objects) {
|
|
908
|
+
if (object.kind === "constraint" || object.kind === "index")
|
|
909
|
+
taken.add(object.identity.name);
|
|
910
|
+
}
|
|
409
911
|
}
|
|
410
|
-
|
|
411
|
-
|
|
912
|
+
const base = fitIdentifier2(`${table}_${column}_notnull`);
|
|
913
|
+
if (!taken.has(base))
|
|
914
|
+
return base;
|
|
915
|
+
return fitIdentifier2(`${table}_${column}_notnull_okm`);
|
|
916
|
+
}
|
|
917
|
+
function catalogVolatility(catalogs) {
|
|
918
|
+
const map = new Map;
|
|
919
|
+
for (const source of catalogs) {
|
|
920
|
+
for (const object of source.objects) {
|
|
921
|
+
if (object.kind !== "function")
|
|
922
|
+
continue;
|
|
923
|
+
const name = object.identity.name.toLowerCase();
|
|
924
|
+
const next = object.definition.volatility;
|
|
925
|
+
const current = map.get(name);
|
|
926
|
+
if (current === "volatile" || next === "volatile")
|
|
927
|
+
map.set(name, "volatile");
|
|
928
|
+
else if (current === undefined)
|
|
929
|
+
map.set(name, next);
|
|
930
|
+
}
|
|
412
931
|
}
|
|
413
|
-
|
|
414
|
-
|
|
932
|
+
return map;
|
|
933
|
+
}
|
|
934
|
+
function functionCalls(expression) {
|
|
935
|
+
const names = [];
|
|
936
|
+
let index = 0;
|
|
937
|
+
while (index < expression.length) {
|
|
938
|
+
const char = expression[index];
|
|
939
|
+
if (char === "'") {
|
|
940
|
+
index += 1;
|
|
941
|
+
while (index < expression.length) {
|
|
942
|
+
if (expression[index] === "'" && expression[index + 1] === "'") {
|
|
943
|
+
index += 2;
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
if (expression[index] === "'") {
|
|
947
|
+
index += 1;
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
index += 1;
|
|
951
|
+
}
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
if (char !== undefined && /[A-Za-z_]/.test(char)) {
|
|
955
|
+
const start = index;
|
|
956
|
+
index += 1;
|
|
957
|
+
while (index < expression.length && /[\w$]/.test(expression[index] ?? ""))
|
|
958
|
+
index += 1;
|
|
959
|
+
let name = expression.slice(start, index);
|
|
960
|
+
if (expression[index] === ".") {
|
|
961
|
+
index += 1;
|
|
962
|
+
const qualifier = index;
|
|
963
|
+
while (index < expression.length && /[\w$]/.test(expression[index] ?? ""))
|
|
964
|
+
index += 1;
|
|
965
|
+
name = expression.slice(qualifier, index);
|
|
966
|
+
}
|
|
967
|
+
while (expression[index] === " ")
|
|
968
|
+
index += 1;
|
|
969
|
+
if (expression[index] === "(")
|
|
970
|
+
names.push(name);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
index += 1;
|
|
415
974
|
}
|
|
416
|
-
return
|
|
975
|
+
return names;
|
|
417
976
|
}
|
|
418
977
|
|
|
419
978
|
// src/tooling/migrate/values.ts
|
|
@@ -458,10 +1017,10 @@ function pair(table, column, value) {
|
|
|
458
1017
|
}
|
|
459
1018
|
|
|
460
1019
|
// src/tooling/migrate/plan.ts
|
|
461
|
-
var
|
|
1020
|
+
var ACCESS2 = "ACCESS EXCLUSIVE";
|
|
462
1021
|
var SHARE = "SHARE";
|
|
463
|
-
var
|
|
464
|
-
var
|
|
1022
|
+
var SHARE_UPDATE2 = "SHARE UPDATE EXCLUSIVE";
|
|
1023
|
+
var ROW2 = "ROW EXCLUSIVE";
|
|
465
1024
|
function planMigration(input) {
|
|
466
1025
|
const request = {
|
|
467
1026
|
...input,
|
|
@@ -469,6 +1028,7 @@ function planMigration(input) {
|
|
|
469
1028
|
after: omitManagedObjects(input.after)
|
|
470
1029
|
};
|
|
471
1030
|
const schema = request.schema ?? "public";
|
|
1031
|
+
const batchSize = resolveBatchSize(request.batchSize);
|
|
472
1032
|
const renames = request.renames ?? [];
|
|
473
1033
|
const replacements = request.replacements ?? [];
|
|
474
1034
|
assertNoChains(replacements);
|
|
@@ -532,15 +1092,20 @@ function planMigration(input) {
|
|
|
532
1092
|
refuseRoutineDrops(beforeBy, afterBy, dropKeys);
|
|
533
1093
|
omitOwnedSequences(createKeys, afterBy, request.after);
|
|
534
1094
|
omitOwnedSequences(dropKeys, beforeBy, renamed);
|
|
535
|
-
const
|
|
1095
|
+
const recreated = new Set;
|
|
1096
|
+
for (const key of createKeys) {
|
|
1097
|
+
if (dropKeys.has(key))
|
|
1098
|
+
recreated.add(key);
|
|
1099
|
+
}
|
|
1100
|
+
const privileges = privilegeSql(renamed.objects, request.after.objects, schema, recreated);
|
|
536
1101
|
const steps = [];
|
|
537
1102
|
steps.push(...renameSteps(request.before, renames, schema));
|
|
538
|
-
for (const
|
|
539
|
-
steps.push(step(sql,
|
|
1103
|
+
for (const item of privileges.revoke)
|
|
1104
|
+
steps.push(step(item.sql, item.kind, "ddl", ACCESS2));
|
|
540
1105
|
steps.push(...keptNames.map((item) => renameShapeStep(item, schema)));
|
|
541
1106
|
steps.push(...enumAddSteps(enums));
|
|
542
1107
|
steps.push(...domainCheckSteps(beforeBy, afterBy, schema));
|
|
543
|
-
steps.push(...expandBackfills(picklists, enums, replacements, schema));
|
|
1108
|
+
steps.push(...expandBackfills(picklists, enums, replacements, schema, [renamed, request.after], batchSize));
|
|
544
1109
|
const droppedTables = new Set([...dropKeys].map((key) => beforeBy.get(key)).filter((object) => object?.kind === "table").map((object) => object.identity.name));
|
|
545
1110
|
const deferred = new Set(picklists.map((change) => identityKey2(change.before.identity)));
|
|
546
1111
|
const typeDrops = [];
|
|
@@ -551,9 +1116,19 @@ function planMigration(input) {
|
|
|
551
1116
|
if (!dropKeys.has(key) || deferred.has(key) || covered(object, droppedTables))
|
|
552
1117
|
return;
|
|
553
1118
|
const sql = dropObjectSql(object, schema);
|
|
554
|
-
|
|
1119
|
+
const kind = dropKind(object);
|
|
1120
|
+
if (sql === undefined || kind === undefined)
|
|
1121
|
+
return;
|
|
1122
|
+
if (object.kind === "index" && hasTable(renamed, object.identity.parent.name)) {
|
|
1123
|
+
steps.push({
|
|
1124
|
+
...step(concurrentDropIndexSql(sql), "drop-index", "ddl", SHARE_UPDATE2, false, [
|
|
1125
|
+
object.identity.parent.name
|
|
1126
|
+
]),
|
|
1127
|
+
safeRewrite: true
|
|
1128
|
+
});
|
|
555
1129
|
return;
|
|
556
|
-
|
|
1130
|
+
}
|
|
1131
|
+
steps.push(step(sql, kind, "ddl", ACCESS2));
|
|
557
1132
|
};
|
|
558
1133
|
for (const object of reverse)
|
|
559
1134
|
if (object.kind === "trigger")
|
|
@@ -585,31 +1160,36 @@ function planMigration(input) {
|
|
|
585
1160
|
}
|
|
586
1161
|
emitDrop(object);
|
|
587
1162
|
}
|
|
588
|
-
for (const object of typeDrops)
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
steps.push(step(sql, "contract", "ddl", ACCESS));
|
|
593
|
-
}
|
|
594
|
-
for (const object of extensionDrops) {
|
|
595
|
-
const sql = dropObjectSql(object, schema);
|
|
596
|
-
if (sql === undefined)
|
|
597
|
-
continue;
|
|
598
|
-
steps.push(step(sql, "contract", "ddl", ACCESS));
|
|
599
|
-
}
|
|
1163
|
+
for (const object of typeDrops)
|
|
1164
|
+
pushDrop(steps, object, schema);
|
|
1165
|
+
for (const object of extensionDrops)
|
|
1166
|
+
pushDrop(steps, object, schema);
|
|
600
1167
|
steps.push(...extensionAlterSteps(beforeBy, afterBy, dropKeys));
|
|
601
1168
|
for (const change of alters) {
|
|
602
|
-
|
|
603
|
-
|
|
1169
|
+
const aligned = alignedColumnType(change.before, change.after);
|
|
1170
|
+
const statements = alterColumnSql(aligned, change.after, schema);
|
|
1171
|
+
const kinds = columnAlterKinds(aligned, change.after);
|
|
1172
|
+
for (let index = 0;index < statements.length; index += 1) {
|
|
1173
|
+
const sql = statements[index];
|
|
1174
|
+
const kind = kinds[index];
|
|
1175
|
+
if (sql === undefined || kind === undefined)
|
|
1176
|
+
continue;
|
|
1177
|
+
if (kind === "set-not-null") {
|
|
1178
|
+
pushSafe(steps, notNullSteps(change.after, schema, "set-not-null", [renamed, request.after]));
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
steps.push(step(sql, kind, "ddl", ACCESS2));
|
|
604
1182
|
}
|
|
605
1183
|
const identitySql = identityChangeSql(change.before, change.after, schema);
|
|
606
|
-
|
|
607
|
-
|
|
1184
|
+
const identity = identityKind(change.before, change.after);
|
|
1185
|
+
if (identitySql !== undefined && identity !== undefined) {
|
|
1186
|
+
steps.push(step(identitySql, identity, "ddl", ACCESS2));
|
|
1187
|
+
}
|
|
608
1188
|
}
|
|
609
1189
|
const createdTables = new Set([...createKeys].map((key) => afterBy.get(key)).filter((object) => object?.kind === "table").map((object) => object.identity.name));
|
|
610
1190
|
const emitted = new Set;
|
|
611
|
-
for (const
|
|
612
|
-
steps.push(step(sql,
|
|
1191
|
+
for (const item of privileges.prepare)
|
|
1192
|
+
steps.push(step(item.sql, item.kind, "ddl", ACCESS2));
|
|
613
1193
|
for (const object of creationOrder2(request.after)) {
|
|
614
1194
|
if (object.kind !== "extension")
|
|
615
1195
|
continue;
|
|
@@ -618,9 +1198,10 @@ function planMigration(input) {
|
|
|
618
1198
|
continue;
|
|
619
1199
|
emitted.add(key);
|
|
620
1200
|
const sql = createObjectSql(object, schema);
|
|
621
|
-
|
|
1201
|
+
const kind = createKind(object, false);
|
|
1202
|
+
if (sql === undefined || kind === undefined)
|
|
622
1203
|
continue;
|
|
623
|
-
steps.push(step(sql,
|
|
1204
|
+
steps.push(step(sql, kind, "ddl", ACCESS2));
|
|
624
1205
|
}
|
|
625
1206
|
for (const object of creationOrder2(request.after)) {
|
|
626
1207
|
if (object.kind !== "type")
|
|
@@ -630,9 +1211,10 @@ function planMigration(input) {
|
|
|
630
1211
|
continue;
|
|
631
1212
|
emitted.add(key);
|
|
632
1213
|
const sql = createObjectSql(object, schema);
|
|
633
|
-
|
|
1214
|
+
const kind = createKind(object, false);
|
|
1215
|
+
if (sql === undefined || kind === undefined)
|
|
634
1216
|
continue;
|
|
635
|
-
steps.push(step(sql,
|
|
1217
|
+
steps.push(step(sql, kind, "ddl", ACCESS2));
|
|
636
1218
|
}
|
|
637
1219
|
for (const object of creationOrder2(request.after)) {
|
|
638
1220
|
if (object.kind === "type" || object.kind === "extension" || object.kind === "function" || object.kind === "trigger" || object.kind === "view" || object.kind === "materializedView" || ownedByView(object)) {
|
|
@@ -650,13 +1232,21 @@ function planMigration(input) {
|
|
|
650
1232
|
continue;
|
|
651
1233
|
emitted.add(key);
|
|
652
1234
|
if (object.kind === "table") {
|
|
653
|
-
steps.push(step(createTableSql(object, request.after, schema), "
|
|
1235
|
+
steps.push(step(createTableSql(object, request.after, schema), "create-table", "ddl", ACCESS2));
|
|
654
1236
|
continue;
|
|
655
1237
|
}
|
|
656
1238
|
const sql = createObjectSql(object, schema);
|
|
657
|
-
|
|
1239
|
+
const kind = createKind(object, tableIsNew);
|
|
1240
|
+
if (sql === undefined || kind === undefined)
|
|
658
1241
|
continue;
|
|
659
|
-
|
|
1242
|
+
if (!tableIsNew && parent !== undefined && hasTable(renamed, parent)) {
|
|
1243
|
+
const safe = stepsForExistingTable(object, schema, [renamed, request.after], batchSize);
|
|
1244
|
+
if (safe !== undefined) {
|
|
1245
|
+
pushSafe(steps, safe);
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
steps.push(step(sql, kind, "ddl", object.kind === "index" ? SHARE : ACCESS2));
|
|
660
1250
|
}
|
|
661
1251
|
for (const change of replaces) {
|
|
662
1252
|
if (sameDefinition(change.before, change.after))
|
|
@@ -665,11 +1255,13 @@ function planMigration(input) {
|
|
|
665
1255
|
if (dropKeys.has(key) && createKeys.has(key))
|
|
666
1256
|
continue;
|
|
667
1257
|
const sql = change.kind === "function" ? functionSql(change.after, schema, true) : viewSql(change.after, schema, true);
|
|
1258
|
+
const kind = change.kind === "function" ? "replace-function" : "replace-view";
|
|
668
1259
|
steps.push({
|
|
669
1260
|
sql,
|
|
670
|
-
|
|
1261
|
+
kind,
|
|
1262
|
+
class: classOf(kind),
|
|
671
1263
|
action: "ddl",
|
|
672
|
-
lock:
|
|
1264
|
+
lock: ACCESS2,
|
|
673
1265
|
transactional: true,
|
|
674
1266
|
behavior: "change"
|
|
675
1267
|
});
|
|
@@ -683,9 +1275,10 @@ function planMigration(input) {
|
|
|
683
1275
|
continue;
|
|
684
1276
|
emitted.add(key);
|
|
685
1277
|
const sql = createObjectSql(object, schema);
|
|
686
|
-
|
|
1278
|
+
const created = createKind(object, false);
|
|
1279
|
+
if (sql === undefined || created === undefined)
|
|
687
1280
|
continue;
|
|
688
|
-
steps.push(step(sql,
|
|
1281
|
+
steps.push(step(sql, created, "ddl", ACCESS2));
|
|
689
1282
|
}
|
|
690
1283
|
}
|
|
691
1284
|
for (const object of creationOrder2(request.after)) {
|
|
@@ -696,9 +1289,10 @@ function planMigration(input) {
|
|
|
696
1289
|
continue;
|
|
697
1290
|
emitted.add(key);
|
|
698
1291
|
const sql = createObjectSql(object, schema);
|
|
699
|
-
|
|
1292
|
+
const kind = createKind(object, false);
|
|
1293
|
+
if (sql === undefined || kind === undefined)
|
|
700
1294
|
continue;
|
|
701
|
-
steps.push(step(sql,
|
|
1295
|
+
steps.push(step(sql, kind, "ddl", SHARE));
|
|
702
1296
|
}
|
|
703
1297
|
for (const object of creationOrder2(request.after)) {
|
|
704
1298
|
if (object.kind !== "materializedView")
|
|
@@ -707,18 +1301,18 @@ function planMigration(input) {
|
|
|
707
1301
|
if (!createKeys.has(key))
|
|
708
1302
|
continue;
|
|
709
1303
|
assertConcurrentRefresh(object, request.after);
|
|
710
|
-
steps.push(step(refreshMaterializedViewSql(object, schema, false), "
|
|
1304
|
+
steps.push(step(refreshMaterializedViewSql(object, schema, false), "refresh-matview", "backfill", ACCESS2));
|
|
711
1305
|
}
|
|
712
|
-
for (const
|
|
713
|
-
steps.push(step(sql,
|
|
714
|
-
steps.push(...contractSwaps(picklists, enums, replacements, schema));
|
|
1306
|
+
for (const item of privileges.grant)
|
|
1307
|
+
steps.push(step(item.sql, item.kind, "ddl", ACCESS2));
|
|
1308
|
+
steps.push(...contractSwaps(picklists, enums, replacements, schema, [renamed, request.after], batchSize));
|
|
715
1309
|
return {
|
|
716
1310
|
name: request.name ?? "migration",
|
|
717
1311
|
class: overall(steps),
|
|
718
1312
|
steps
|
|
719
1313
|
};
|
|
720
1314
|
}
|
|
721
|
-
function formatPlan(plan) {
|
|
1315
|
+
function formatPlan(plan, lockText) {
|
|
722
1316
|
const lines = [`-- class: ${plan.class}`, `-- name: ${plan.name}`, ""];
|
|
723
1317
|
if (plan.steps.length === 0) {
|
|
724
1318
|
lines.push("-- no steps");
|
|
@@ -728,14 +1322,21 @@ function formatPlan(plan) {
|
|
|
728
1322
|
}
|
|
729
1323
|
for (const item of plan.steps) {
|
|
730
1324
|
lines.push(`-- class: ${item.class}`);
|
|
1325
|
+
if (item.kind !== undefined)
|
|
1326
|
+
lines.push(`-- kind: ${item.kind}`);
|
|
731
1327
|
lines.push(`-- action: ${item.action}`);
|
|
732
|
-
lines.push(`-- lock: ${item.lock}`);
|
|
1328
|
+
lines.push(`-- lock: ${lockText === undefined ? item.lock : lockText(item)}`);
|
|
1329
|
+
if (item.backfill !== undefined)
|
|
1330
|
+
lines.push(formatBackfillHeader(item.backfill));
|
|
733
1331
|
if (!item.transactional)
|
|
734
1332
|
lines.push("-- transactional: false");
|
|
735
1333
|
if (item.path !== undefined)
|
|
736
1334
|
lines.push(`-- path: ${item.path}`);
|
|
737
1335
|
if (item.behavior === "change")
|
|
738
1336
|
lines.push("-- behavior: change");
|
|
1337
|
+
for (const allow of item.allows ?? []) {
|
|
1338
|
+
lines.push(`-- okm-allow ${allow.code}: ${allow.reason}`);
|
|
1339
|
+
}
|
|
739
1340
|
lines.push(`${item.sql};`);
|
|
740
1341
|
lines.push("");
|
|
741
1342
|
}
|
|
@@ -765,46 +1366,71 @@ function parsePlan(text) {
|
|
|
765
1366
|
if (lines[index] === "-- no steps")
|
|
766
1367
|
break;
|
|
767
1368
|
let stepClass = "expand";
|
|
1369
|
+
let sawClass = false;
|
|
1370
|
+
let stepKind;
|
|
768
1371
|
let action = "ddl";
|
|
769
1372
|
let lock = "";
|
|
770
1373
|
let transactional = true;
|
|
1374
|
+
let backfill;
|
|
771
1375
|
let path;
|
|
772
1376
|
let behavior;
|
|
1377
|
+
const allows = [];
|
|
773
1378
|
const sql = [];
|
|
774
1379
|
while (index < lines.length && (lines[index] ?? "") !== "") {
|
|
775
1380
|
const line = lines[index] ?? "";
|
|
776
1381
|
index += 1;
|
|
777
|
-
if (line.startsWith("-- class: "))
|
|
1382
|
+
if (line.startsWith("-- class: ")) {
|
|
778
1383
|
stepClass = readClass(line.slice("-- class: ".length));
|
|
779
|
-
|
|
1384
|
+
sawClass = true;
|
|
1385
|
+
} else if (line.startsWith("-- kind: ")) {
|
|
1386
|
+
const kind = line.slice("-- kind: ".length);
|
|
1387
|
+
if (isStepKind(kind))
|
|
1388
|
+
stepKind = kind;
|
|
1389
|
+
} else if (line.startsWith("-- action: "))
|
|
780
1390
|
action = line.endsWith("backfill") ? "backfill" : "ddl";
|
|
781
1391
|
else if (line.startsWith("-- lock: "))
|
|
782
1392
|
lock = line.slice("-- lock: ".length);
|
|
1393
|
+
else if (line.startsWith("-- backfill "))
|
|
1394
|
+
backfill = parseBackfillHeader(line);
|
|
783
1395
|
else if (line === "-- transactional: false")
|
|
784
1396
|
transactional = false;
|
|
785
1397
|
else if (line === "-- path: unverified")
|
|
786
1398
|
path = "unverified";
|
|
787
1399
|
else if (line === "-- behavior: change")
|
|
788
1400
|
behavior = "change";
|
|
1401
|
+
else if (line.startsWith("-- okm-allow"))
|
|
1402
|
+
allows.push(readAllow(line));
|
|
789
1403
|
else if (!line.startsWith("--"))
|
|
790
1404
|
sql.push(line);
|
|
791
1405
|
}
|
|
792
1406
|
const statement = sql.join(`
|
|
793
1407
|
`).replace(/;\s*$/, "");
|
|
794
1408
|
if (statement.length > 0) {
|
|
1409
|
+
const kind = sawClass ? stepKind : "raw-sql";
|
|
1410
|
+
const classification = sawClass ? stepClass : "unclassified";
|
|
795
1411
|
steps.push({
|
|
796
1412
|
sql: statement,
|
|
797
|
-
class:
|
|
1413
|
+
class: classification,
|
|
1414
|
+
...kind !== undefined ? { kind } : {},
|
|
798
1415
|
action,
|
|
799
1416
|
lock,
|
|
800
1417
|
transactional,
|
|
1418
|
+
...backfill !== undefined ? { backfill } : {},
|
|
801
1419
|
...path !== undefined ? { path } : {},
|
|
802
|
-
...behavior !== undefined ? { behavior } : {}
|
|
1420
|
+
...behavior !== undefined ? { behavior } : {},
|
|
1421
|
+
...allows.length > 0 ? { allows } : {}
|
|
803
1422
|
});
|
|
804
1423
|
}
|
|
805
1424
|
}
|
|
806
1425
|
return { name, class: steps.length === 0 ? planClass : overall(steps), steps };
|
|
807
1426
|
}
|
|
1427
|
+
var ALLOW = /^-- okm-allow\s+(OKM\d+)\s*(?::\s*(.*))?$/;
|
|
1428
|
+
function readAllow(line) {
|
|
1429
|
+
const match = ALLOW.exec(line);
|
|
1430
|
+
if (match === null)
|
|
1431
|
+
return { code: "", reason: "" };
|
|
1432
|
+
return { code: match[1] ?? "", reason: (match[2] ?? "").trim() };
|
|
1433
|
+
}
|
|
808
1434
|
function readClass(value) {
|
|
809
1435
|
if (value === "contract" || value === "unclassified")
|
|
810
1436
|
return value;
|
|
@@ -856,7 +1482,8 @@ function renameSteps(source, renames, schema) {
|
|
|
856
1482
|
if (rename.kind !== "table" || !hasTable(source, rename.from))
|
|
857
1483
|
continue;
|
|
858
1484
|
tables.set(rename.to, rename.from);
|
|
859
|
-
steps.push(step(`alter table ${qualify(schema, rename.from)} rename to ${quoteIdent(rename.to)}`, "
|
|
1485
|
+
steps.push(step(`alter table ${qualify(schema, rename.from)} rename to ${quoteIdent(rename.to)}`, "rename-table", "ddl", ACCESS2));
|
|
1486
|
+
steps.push(...sequenceRenames(source, rename, schema));
|
|
860
1487
|
}
|
|
861
1488
|
for (const rename of renames) {
|
|
862
1489
|
if (rename.kind !== "column")
|
|
@@ -864,7 +1491,25 @@ function renameSteps(source, renames, schema) {
|
|
|
864
1491
|
const previousTable = tables.get(rename.table) ?? rename.table;
|
|
865
1492
|
if (!hasColumn(source, previousTable, rename.from))
|
|
866
1493
|
continue;
|
|
867
|
-
steps.push(step(`alter table ${qualify(schema, rename.table)} rename column ${quoteIdent(rename.from)} to ${quoteIdent(rename.to)}`, "
|
|
1494
|
+
steps.push(step(`alter table ${qualify(schema, rename.table)} rename column ${quoteIdent(rename.from)} to ${quoteIdent(rename.to)}`, "rename-column", "ddl", ACCESS2));
|
|
1495
|
+
}
|
|
1496
|
+
return steps;
|
|
1497
|
+
}
|
|
1498
|
+
function sequenceRenames(source, rename, schema) {
|
|
1499
|
+
const steps = [];
|
|
1500
|
+
for (const object of source.objects) {
|
|
1501
|
+
if (object.kind !== "column" || object.definition.identity === undefined)
|
|
1502
|
+
continue;
|
|
1503
|
+
if (object.identity.parent.name !== rename.from)
|
|
1504
|
+
continue;
|
|
1505
|
+
const fromName = fitIdentifier2(`${rename.from}_${object.identity.name}_seq`);
|
|
1506
|
+
const toName = fitIdentifier2(`${rename.to}_${object.identity.name}_seq`);
|
|
1507
|
+
if (fromName === toName)
|
|
1508
|
+
continue;
|
|
1509
|
+
const owned = source.objects.some((item) => item.kind === "sequence" && item.identity.name === fromName);
|
|
1510
|
+
if (!owned)
|
|
1511
|
+
continue;
|
|
1512
|
+
steps.push(step(`alter sequence ${qualify(schema, fromName)} rename to ${quoteIdent(toName)}`, "rename-sequence", "ddl", ACCESS2));
|
|
868
1513
|
}
|
|
869
1514
|
return steps;
|
|
870
1515
|
}
|
|
@@ -913,12 +1558,14 @@ function withoutNameKey(value) {
|
|
|
913
1558
|
}
|
|
914
1559
|
function renameShapeStep(pair, schema) {
|
|
915
1560
|
if (pair.from.kind === "index" && pair.to.kind === "index") {
|
|
916
|
-
return step(`alter index ${qualify(schema, pair.from.identity.name)} rename to ${quoteIdent(pair.to.identity.name)}`, "
|
|
1561
|
+
return step(`alter index ${qualify(schema, pair.from.identity.name)} rename to ${quoteIdent(pair.to.identity.name)}`, "rename-index", "ddl", ACCESS2);
|
|
917
1562
|
}
|
|
918
1563
|
if (pair.from.kind !== "constraint" || pair.to.kind !== "constraint") {
|
|
919
|
-
|
|
1564
|
+
throw new OkmError2("invalid", "A same-shape rename is an index or a constraint.", {
|
|
1565
|
+
fix: { summary: "The planner only renames an index or a constraint in place." }
|
|
1566
|
+
});
|
|
920
1567
|
}
|
|
921
|
-
return step(`alter table ${qualify(schema, pair.from.identity.parent.name)} rename constraint ${quoteIdent(pair.from.identity.name)} to ${quoteIdent(pair.to.identity.name)}`, "
|
|
1568
|
+
return step(`alter table ${qualify(schema, pair.from.identity.parent.name)} rename constraint ${quoteIdent(pair.from.identity.name)} to ${quoteIdent(pair.to.identity.name)}`, "rename-constraint", "ddl", ACCESS2);
|
|
922
1569
|
}
|
|
923
1570
|
function assertUnambiguous(beforeBy, afterBy) {
|
|
924
1571
|
const dropped = new Map;
|
|
@@ -1092,24 +1739,25 @@ function enumAddSteps(enums) {
|
|
|
1092
1739
|
if (change.kind !== "add")
|
|
1093
1740
|
continue;
|
|
1094
1741
|
for (const sql of change.statements) {
|
|
1095
|
-
steps.push(step(sql, "
|
|
1742
|
+
steps.push(step(sql, "add-enum-value", "ddl", ACCESS2, false));
|
|
1096
1743
|
}
|
|
1097
1744
|
}
|
|
1098
1745
|
return steps;
|
|
1099
1746
|
}
|
|
1100
|
-
function expandBackfills(picklists, enums, replacements, schema) {
|
|
1101
|
-
return dataSteps(picklists, enums, replacements, schema, "expand");
|
|
1747
|
+
function expandBackfills(picklists, enums, replacements, schema, catalogs, batchSize) {
|
|
1748
|
+
return dataSteps(picklists, enums, replacements, schema, catalogs, batchSize, "expand");
|
|
1102
1749
|
}
|
|
1103
|
-
function contractSwaps(picklists, enums, replacements, schema) {
|
|
1750
|
+
function contractSwaps(picklists, enums, replacements, schema, catalogs, batchSize) {
|
|
1104
1751
|
const steps = [];
|
|
1105
|
-
steps.push(...dataSteps(picklists, enums, replacements, schema, "contract"));
|
|
1752
|
+
steps.push(...dataSteps(picklists, enums, replacements, schema, catalogs, batchSize, "contract"));
|
|
1106
1753
|
for (const change of picklists) {
|
|
1107
1754
|
const table = qualify(schema, change.after.identity.parent.name);
|
|
1108
1755
|
const name = quoteIdent(change.after.identity.name);
|
|
1109
|
-
|
|
1756
|
+
const kind = change.removed.length > 0 ? "narrow-check" : "widen-check";
|
|
1757
|
+
steps.push(step(`alter table ${table} drop constraint ${name}`, kind, "ddl", ACCESS2));
|
|
1110
1758
|
const expression = change.after.definition.expression ?? "true";
|
|
1111
|
-
steps.push(step(`alter table ${table} add constraint ${name} check (${expression}) not valid`,
|
|
1112
|
-
steps.push(step(`alter table ${table} validate constraint ${name}`,
|
|
1759
|
+
steps.push(step(`alter table ${table} add constraint ${name} check (${expression}) not valid`, kind, "ddl", ACCESS2));
|
|
1760
|
+
steps.push(step(`alter table ${table} validate constraint ${name}`, kind, "ddl", SHARE_UPDATE2));
|
|
1113
1761
|
}
|
|
1114
1762
|
for (const change of enums) {
|
|
1115
1763
|
if (change.kind !== "replace")
|
|
@@ -1117,19 +1765,20 @@ function contractSwaps(picklists, enums, replacements, schema) {
|
|
|
1117
1765
|
const type = qualify(schema, change.name);
|
|
1118
1766
|
const old = quoteIdent(`${change.name}_old`);
|
|
1119
1767
|
const labels = change.labels.map((label) => quoteLiteral(label)).join(", ");
|
|
1120
|
-
steps.push(step(`alter type ${type} rename to ${old}`, "
|
|
1121
|
-
steps.push(step(`create type ${type} as enum (${labels})`, "
|
|
1768
|
+
steps.push(step(`alter type ${type} rename to ${old}`, "rename-enum", "ddl", ACCESS2));
|
|
1769
|
+
steps.push(step(`create type ${type} as enum (${labels})`, "recreate-enum", "ddl", ACCESS2));
|
|
1122
1770
|
for (const column of change.columns) {
|
|
1123
1771
|
const table = qualify(schema, column.table);
|
|
1124
1772
|
const name = quoteIdent(column.column);
|
|
1125
|
-
steps.push(step(`alter table ${table} alter column ${name} type ${type} using ${name}::text::${type}`, "
|
|
1773
|
+
steps.push(step(`alter table ${table} alter column ${name} type ${type} using ${name}::text::${type}`, "set-enum-column", "ddl", ACCESS2));
|
|
1126
1774
|
}
|
|
1127
|
-
steps.push(step(`drop type ${qualify(schema, `${change.name}_old`)}`, "
|
|
1775
|
+
steps.push(step(`drop type ${qualify(schema, `${change.name}_old`)}`, "drop-enum", "ddl", ACCESS2));
|
|
1128
1776
|
}
|
|
1129
1777
|
return steps;
|
|
1130
1778
|
}
|
|
1131
|
-
function dataSteps(picklists, enums, replacements, schema, phase) {
|
|
1779
|
+
function dataSteps(picklists, enums, replacements, schema, catalogs, batchSize, phase) {
|
|
1132
1780
|
const steps = [];
|
|
1781
|
+
const kind = phase === "expand" ? "backfill-expand" : "backfill-contract";
|
|
1133
1782
|
for (const change of picklists) {
|
|
1134
1783
|
if (change.removed.length === 0)
|
|
1135
1784
|
continue;
|
|
@@ -1138,7 +1787,7 @@ function dataSteps(picklists, enums, replacements, schema, phase) {
|
|
|
1138
1787
|
const replacement = findReplacement(replacements, table, change.column, value);
|
|
1139
1788
|
if (replacement === undefined)
|
|
1140
1789
|
continue;
|
|
1141
|
-
steps.push(
|
|
1790
|
+
steps.push(backfillStep(updateSql(schema, table, change.column, value, replacement.to, catalogs, batchSize), kind));
|
|
1142
1791
|
}
|
|
1143
1792
|
}
|
|
1144
1793
|
for (const change of enums) {
|
|
@@ -1151,7 +1800,7 @@ function dataSteps(picklists, enums, replacements, schema, phase) {
|
|
|
1151
1800
|
const replacement = findReplacement(replacements, column.table, column.column, value);
|
|
1152
1801
|
if (replacement === undefined)
|
|
1153
1802
|
continue;
|
|
1154
|
-
steps.push(
|
|
1803
|
+
steps.push(backfillStep(updateSql(schema, column.table, column.column, value, replacement.to, catalogs, batchSize, true), kind));
|
|
1155
1804
|
}
|
|
1156
1805
|
}
|
|
1157
1806
|
}
|
|
@@ -1271,10 +1920,10 @@ function domainCheckSteps(beforeBy, afterBy, schema) {
|
|
|
1271
1920
|
const current = quoteIdent(fitIdentifier2(`${name}_check`));
|
|
1272
1921
|
const upcoming = quoteIdent(fitIdentifier2(`${name}_check_next`));
|
|
1273
1922
|
const expression = next.definition.check;
|
|
1274
|
-
steps.push(step(`alter domain ${type} add constraint ${upcoming} check (${expression}) not valid`, "
|
|
1275
|
-
steps.push(step(`alter domain ${type} validate constraint ${upcoming}`, "
|
|
1276
|
-
steps.push(step(`alter domain ${type} drop constraint ${current}`, "
|
|
1277
|
-
steps.push(step(`alter domain ${type} rename constraint ${upcoming} to ${current}`, "
|
|
1923
|
+
steps.push(step(`alter domain ${type} add constraint ${upcoming} check (${expression}) not valid`, "add-domain-check", "ddl", ACCESS2));
|
|
1924
|
+
steps.push(step(`alter domain ${type} validate constraint ${upcoming}`, "validate-domain-check", "ddl", ACCESS2));
|
|
1925
|
+
steps.push(step(`alter domain ${type} drop constraint ${current}`, "drop-domain-check", "ddl", ACCESS2));
|
|
1926
|
+
steps.push(step(`alter domain ${type} rename constraint ${upcoming} to ${current}`, "rename-domain-check", "ddl", ACCESS2));
|
|
1278
1927
|
}
|
|
1279
1928
|
return steps;
|
|
1280
1929
|
}
|
|
@@ -1448,6 +2097,40 @@ function omitOwnedSequences(keys, by, source) {
|
|
|
1448
2097
|
keys.delete(key);
|
|
1449
2098
|
}
|
|
1450
2099
|
}
|
|
2100
|
+
var TRAILING_CAST = /^(.*)::((?:timestamp|time)(?:\(\d+\))? (?:with|without) time zone|character varying(?:\(\d+\))?|[A-Za-z_][\w$]*(?:\([^)]*\))?)$/;
|
|
2101
|
+
function canonicalDefault(expression, dataType) {
|
|
2102
|
+
if (expression === undefined)
|
|
2103
|
+
return {};
|
|
2104
|
+
const match = TRAILING_CAST.exec(expression.trim());
|
|
2105
|
+
const body = match?.[1]?.trim();
|
|
2106
|
+
const cast = match?.[2];
|
|
2107
|
+
if (body === undefined || body.length === 0 || cast === undefined)
|
|
2108
|
+
return { defaultExpression: expression };
|
|
2109
|
+
if (!defaultCastMatches(cast, dataType))
|
|
2110
|
+
return { defaultExpression: expression };
|
|
2111
|
+
return { defaultExpression: body };
|
|
2112
|
+
}
|
|
2113
|
+
function defaultCastMatches(cast, dataType) {
|
|
2114
|
+
if (canonicalTypeName(cast) === canonicalTypeName(dataType))
|
|
2115
|
+
return true;
|
|
2116
|
+
const castBase = reprintedDefaultBase(cast);
|
|
2117
|
+
const columnBase = reprintedDefaultBase(dataType);
|
|
2118
|
+
return castBase !== undefined && castBase === columnBase;
|
|
2119
|
+
}
|
|
2120
|
+
function reprintedDefaultBase(typeName) {
|
|
2121
|
+
const raw = typeName.trim();
|
|
2122
|
+
if (/^(?:bpchar|character|char)(?:\(\d+\))?$/.test(raw))
|
|
2123
|
+
return "character";
|
|
2124
|
+
const canonical = canonicalTypeName(raw);
|
|
2125
|
+
if (/^character varying(?:\(\d+\))?$/.test(canonical))
|
|
2126
|
+
return "character varying";
|
|
2127
|
+
if (/^character(?:\(\d+\))?$/.test(canonical))
|
|
2128
|
+
return "character";
|
|
2129
|
+
const time = /^(timestamp|time)(?:\(\d+\))? (with time zone|without time zone)$/.exec(canonical);
|
|
2130
|
+
if (time !== null)
|
|
2131
|
+
return `${time[1] ?? ""} ${time[2] ?? ""}`;
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
1451
2134
|
function sameDefinition(left, right) {
|
|
1452
2135
|
if (left.kind === "materializedView" && right.kind === "materializedView") {
|
|
1453
2136
|
return stable(canonicalColumns(left.definition.columns)) === stable(canonicalColumns(right.definition.columns)) && left.definition.query === right.definition.query;
|
|
@@ -1457,6 +2140,11 @@ function sameDefinition(left, right) {
|
|
|
1457
2140
|
function forComparison(object) {
|
|
1458
2141
|
switch (object.kind) {
|
|
1459
2142
|
case "column":
|
|
2143
|
+
return {
|
|
2144
|
+
...object.definition,
|
|
2145
|
+
dataType: canonicalTypeName(object.definition.dataType),
|
|
2146
|
+
...canonicalDefault(object.definition.defaultExpression, object.definition.dataType)
|
|
2147
|
+
};
|
|
1460
2148
|
case "sequence":
|
|
1461
2149
|
return { ...object.definition, dataType: canonicalTypeName(object.definition.dataType) };
|
|
1462
2150
|
case "view":
|
|
@@ -1528,22 +2216,30 @@ function anchoredParent(object) {
|
|
|
1528
2216
|
}
|
|
1529
2217
|
return;
|
|
1530
2218
|
}
|
|
1531
|
-
function alterClass(sql) {
|
|
1532
|
-
if (sql.includes("set data type") || sql.includes("set not null"))
|
|
1533
|
-
return "contract";
|
|
1534
|
-
return "expand";
|
|
1535
|
-
}
|
|
1536
2219
|
function overall(steps) {
|
|
1537
|
-
|
|
1538
|
-
return "contract";
|
|
1539
|
-
if (steps.some((item) => item.class === "unclassified"))
|
|
1540
|
-
return "unclassified";
|
|
1541
|
-
return "expand";
|
|
2220
|
+
return strictestClass(steps.map((item) => item.class));
|
|
1542
2221
|
}
|
|
1543
|
-
function updateSql(schema, table, column, from, to, cast = false) {
|
|
2222
|
+
function updateSql(schema, table, column, from, to, catalogs, batchSize, cast = false) {
|
|
2223
|
+
const key = primaryKeyColumns(catalogs, table);
|
|
2224
|
+
const qualified = qualify(schema, table);
|
|
1544
2225
|
const value = to === null ? "null" : quoteLiteral(to);
|
|
1545
2226
|
const compare = cast ? `${quoteIdent(column)}::text` : quoteIdent(column);
|
|
1546
|
-
|
|
2227
|
+
const where = `${compare} = ${quoteLiteral(from)}`;
|
|
2228
|
+
const backfill = {
|
|
2229
|
+
table: qualified,
|
|
2230
|
+
key: key.map((item) => item.quoted),
|
|
2231
|
+
batch: batchSize
|
|
2232
|
+
};
|
|
2233
|
+
return {
|
|
2234
|
+
sql: `update ${qualified} set ${quoteIdent(column)} = ${value} where ${where} and ${keyRangePredicate(key)}`,
|
|
2235
|
+
backfill
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
function backfillStep(planned, kind) {
|
|
2239
|
+
return {
|
|
2240
|
+
...step(planned.sql, kind, "backfill", ROW2, false),
|
|
2241
|
+
backfill: planned.backfill
|
|
2242
|
+
};
|
|
1547
2243
|
}
|
|
1548
2244
|
function hasTable(source, name) {
|
|
1549
2245
|
return source.objects.some((object) => object.kind === "table" && object.identity.name === name);
|
|
@@ -1554,8 +2250,167 @@ function hasColumn(source, table, name) {
|
|
|
1554
2250
|
function index2(source) {
|
|
1555
2251
|
return source.objects.map((object) => ({ key: identityKey2(object.identity), object }));
|
|
1556
2252
|
}
|
|
1557
|
-
function step(sql,
|
|
1558
|
-
|
|
2253
|
+
function step(sql, kind, action, lock, transactional = true, extraTables) {
|
|
2254
|
+
const tables = touchedTables(sql, lock, extraTables);
|
|
2255
|
+
return {
|
|
2256
|
+
sql,
|
|
2257
|
+
kind,
|
|
2258
|
+
class: classOf(kind),
|
|
2259
|
+
action,
|
|
2260
|
+
lock,
|
|
2261
|
+
transactional,
|
|
2262
|
+
...tables.length > 0 ? { tables } : {}
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
function pushSafe(steps, safe) {
|
|
2266
|
+
for (const item of safe) {
|
|
2267
|
+
steps.push({
|
|
2268
|
+
...step(item.sql, item.kind, item.action, item.lock, item.transactional),
|
|
2269
|
+
safeRewrite: true,
|
|
2270
|
+
...item.backfill !== undefined ? { backfill: item.backfill } : {}
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
var TABLE_PREFIX = /\b(?:create|alter|drop)\s+table(?:\s+if\s+exists)?\s+|\bupdate\s+|\binsert\s+into\s+|\breferences\s+|\bon\s+table\s+|\bon\s+/gi;
|
|
2275
|
+
function touchedTables(sql, lock, extra) {
|
|
2276
|
+
const names = [];
|
|
2277
|
+
const text = `${sql}
|
|
2278
|
+
${lock}`;
|
|
2279
|
+
TABLE_PREFIX.lastIndex = 0;
|
|
2280
|
+
for (let match = TABLE_PREFIX.exec(text);match !== null; match = TABLE_PREFIX.exec(text)) {
|
|
2281
|
+
const found = readQualified(text, match.index + match[0].length);
|
|
2282
|
+
if (found !== undefined)
|
|
2283
|
+
pushTable(names, found.name);
|
|
2284
|
+
}
|
|
2285
|
+
for (const name of extra ?? [])
|
|
2286
|
+
pushTable(names, name);
|
|
2287
|
+
return names;
|
|
2288
|
+
}
|
|
2289
|
+
function pushTable(names, name) {
|
|
2290
|
+
if (!names.includes(name))
|
|
2291
|
+
names.push(name);
|
|
2292
|
+
}
|
|
2293
|
+
function readQualified(text, start) {
|
|
2294
|
+
const schema = readIdent(text, start);
|
|
2295
|
+
if (schema === undefined)
|
|
2296
|
+
return;
|
|
2297
|
+
if (text[schema.end] !== "." || text[schema.end + 1] !== '"')
|
|
2298
|
+
return;
|
|
2299
|
+
const name = readIdent(text, schema.end + 1);
|
|
2300
|
+
if (name === undefined || text[name.end] === ".")
|
|
2301
|
+
return;
|
|
2302
|
+
return name;
|
|
2303
|
+
}
|
|
2304
|
+
function readIdent(text, start) {
|
|
2305
|
+
if (text[start] !== '"')
|
|
2306
|
+
return;
|
|
2307
|
+
let value = "";
|
|
2308
|
+
let index = start + 1;
|
|
2309
|
+
while (index < text.length) {
|
|
2310
|
+
const char = text[index];
|
|
2311
|
+
if (char === '"') {
|
|
2312
|
+
if (text[index + 1] === '"') {
|
|
2313
|
+
value += '"';
|
|
2314
|
+
index += 2;
|
|
2315
|
+
continue;
|
|
2316
|
+
}
|
|
2317
|
+
return { name: value, end: index + 1 };
|
|
2318
|
+
}
|
|
2319
|
+
value += char ?? "";
|
|
2320
|
+
index += 1;
|
|
2321
|
+
}
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
function pushDrop(steps, object, schema) {
|
|
2325
|
+
const sql = dropObjectSql(object, schema);
|
|
2326
|
+
const kind = dropKind(object);
|
|
2327
|
+
if (sql === undefined || kind === undefined)
|
|
2328
|
+
return;
|
|
2329
|
+
steps.push(step(sql, kind, "ddl", ACCESS2));
|
|
2330
|
+
}
|
|
2331
|
+
function createKind(object, tableIsNew) {
|
|
2332
|
+
switch (object.kind) {
|
|
2333
|
+
case "table":
|
|
2334
|
+
return "create-table";
|
|
2335
|
+
case "column":
|
|
2336
|
+
return requiredColumn(object) && !tableIsNew ? "add-column-required" : "add-column";
|
|
2337
|
+
case "index":
|
|
2338
|
+
return "create-index";
|
|
2339
|
+
case "constraint":
|
|
2340
|
+
return "add-constraint";
|
|
2341
|
+
case "sequence":
|
|
2342
|
+
return "create-sequence";
|
|
2343
|
+
case "type":
|
|
2344
|
+
return isDomain(object.definition) ? "create-domain" : "create-enum";
|
|
2345
|
+
case "extension":
|
|
2346
|
+
return "create-extension";
|
|
2347
|
+
case "function":
|
|
2348
|
+
return "create-function";
|
|
2349
|
+
case "trigger":
|
|
2350
|
+
return "create-trigger";
|
|
2351
|
+
case "view":
|
|
2352
|
+
return "create-view";
|
|
2353
|
+
case "materializedView":
|
|
2354
|
+
return "create-matview";
|
|
2355
|
+
default:
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
function dropKind(object) {
|
|
2360
|
+
switch (object.kind) {
|
|
2361
|
+
case "table":
|
|
2362
|
+
return "drop-table";
|
|
2363
|
+
case "column":
|
|
2364
|
+
return "drop-column";
|
|
2365
|
+
case "index":
|
|
2366
|
+
return "drop-index";
|
|
2367
|
+
case "constraint":
|
|
2368
|
+
return "drop-constraint";
|
|
2369
|
+
case "sequence":
|
|
2370
|
+
return "drop-sequence";
|
|
2371
|
+
case "type":
|
|
2372
|
+
return isDomain(object.definition) ? "drop-domain" : "drop-enum";
|
|
2373
|
+
case "extension":
|
|
2374
|
+
return "drop-extension";
|
|
2375
|
+
case "function":
|
|
2376
|
+
return "drop-function";
|
|
2377
|
+
case "trigger":
|
|
2378
|
+
return "drop-trigger";
|
|
2379
|
+
case "view":
|
|
2380
|
+
return "drop-view";
|
|
2381
|
+
case "materializedView":
|
|
2382
|
+
return "drop-matview";
|
|
2383
|
+
default:
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
function requiredColumn(column) {
|
|
2388
|
+
const definition = column.definition;
|
|
2389
|
+
return !definition.nullable && definition.defaultExpression === undefined && definition.identity === undefined && definition.generated === undefined;
|
|
2390
|
+
}
|
|
2391
|
+
function columnAlterKinds(before, after) {
|
|
2392
|
+
const kinds = [];
|
|
2393
|
+
if (before.definition.dataType !== after.definition.dataType || before.definition.collation !== after.definition.collation) {
|
|
2394
|
+
kinds.push("set-column-type");
|
|
2395
|
+
}
|
|
2396
|
+
if (before.definition.nullable !== after.definition.nullable) {
|
|
2397
|
+
kinds.push(after.definition.nullable ? "drop-not-null" : "set-not-null");
|
|
2398
|
+
}
|
|
2399
|
+
if (before.definition.defaultExpression !== after.definition.defaultExpression) {
|
|
2400
|
+
kinds.push(after.definition.defaultExpression === undefined ? "drop-default" : "set-default");
|
|
2401
|
+
}
|
|
2402
|
+
return kinds;
|
|
2403
|
+
}
|
|
2404
|
+
function identityKind(before, after) {
|
|
2405
|
+
if (stable(before.definition.identity) === stable(after.definition.identity))
|
|
2406
|
+
return;
|
|
2407
|
+
const next = after.definition.identity;
|
|
2408
|
+
const previous = before.definition.identity;
|
|
2409
|
+
if (next === undefined)
|
|
2410
|
+
return "drop-identity";
|
|
2411
|
+
if (previous === undefined)
|
|
2412
|
+
return "add-identity";
|
|
2413
|
+
return next.always ? "set-identity-always" : "set-identity-by-default";
|
|
1559
2414
|
}
|
|
1560
2415
|
function stable(value) {
|
|
1561
2416
|
return JSON.stringify(order(value));
|
|
@@ -1573,4 +2428,4 @@ function order(value) {
|
|
|
1573
2428
|
return value;
|
|
1574
2429
|
}
|
|
1575
2430
|
|
|
1576
|
-
export { createdRoleName, changesRole, assertExtensionsAvailable, omitManagedObjects, parseReplace, planMigration, formatPlan, parsePlan, staleRenames };
|
|
2431
|
+
export { canonicalTypeName, createdRoleName, changesRole, DEFAULT_BACKFILL_PAUSE_MS, DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS, batchSizeField, aboutBatchCount, boundaryQuery, checkedCast, classOf, strictestClass, assertExtensionsAvailable, omitManagedObjects, parseReplace, planMigration, formatPlan, parsePlan, staleRenames };
|