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