okmodel 0.4.0 → 0.5.1
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 +95 -12
- package/dist/contracts/catalog/document.d.ts +20 -0
- package/dist/contracts/driver.d.ts +7 -0
- package/dist/dialects/pg/filter-parse.d.ts +8 -2
- package/dist/dialects/pg/fn/index.js +8 -0
- package/dist/dialects/pg/index.js +8 -2
- package/dist/dialects/pg/introspect.d.ts +6 -0
- package/dist/dialects/pg/ops/or.d.ts +1 -1
- package/dist/dialects/pg/role/sql.d.ts +10 -0
- package/dist/dialects/pg/schema.d.ts +15 -6
- package/dist/dialects/pg/temporal.d.ts +5 -76
- 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 -2
- package/dist/runtime/client.d.ts +6 -1
- package/dist/runtime/conflict.d.ts +2 -1
- package/dist/runtime/pg/bun.d.ts +12 -7
- package/dist/runtime/pg/bun.js +7 -4
- package/dist/runtime/pg/pg.d.ts +12 -6
- package/dist/runtime/pg/pg.js +7 -4
- package/dist/runtime/pg/pglite.d.ts +11 -3
- package/dist/runtime/pg/pglite.js +7 -4
- package/dist/runtime/pg/postgresjs.d.ts +12 -6
- package/dist/runtime/pg/postgresjs.js +7 -4
- package/dist/runtime/plan.d.ts +16 -1
- package/dist/runtime/tenancy/index.js +1 -1
- package/dist/runtime/topology.d.ts +191 -0
- package/dist/runtime/traits/index.js +7 -7
- package/dist/runtime/types.d.ts +57 -2
- package/dist/runtime/validate/index.js +3 -3
- package/dist/shared/{radd3z4g.js → 0t22wwh1.js} +5 -9
- package/dist/shared/{ady1ms74.js → 2pqx26h8.js} +25 -10
- package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
- package/dist/shared/{jns2wx4t.js → 32zqc1dw.js} +18 -7
- package/dist/shared/{nxb42q7h.js → 47x8x9ze.js} +2 -2
- package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
- package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
- package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
- package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
- package/dist/shared/{6693j9rs.js → 5fqx9yh4.js} +6 -2
- package/dist/shared/{c5g81ngb.js → 6mnp99hx.js} +11 -8
- package/dist/shared/{hqjsxnz7.js → 6qd27mx0.js} +5 -4
- package/dist/shared/{v7tpnhnz.js → 6rk2gen5.js} +5 -3
- package/dist/shared/{2321avmn.js → 7zwwyh0v.js} +46 -16
- package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
- package/dist/shared/{8jb4ymh2.js → 8bwbwy8h.js} +12 -1
- package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
- package/dist/shared/{41kv2xdm.js → 8xsbagad.js} +1 -1
- package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
- package/dist/shared/{jgxzjzcz.js → aj2qxyhj.js} +2 -2
- package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
- package/dist/shared/{s4mb6a4b.js → cctw17hj.js} +74 -15
- package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
- package/dist/shared/{axhk06q1.js → cr74e96e.js} +1 -1
- package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
- package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
- package/dist/shared/{xghkt7a9.js → fancm5pt.js} +6 -233
- package/dist/shared/{n5y9zyj1.js → ga3sfy93.js} +369 -14
- package/dist/shared/{xrfnq5k8.js → gbtcc2d7.js} +55 -5
- package/dist/shared/{7nk68fj8.js → gde73es6.js} +20 -15
- package/dist/shared/{kkzwp3d2.js → hf22e0wt.js} +55 -19
- package/dist/shared/{tny2t3sm.js → janx09m5.js} +8 -7
- package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
- package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
- package/dist/shared/{qn0srwwq.js → kfjp0gck.js} +5 -3
- package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
- package/dist/shared/{edwqqryd.js → nkaxvt9q.js} +1 -1
- package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
- package/dist/shared/nxdp2ze3.js +1197 -0
- package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
- package/dist/shared/{84ptn35q.js → ps7a6h14.js} +10 -9
- package/dist/shared/{kj680nxv.js → q58y17hy.js} +9 -7
- package/dist/shared/{awrsqrn7.js → qm7cdtvr.js} +22 -9
- package/dist/shared/{q6zsyh31.js → qv2ydck9.js} +156 -205
- package/dist/shared/{kafaa5zv.js → r2sp8hrc.js} +28 -8
- package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
- package/dist/shared/{yjw02enp.js → sdc7r2ww.js} +8 -7
- package/dist/shared/sycnx8gt.js +289 -0
- package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
- package/dist/shared/{zg24x0hm.js → tm5ab8we.js} +10 -2
- package/dist/shared/{ygem90rn.js → v36c5g6p.js} +85 -21
- package/dist/shared/wq9n6pwr.js +139 -0
- package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
- package/dist/shared/{q795afwa.js → z2rnngj4.js} +3 -3
- package/dist/shared/{sr5ms7b3.js → zqrnhwsq.js} +3 -3
- package/dist/tooling/cli.js +70 -23
- package/dist/tooling/migrate/apply.d.ts +2 -0
- package/dist/tooling/migrate/commands.d.ts +1 -2
- package/dist/tooling/migrate/drift.d.ts +1 -1
- package/dist/tooling/migrate/estimate.d.ts +14 -1
- package/dist/tooling/migrate/files.d.ts +7 -0
- package/dist/tooling/migrate/index.js +12 -9
- package/dist/tooling/migrate/lint.d.ts +3 -3
- package/dist/tooling/migrate/plan.d.ts +7 -3
- package/dist/tooling/migrate/policy.d.ts +11 -0
- package/dist/tooling/migrate/sql-lex.d.ts +22 -0
- package/dist/tooling/testing/factories.d.ts +10 -0
- package/dist/tooling/testing/facts.d.ts +3 -0
- package/dist/tooling/testing/index.js +3 -3
- package/dist/tooling/testing/isolation.d.ts +4 -3
- package/package.json +6 -5
- package/dist/shared/0v1g424p.js +0 -26
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
noticeBuffer,
|
|
4
4
|
WireSession,
|
|
5
5
|
cursorStream
|
|
6
|
-
} from "./
|
|
6
|
+
} from "./4az561fn.js";
|
|
7
7
|
import {
|
|
8
8
|
NODE_POSTGRES_CAPABILITIES,
|
|
9
9
|
driverErrors,
|
|
10
10
|
rejectClosed,
|
|
11
11
|
mapFailure,
|
|
12
12
|
runCall
|
|
13
|
-
} from "./
|
|
13
|
+
} from "./cgbamx3e.js";
|
|
14
14
|
|
|
15
15
|
// src/adapters/pg/nodepostgres.ts
|
|
16
16
|
import net from "node:net";
|
|
@@ -53,7 +53,7 @@ function open4(config) {
|
|
|
53
53
|
counters.busy = Math.max(0, counters.busy - 1);
|
|
54
54
|
}, inflight);
|
|
55
55
|
const held = { pool, acquireMs, counters, notices, named, isClosed };
|
|
56
|
-
const checkout = () => closed ? rejectClosed() : import("./
|
|
56
|
+
const checkout = () => closed ? rejectClosed() : import("./r8x5qhd7.js").then((mod) => mod.checkout(held));
|
|
57
57
|
return {
|
|
58
58
|
capabilities: NODE_POSTGRES_CAPABILITIES,
|
|
59
59
|
execute(text, params, options) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PgSession
|
|
3
|
-
} from "./
|
|
3
|
+
} from "./krxx77g0.js";
|
|
4
4
|
import {
|
|
5
5
|
isConnectionLost
|
|
6
|
-
} from "./
|
|
6
|
+
} from "./pf0d8f77.js";
|
|
7
7
|
import {
|
|
8
8
|
driverErrors,
|
|
9
9
|
rejectClosed,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
runCall,
|
|
12
12
|
rowsFrom,
|
|
13
13
|
resultFrom
|
|
14
|
-
} from "./
|
|
14
|
+
} from "./cgbamx3e.js";
|
|
15
15
|
|
|
16
16
|
// src/adapters/pg/postgres-extra.ts
|
|
17
17
|
var RESET_ALL = "RESET ALL";
|
|
@@ -33,7 +33,7 @@ async function checkout(pool) {
|
|
|
33
33
|
return {
|
|
34
34
|
execute: (text, params, options) => guard(() => runCall(pool.isClosed(), options, (watch) => session.query(text, params, watch))),
|
|
35
35
|
batch: (statements, options) => guard(() => runCall(pool.isClosed(), options, async (watch) => {
|
|
36
|
-
const { runAtomicBatch } = await import("./
|
|
36
|
+
const { runAtomicBatch } = await import("./efmy2gcg.js");
|
|
37
37
|
return runAtomicBatch(session, statements, watch);
|
|
38
38
|
})),
|
|
39
39
|
cancel() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
open2
|
|
3
|
-
} from "./
|
|
3
|
+
} from "./krxx77g0.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
} from "./
|
|
5
|
+
startupHash2
|
|
6
|
+
} from "./8bwbwy8h.js";
|
|
7
7
|
import {
|
|
8
8
|
runTarget,
|
|
9
9
|
assumeRole,
|
|
@@ -15,29 +15,30 @@ import {
|
|
|
15
15
|
provisionMark,
|
|
16
16
|
expandProvisioned,
|
|
17
17
|
installSnapshot
|
|
18
|
-
} from "./
|
|
18
|
+
} from "./gde73es6.js";
|
|
19
19
|
import {
|
|
20
20
|
assertCreateRole,
|
|
21
21
|
currentUser,
|
|
22
22
|
roleExists
|
|
23
|
-
} from "./
|
|
23
|
+
} from "./szm0cksa.js";
|
|
24
24
|
import {
|
|
25
25
|
lintMigrationDirectory,
|
|
26
26
|
hasError,
|
|
27
27
|
lintRefusal,
|
|
28
|
-
assertTargetPolicy,
|
|
29
|
-
assertDirectConnection,
|
|
30
|
-
selectTarget,
|
|
31
28
|
projectHead,
|
|
32
29
|
loadBuiltSchema,
|
|
33
30
|
loadConfig
|
|
34
|
-
} from "./
|
|
31
|
+
} from "./qv2ydck9.js";
|
|
35
32
|
import {
|
|
36
33
|
changesRole,
|
|
37
34
|
batchSizeField,
|
|
38
35
|
assertExtensionsAvailable,
|
|
39
|
-
planMigration
|
|
40
|
-
|
|
36
|
+
planMigration,
|
|
37
|
+
assertTargetPolicy,
|
|
38
|
+
assertDirectConnection,
|
|
39
|
+
assertStableBackend,
|
|
40
|
+
selectTarget
|
|
41
|
+
} from "./ga3sfy93.js";
|
|
41
42
|
import {
|
|
42
43
|
OkmError2
|
|
43
44
|
} from "./52szzakm.js";
|
|
@@ -177,6 +178,8 @@ async function applyTarget2(request) {
|
|
|
177
178
|
const connection = await pool.reserve();
|
|
178
179
|
let locked = false;
|
|
179
180
|
try {
|
|
181
|
+
await assertStableBackend(() => backendPid(connection), request.allowPooler === true);
|
|
182
|
+
await assertStandardStrings(connection);
|
|
180
183
|
await setTimeouts(connection, request);
|
|
181
184
|
await lockTarget(connection, request.target);
|
|
182
185
|
locked = true;
|
|
@@ -202,6 +205,7 @@ async function applyTarget2(request) {
|
|
|
202
205
|
await connection.execute(META);
|
|
203
206
|
await connection.execute(HISTORY);
|
|
204
207
|
await connection.execute(BACKFILL);
|
|
208
|
+
const restamped = await restampLegacy(connection, request);
|
|
205
209
|
const history = await readHistory(connection);
|
|
206
210
|
const done = expandProvisioned(request.migrations, history.done, history.ids);
|
|
207
211
|
const units = request.migrations.flatMap((migration) => applyUnits(migration, done));
|
|
@@ -221,7 +225,7 @@ async function applyTarget2(request) {
|
|
|
221
225
|
if (request.snapshot !== undefined) {
|
|
222
226
|
await insertReference(connection, request);
|
|
223
227
|
}
|
|
224
|
-
return report;
|
|
228
|
+
return restamped.length === 0 ? report : { ...report, restamped };
|
|
225
229
|
} finally {
|
|
226
230
|
if (locked)
|
|
227
231
|
await connection.execute("select pg_advisory_unlock(hashtext($1))", [
|
|
@@ -306,7 +310,7 @@ no changes
|
|
|
306
310
|
migrations: [
|
|
307
311
|
{
|
|
308
312
|
id: "push",
|
|
309
|
-
catalogHash:
|
|
313
|
+
catalogHash: startupHash2(head.catalog),
|
|
310
314
|
steps: plan.steps
|
|
311
315
|
}
|
|
312
316
|
],
|
|
@@ -323,18 +327,73 @@ function backfillTiming2(backfill) {
|
|
|
323
327
|
function formatReport(report) {
|
|
324
328
|
const role = report.setRole !== undefined ? `set role ${report.setRole}
|
|
325
329
|
` : "";
|
|
330
|
+
const restamped = (report.restamped ?? []).map((id) => `restamped ${id}
|
|
331
|
+
`).join("");
|
|
326
332
|
if (report.applied.length === 0)
|
|
327
333
|
return `target ${report.target}
|
|
328
|
-
${role}nothing to apply
|
|
334
|
+
${role}${restamped}nothing to apply
|
|
329
335
|
`;
|
|
330
336
|
return `target ${report.target}
|
|
331
|
-
${role}${report.applied.map((id) => `applied ${id}`).join(`
|
|
337
|
+
${role}${restamped}${report.applied.map((id) => `applied ${id}`).join(`
|
|
332
338
|
`)}
|
|
333
339
|
`;
|
|
334
340
|
}
|
|
341
|
+
async function restampLegacy(connection, request) {
|
|
342
|
+
const pairs = [];
|
|
343
|
+
for (const migration of request.migrations) {
|
|
344
|
+
if (migration.legacyHash === undefined)
|
|
345
|
+
continue;
|
|
346
|
+
pairs.push({
|
|
347
|
+
id: migration.id,
|
|
348
|
+
mark: provisionMark(migration.id),
|
|
349
|
+
legacy: migration.legacyHash,
|
|
350
|
+
startup: migration.catalogHash
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
if (pairs.length === 0)
|
|
354
|
+
return [];
|
|
355
|
+
await connection.execute("begin");
|
|
356
|
+
try {
|
|
357
|
+
const result = await connection.execute(RESTAMP, [JSON.stringify(pairs)]);
|
|
358
|
+
const restamped = result.rows.flatMap((row) => typeof row[0] === "string" ? [row[0]] : []);
|
|
359
|
+
if (restamped.length > 0) {
|
|
360
|
+
assertTargetPolicy({ name: request.target, protected: request.protected }, "history-repair", request.allowProtected === true);
|
|
361
|
+
}
|
|
362
|
+
await connection.execute("commit");
|
|
363
|
+
return restamped;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
await connection.execute("rollback");
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
var RESTAMP = `with pairs as (
|
|
370
|
+
select * from json_to_recordset($1::json) as p(id text, mark text, legacy text, startup text)
|
|
371
|
+
), history as (
|
|
372
|
+
update okm_history h set catalog_hash = p.startup from pairs p
|
|
373
|
+
where h.migration_id in (p.id, p.mark) and h.catalog_hash = p.legacy
|
|
374
|
+
returning p.id, h.migration_id
|
|
375
|
+
), meta as (
|
|
376
|
+
update okm_meta m set catalog_hash = p.startup from pairs p
|
|
377
|
+
where m.id = 'head' and m.migration_id in (p.id, p.mark) and m.catalog_hash = p.legacy
|
|
378
|
+
and m.migration_id in (select migration_id from history)
|
|
379
|
+
returning m.id
|
|
380
|
+
)
|
|
381
|
+
select distinct id from history order by id`;
|
|
335
382
|
function joinMigrations(cwd, migrations) {
|
|
336
383
|
return join(cwd, migrations ?? "migrations");
|
|
337
384
|
}
|
|
385
|
+
async function backendPid(connection) {
|
|
386
|
+
const result = await connection.execute("select pg_backend_pid()::text");
|
|
387
|
+
const cell = result.rows[0]?.[0];
|
|
388
|
+
return cell === null || cell === undefined ? "" : String(cell);
|
|
389
|
+
}
|
|
390
|
+
async function assertStandardStrings(connection) {
|
|
391
|
+
const result = await connection.execute("show standard_conforming_strings");
|
|
392
|
+
const value = result.rows[0]?.[0];
|
|
393
|
+
if (value === "on")
|
|
394
|
+
return;
|
|
395
|
+
throw new OkmError2("invalid", `standard_conforming_strings is ${value === null || value === undefined ? "off" : String(value)}. Apply refuses to run.`, { fix: { summary: "Turn standard_conforming_strings on for this database." } });
|
|
396
|
+
}
|
|
338
397
|
async function setTimeouts(connection, request) {
|
|
339
398
|
const lock = request.lockTimeoutMs ?? DEFAULT_LOCK_MS;
|
|
340
399
|
const statement = request.statementTimeoutMs ?? DEFAULT_STATEMENT_MS;
|
|
@@ -41,7 +41,7 @@ function hasCapability2(flags, name) {
|
|
|
41
41
|
// src/adapters/failure.ts
|
|
42
42
|
var loading;
|
|
43
43
|
function driverErrors() {
|
|
44
|
-
loading ??= import("./
|
|
44
|
+
loading ??= import("./x72jb7ad.js");
|
|
45
45
|
return loading;
|
|
46
46
|
}
|
|
47
47
|
function rejectClosed(message = "The pool is closed.") {
|
|
@@ -83,7 +83,7 @@ function runCall(closed, options, fn) {
|
|
|
83
83
|
if (options === undefined || !needsWatch(options))
|
|
84
84
|
return fn(undefined);
|
|
85
85
|
const watched = options;
|
|
86
|
-
return import("./
|
|
86
|
+
return import("./54tqkxja.js").then(({ openWatch }) => {
|
|
87
87
|
const watch = openWatch(watched);
|
|
88
88
|
return fn(watch).finally(() => {
|
|
89
89
|
watch.finish();
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
open3
|
|
3
|
-
} from "./
|
|
3
|
+
} from "./87jvvhnt.js";
|
|
4
4
|
import {
|
|
5
5
|
PGLITE_CAPABILITIES,
|
|
6
6
|
readCapability2,
|
|
7
7
|
hasCapability2
|
|
8
|
-
} from "./
|
|
8
|
+
} from "./cgbamx3e.js";
|
|
9
9
|
export {
|
|
10
10
|
PGLITE_CAPABILITIES as capabilities,
|
|
11
11
|
hasCapability2 as hasCapability,
|
|
@@ -2,14 +2,10 @@ import {
|
|
|
2
2
|
roleObject,
|
|
3
3
|
grantObject,
|
|
4
4
|
defaultPrivilegeObject
|
|
5
|
-
} from "./
|
|
5
|
+
} from "./8bwbwy8h.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
} from "./
|
|
9
|
-
import {
|
|
10
|
-
quoteIdent,
|
|
11
|
-
createObjectSql
|
|
12
|
-
} from "./41kv2xdm.js";
|
|
7
|
+
quoteIdent
|
|
8
|
+
} from "./8xsbagad.js";
|
|
13
9
|
import {
|
|
14
10
|
functionObject,
|
|
15
11
|
triggerObject
|
|
@@ -40,12 +36,6 @@ import {
|
|
|
40
36
|
import {
|
|
41
37
|
referentialAction
|
|
42
38
|
} from "./4e4c6vq2.js";
|
|
43
|
-
import {
|
|
44
|
-
OkmError2
|
|
45
|
-
} from "./52szzakm.js";
|
|
46
|
-
|
|
47
|
-
// src/tooling/migrate/drift.ts
|
|
48
|
-
import postgres from "postgres";
|
|
49
39
|
|
|
50
40
|
// src/dialects/pg/view/depend.ts
|
|
51
41
|
function cellText(value) {
|
|
@@ -446,8 +436,9 @@ var RELATION_GRANTS = `select c.relname as name,
|
|
|
446
436
|
select 1 from pg_depend d
|
|
447
437
|
where d.classid = 'pg_class'::regclass and d.objid = c.oid and d.deptype = 'e'
|
|
448
438
|
)`;
|
|
439
|
+
var FUNCTION_ARG_TYPES = "replace(oidvectortypes(p.proargtypes), ' ', '')";
|
|
449
440
|
var FUNCTION_GRANTS = `select p.proname as name,
|
|
450
|
-
|
|
441
|
+
${FUNCTION_ARG_TYPES} as args,
|
|
451
442
|
r.rolname as role, a.privilege_type as privilege
|
|
452
443
|
from pg_proc p
|
|
453
444
|
join pg_namespace n on n.oid = p.pronamespace
|
|
@@ -923,222 +914,4 @@ function flag(row, key) {
|
|
|
923
914
|
return value === true || value === "true" || value === "t";
|
|
924
915
|
}
|
|
925
916
|
|
|
926
|
-
|
|
927
|
-
var SCRATCH = "okm_view_seal";
|
|
928
|
-
var VIEW_SHAPE = `
|
|
929
|
-
select c.relname as name, c.relkind as kind, pg_get_viewdef(c.oid, true) as query,
|
|
930
|
-
(
|
|
931
|
-
select coalesce(string_agg(
|
|
932
|
-
a.attname || '|' || format_type(a.atttypid, a.atttypmod),
|
|
933
|
-
E'\\n' order by a.attnum
|
|
934
|
-
), '')
|
|
935
|
-
from pg_attribute a
|
|
936
|
-
where a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
|
|
937
|
-
) as columns
|
|
938
|
-
from pg_class c
|
|
939
|
-
join pg_namespace n on n.oid = c.relnamespace
|
|
940
|
-
where n.nspname = $1 and c.relkind in ('v', 'm')
|
|
941
|
-
`;
|
|
942
|
-
async function sealViews(runner, source) {
|
|
943
|
-
const views = source.objects.filter((object) => object.kind === "view" || object.kind === "materializedView");
|
|
944
|
-
if (views.length === 0)
|
|
945
|
-
return source;
|
|
946
|
-
await dropScratch(runner);
|
|
947
|
-
await runner.query(`create schema ${quoteIdent(SCRATCH)}`);
|
|
948
|
-
try {
|
|
949
|
-
await runner.query(`set search_path to ${quoteIdent(SCRATCH)}`);
|
|
950
|
-
for (const object of source.objects) {
|
|
951
|
-
if (object.kind !== "type")
|
|
952
|
-
continue;
|
|
953
|
-
const statement = createObjectSql(object, SCRATCH);
|
|
954
|
-
if (statement !== undefined)
|
|
955
|
-
await runner.query(statement);
|
|
956
|
-
}
|
|
957
|
-
for (const object of source.objects) {
|
|
958
|
-
if (object.kind !== "table")
|
|
959
|
-
continue;
|
|
960
|
-
await runner.query(stubTable(object, source));
|
|
961
|
-
}
|
|
962
|
-
await createViews(runner, views);
|
|
963
|
-
const shapes = await runner.query(VIEW_SHAPE, [SCRATCH]);
|
|
964
|
-
const deps = await runner.query(VIEW_DEPENDENCIES, [SCRATCH]);
|
|
965
|
-
const edges = viewDependencyEdges(deps, PUBLIC, source.objects);
|
|
966
|
-
const byName = new Map(shapes.map((row) => [cellText(row.name), row]));
|
|
967
|
-
const next = source.objects.map((object) => {
|
|
968
|
-
if (object.kind !== "view" && object.kind !== "materializedView")
|
|
969
|
-
return object;
|
|
970
|
-
const row = byName.get(object.identity.name);
|
|
971
|
-
if (row === undefined) {
|
|
972
|
-
throw new OkmError2("OKM1020", `View ${object.identity.name} was not created in the scratch schema.`, {
|
|
973
|
-
fix: { summary: "The query has to run against the tables in this schema." }
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
const columns = readColumns(object, cellText(row.columns));
|
|
977
|
-
const query = normaliseViewQuery(cellText(row.query));
|
|
978
|
-
const dependencies = edges.get(object.identity.name) ?? [];
|
|
979
|
-
if (object.kind === "view") {
|
|
980
|
-
return viewObject({
|
|
981
|
-
namespace: object.identity.namespace,
|
|
982
|
-
name: object.identity.name,
|
|
983
|
-
columns,
|
|
984
|
-
query,
|
|
985
|
-
owner: object.owner,
|
|
986
|
-
provenance: object.provenance,
|
|
987
|
-
dependencies
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
return materializedViewObject({
|
|
991
|
-
namespace: object.identity.namespace,
|
|
992
|
-
name: object.identity.name,
|
|
993
|
-
columns,
|
|
994
|
-
query,
|
|
995
|
-
owner: object.owner,
|
|
996
|
-
provenance: object.provenance,
|
|
997
|
-
dependencies,
|
|
998
|
-
...object.definition.refresh !== undefined ? { refresh: object.definition.refresh } : {}
|
|
999
|
-
});
|
|
1000
|
-
});
|
|
1001
|
-
return catalog(next);
|
|
1002
|
-
} finally {
|
|
1003
|
-
await runner.query("set search_path to public");
|
|
1004
|
-
await dropScratch(runner);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
async function createViews(runner, views) {
|
|
1008
|
-
const pending = [...views];
|
|
1009
|
-
let last = "";
|
|
1010
|
-
for (let pass = 0;pass < views.length && pending.length > 0; pass += 1) {
|
|
1011
|
-
const failed = [];
|
|
1012
|
-
let created = 0;
|
|
1013
|
-
for (const object of pending) {
|
|
1014
|
-
const sql = object.kind === "materializedView" ? `create materialized view ${quoteIdent(object.identity.name)} as ${object.definition.query} with no data` : `create view ${quoteIdent(object.identity.name)} as ${object.definition.query}`;
|
|
1015
|
-
try {
|
|
1016
|
-
await runner.query(sql);
|
|
1017
|
-
created += 1;
|
|
1018
|
-
} catch (error) {
|
|
1019
|
-
last = error instanceof Error ? error.message : String(error);
|
|
1020
|
-
failed.push(object);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
if (created === 0) {
|
|
1024
|
-
throw new OkmError2("OKM1020", `View ${failed[0]?.identity.name ?? ""} could not be created. ${last}`, {
|
|
1025
|
-
fix: { summary: "The query has to run against the tables in this schema." }
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
pending.length = 0;
|
|
1029
|
-
pending.push(...failed);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
function stubTable(object, source) {
|
|
1033
|
-
const columns = source.objects.filter((item) => item.kind === "column" && item.identity.parent.name === object.identity.name);
|
|
1034
|
-
const lines = columns.map((column) => `${quoteIdent(column.identity.name)} ${column.definition.dataType}`);
|
|
1035
|
-
return `create table ${quoteIdent(object.identity.name)} (${lines.join(", ")})`;
|
|
1036
|
-
}
|
|
1037
|
-
function readColumns(object, printed) {
|
|
1038
|
-
const live = printed.length === 0 ? [] : printed.split(`
|
|
1039
|
-
`);
|
|
1040
|
-
const declared = object.definition.columns;
|
|
1041
|
-
if (live.length !== declared.length) {
|
|
1042
|
-
throw mismatch(object.identity.name, declared.map((column) => column.name).join(", "), printed);
|
|
1043
|
-
}
|
|
1044
|
-
return declared.map((column, index) => {
|
|
1045
|
-
const line = live[index] ?? "";
|
|
1046
|
-
const splitAt = line.indexOf("|");
|
|
1047
|
-
const name = splitAt < 0 ? line : line.slice(0, splitAt);
|
|
1048
|
-
const type = splitAt < 0 ? "" : line.slice(splitAt + 1);
|
|
1049
|
-
if (name !== column.name || type !== column.dataType) {
|
|
1050
|
-
throw mismatch(object.identity.name, `${column.name}:${column.dataType}`, live[index] ?? "");
|
|
1051
|
-
}
|
|
1052
|
-
return column;
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
function mismatch(view, declared, live) {
|
|
1056
|
-
return new OkmError2("OKM1020", `View ${view} declares ${declared} and the database has ${live}.`, { fix: { summary: "Declare the columns the query returns, in that order, with those types." } });
|
|
1057
|
-
}
|
|
1058
|
-
async function dropScratch(runner) {
|
|
1059
|
-
const present = await runner.query(`select 1 as ok from pg_namespace where nspname = $1`, [
|
|
1060
|
-
SCRATCH
|
|
1061
|
-
]);
|
|
1062
|
-
if (present.length === 0)
|
|
1063
|
-
return;
|
|
1064
|
-
const rows = await runner.query(`
|
|
1065
|
-
select c.relname as name, c.relkind as kind
|
|
1066
|
-
from pg_class c
|
|
1067
|
-
join pg_namespace n on n.oid = c.relnamespace
|
|
1068
|
-
where n.nspname = $1 and c.relkind in ('v', 'm', 'r')
|
|
1069
|
-
`, [SCRATCH]);
|
|
1070
|
-
const views = rows.filter((row) => cellText(row.kind) === "v" || cellText(row.kind) === "m");
|
|
1071
|
-
for (let pass = 0;pass < Math.max(views.length, 1); pass += 1) {
|
|
1072
|
-
for (const row of views) {
|
|
1073
|
-
const kind = cellText(row.kind) === "m" ? "materialized view" : "view";
|
|
1074
|
-
try {
|
|
1075
|
-
await runner.query(`drop ${kind} if exists ${quoteIdent(SCRATCH)}.${quoteIdent(cellText(row.name))}`);
|
|
1076
|
-
} catch {}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
for (const row of rows) {
|
|
1080
|
-
if (cellText(row.kind) !== "r")
|
|
1081
|
-
continue;
|
|
1082
|
-
await runner.query(`drop table if exists ${quoteIdent(SCRATCH)}.${quoteIdent(cellText(row.name))}`);
|
|
1083
|
-
}
|
|
1084
|
-
await runner.query(`drop schema if exists ${quoteIdent(SCRATCH)} cascade`);
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
// src/tooling/migrate/drift.ts
|
|
1088
|
-
async function assertAuthorDrift2(url, author, roles) {
|
|
1089
|
-
const sql = postgres(url, {
|
|
1090
|
-
max: 1,
|
|
1091
|
-
connect_timeout: 5,
|
|
1092
|
-
idle_timeout: 1,
|
|
1093
|
-
onnotice: () => {}
|
|
1094
|
-
});
|
|
1095
|
-
try {
|
|
1096
|
-
const present = await sql`
|
|
1097
|
-
select to_regclass('public.okm_meta')::text as reg
|
|
1098
|
-
`;
|
|
1099
|
-
if (present[0]?.reg == null)
|
|
1100
|
-
return;
|
|
1101
|
-
const runner = catalogQuery2(sql);
|
|
1102
|
-
const authorOnServer = await sealViews(runner, author);
|
|
1103
|
-
const live = await introspectSchema(runner, "public", "public", managedRoleOptions2(roles));
|
|
1104
|
-
const plan = planMigration({ before: live, after: authorOnServer, name: "check" });
|
|
1105
|
-
if (plan.steps.length === 0)
|
|
1106
|
-
return;
|
|
1107
|
-
throw new OkmError2("OKM1520", `The database differs from the schema.
|
|
1108
|
-
${plan.steps.map((step) => step.sql).join(`
|
|
1109
|
-
`)}`, {
|
|
1110
|
-
fix: {
|
|
1111
|
-
summary: "Change the schema or the database so okm check reports no difference."
|
|
1112
|
-
}
|
|
1113
|
-
});
|
|
1114
|
-
} finally {
|
|
1115
|
-
await sql.end({ timeout: 5 });
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
function catalogQuery2(sql) {
|
|
1119
|
-
return {
|
|
1120
|
-
async query(text, params) {
|
|
1121
|
-
const rows = await sql.unsafe(text, params === undefined ? undefined : [...params]);
|
|
1122
|
-
return rows.map((row) => {
|
|
1123
|
-
const copy = {};
|
|
1124
|
-
for (const [key, value] of Object.entries(row))
|
|
1125
|
-
copy[key] = value;
|
|
1126
|
-
return copy;
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
function managedRoleOptions2(roles) {
|
|
1132
|
-
const names = managedRoleNames(roles);
|
|
1133
|
-
return names === undefined ? undefined : { managedRoles: names };
|
|
1134
|
-
}
|
|
1135
|
-
function managedRoleNames(roles) {
|
|
1136
|
-
if (roles === undefined)
|
|
1137
|
-
return;
|
|
1138
|
-
const names = new Set([roles.migration, roles.app]);
|
|
1139
|
-
for (const role of roles.managed ?? [])
|
|
1140
|
-
names.add(role.name);
|
|
1141
|
-
return [...names];
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
export { introspectSchema, sealViews, assertAuthorDrift2, catalogQuery2, managedRoleOptions2 };
|
|
917
|
+
export { cellText, VIEW_DEPENDENCIES, viewDependencyEdges, PUBLIC, introspectSchema, FUNCTION_ARG_TYPES };
|