okmodel 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -22
- package/dist/contracts/catalog/document.d.ts +6 -2
- package/dist/dialects/pg/ext/citext.js +1 -1
- package/dist/dialects/pg/ext/index.js +1 -1
- package/dist/dialects/pg/ext/pg-trgm.js +1 -1
- package/dist/dialects/pg/index.js +3 -3
- package/dist/dialects/pg/role/sql.d.ts +11 -4
- package/dist/dialects/pg/table.d.ts +17 -1
- package/dist/dialects/pg/view/index.js +1 -1
- package/dist/dialects/pg/view/scratch.d.ts +3 -2
- package/dist/runtime/catalog-cache.d.ts +17 -0
- package/dist/runtime/compat-words.d.ts +23 -0
- package/dist/runtime/drift-detail.d.ts +20 -4
- package/dist/runtime/drift.d.ts +4 -2
- package/dist/runtime/pg/bun.js +2 -2
- package/dist/runtime/pg/pg.js +2 -2
- package/dist/runtime/pg/pglite.js +2 -2
- package/dist/runtime/pg/postgresjs.js +2 -2
- package/dist/runtime/traits/index.js +5 -4
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/validate/index.js +2 -2
- package/dist/shared/2321avmn.js +418 -0
- package/dist/shared/{x6wysh83.js → 3p4b1fg0.js} +3 -5
- package/dist/shared/{wekze34d.js → 41kv2xdm.js} +74 -2
- package/dist/shared/{fs0ex00p.js → 4ef7mmqf.js} +2 -2
- package/dist/shared/5mtf6cj3.js +94 -0
- package/dist/shared/{999ade2q.js → 5vdm4sgp.js} +1 -1
- package/dist/shared/{qybh48nd.js → 6693j9rs.js} +1 -1
- package/dist/shared/{bqha5cg6.js → 6p5k20sm.js} +2 -2
- package/dist/shared/7nk68fj8.js +751 -0
- package/dist/shared/{ne02ng5c.js → 84ptn35q.js} +7 -4
- package/dist/shared/{t616ynfn.js → 8jb4ymh2.js} +59 -12
- package/dist/shared/{th14ygv2.js → 8ktwd3rh.js} +29 -1
- package/dist/shared/9yn6a2vd.js +11 -0
- package/dist/shared/ady1ms74.js +72 -0
- package/dist/shared/{scwxrpx2.js → c5g81ngb.js} +82 -15
- package/dist/shared/{6bazvqwm.js → fb00y8fp.js} +2 -2
- package/dist/shared/{jkc6ws1p.js → g7ez901n.js} +83 -2
- package/dist/shared/{kgnc8grt.js → gdh9mrtp.js} +1 -1
- package/dist/shared/hqjsxnz7.js +13 -0
- package/dist/shared/htnaj77v.js +17 -0
- package/dist/shared/{mt5wswjz.js → jgxzjzcz.js} +1 -1
- package/dist/shared/{6w5j844a.js → jsd07prz.js} +2 -2
- package/dist/shared/{m78s7898.js → kafaa5zv.js} +1 -1
- package/dist/shared/kj680nxv.js +19 -0
- package/dist/shared/{k34e0xf2.js → kkzwp3d2.js} +8 -8
- package/dist/shared/{2ssqrv66.js → n5y9zyj1.js} +1029 -174
- package/dist/shared/{ty3v3hfk.js → nxb42q7h.js} +2 -2
- package/dist/shared/{5nmdenb6.js → q6zsyh31.js} +1003 -40
- package/dist/shared/{fptpmvxp.js → q795afwa.js} +26 -21
- package/dist/shared/{3ekj9x92.js → qn0srwwq.js} +1 -1
- package/dist/shared/s4mb6a4b.js +545 -0
- package/dist/shared/{01yr20s5.js → sf8m6tfy.js} +2 -2
- package/dist/shared/{bryqk9k9.js → sr5ms7b3.js} +1 -1
- package/dist/shared/{meck53jv.js → tny2t3sm.js} +6 -5
- package/dist/shared/{p24x2334.js → v7tpnhnz.js} +1 -1
- package/dist/shared/{xw1d69vd.js → wqem4jed.js} +6 -6
- package/dist/shared/{49ypnfwt.js → xghkt7a9.js} +50 -28
- package/dist/shared/{7fvtrkwr.js → ycatkxmq.js} +1 -1
- package/dist/shared/ygem90rn.js +900 -0
- package/dist/shared/{1tfdfvhh.js → yjw02enp.js} +6 -4
- package/dist/shared/{ydaz1sv3.js → zg24x0hm.js} +1 -1
- package/dist/tooling/cli.js +46 -17
- package/dist/tooling/migrate/apply.d.ts +59 -5
- package/dist/tooling/migrate/backfill.d.ts +136 -0
- package/dist/tooling/migrate/check.d.ts +65 -0
- package/dist/tooling/migrate/classify.d.ts +108 -0
- package/dist/tooling/migrate/commands.d.ts +2 -2
- package/dist/tooling/migrate/config.d.ts +22 -0
- package/dist/tooling/migrate/doctor.d.ts +3 -0
- package/dist/tooling/migrate/drift.d.ts +18 -2
- package/dist/tooling/migrate/estimate.d.ts +50 -0
- package/dist/tooling/migrate/extensions.d.ts +2 -0
- package/dist/tooling/migrate/files.d.ts +12 -0
- package/dist/tooling/migrate/index.d.ts +3 -2
- package/dist/tooling/migrate/index.js +67 -1
- package/dist/tooling/migrate/lint-rules.d.ts +51 -0
- package/dist/tooling/migrate/lint.d.ts +71 -0
- package/dist/tooling/migrate/managed.d.ts +1 -1
- package/dist/tooling/migrate/plan.d.ts +64 -9
- package/dist/tooling/migrate/project.d.ts +42 -7
- package/dist/tooling/migrate/provision.d.ts +18 -0
- package/dist/tooling/migrate/reference.d.ts +40 -0
- package/dist/tooling/migrate/runner.d.ts +108 -0
- package/dist/tooling/migrate/safe.d.ts +77 -0
- package/dist/tooling/migrate/seed.d.ts +20 -0
- package/dist/tooling/migrate/snapshot.d.ts +78 -0
- package/dist/tooling/migrate/status.d.ts +25 -6
- package/dist/tooling/testing/factories.d.ts +117 -0
- package/dist/tooling/testing/facts.d.ts +72 -0
- package/dist/tooling/testing/index.d.ts +89 -0
- package/dist/tooling/testing/index.js +8 -0
- package/dist/tooling/testing/isolation.d.ts +30 -0
- package/dist/tooling/testing/random.d.ts +40 -0
- package/dist/tooling/testing/record.d.ts +38 -0
- package/package.json +6 -2
- package/dist/shared/dbjej1tw.js +0 -456
- package/dist/shared/g4f5kcx9.js +0 -37
- package/dist/shared/qbxzvwwc.js +0 -88
- package/dist/shared/zwmqxb0g.js +0 -79
- package/dist/shared/{xva8v7qk.js → awrsqrn7.js} +3 -3
- package/dist/shared/{75xkjv0t.js → fa9vt245.js} +0 -0
- package/dist/shared/{m9tv7xn7.js → radd3z4g.js} +3 -3
- /package/dist/shared/{0jc3ffzf.js → zvbjc949.js} +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import"./
|
|
1
|
+
import"./8jb4ymh2.js";
|
|
2
2
|
import"./n442h54c.js";
|
|
3
|
+
import"./wqem4jed.js";
|
|
3
4
|
import {
|
|
4
5
|
listTargets,
|
|
5
6
|
loadConfig
|
|
6
|
-
} from "./
|
|
7
|
-
import"./
|
|
7
|
+
} from "./q6zsyh31.js";
|
|
8
|
+
import"./n5y9zyj1.js";
|
|
9
|
+
import"./41kv2xdm.js";
|
|
8
10
|
|
|
9
11
|
// src/tooling/migrate/dev.ts
|
|
10
12
|
import { mkdirSync } from "node:fs";
|
|
@@ -17,7 +19,7 @@ async function devProject(cwd) {
|
|
|
17
19
|
`;
|
|
18
20
|
const directory = join(cwd, config.out ?? ".okm", "dev-db");
|
|
19
21
|
mkdirSync(directory, { recursive: true });
|
|
20
|
-
const { open } = await import("./
|
|
22
|
+
const { open } = await import("./gdh9mrtp.js");
|
|
21
23
|
const pool = await open({ dataDir: directory });
|
|
22
24
|
try {
|
|
23
25
|
await pool.execute("select 1");
|
package/dist/tooling/cli.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import"../shared/
|
|
1
|
+
import"../shared/8jb4ymh2.js";
|
|
2
2
|
import"../shared/n442h54c.js";
|
|
3
|
+
import"../shared/wqem4jed.js";
|
|
3
4
|
import {
|
|
5
|
+
formatFindings,
|
|
6
|
+
hasError,
|
|
7
|
+
lintRefusal,
|
|
4
8
|
buildProject,
|
|
5
9
|
generateProject,
|
|
6
10
|
planProject,
|
|
7
11
|
checkProject
|
|
8
|
-
} from "../shared/
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
} from "../shared/2ssqrv66.js";
|
|
12
|
+
} from "../shared/q6zsyh31.js";
|
|
13
|
+
import"../shared/n5y9zyj1.js";
|
|
14
|
+
import"../shared/41kv2xdm.js";
|
|
12
15
|
import {
|
|
13
16
|
OkmError2
|
|
14
17
|
} from "../shared/52szzakm.js";
|
|
@@ -35,26 +38,31 @@ async function run(argv, io) {
|
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
40
|
if (command === "check") {
|
|
38
|
-
await checkProject(cwd, invoke(splitFlags(rest)));
|
|
41
|
+
const findings = await checkProject(cwd, invoke(splitFlags(rest)));
|
|
42
|
+
const warnings = formatFindings(findings);
|
|
43
|
+
if (warnings.length > 0)
|
|
44
|
+
stdout(warnings);
|
|
39
45
|
stdout(`ok
|
|
40
46
|
`);
|
|
41
47
|
return;
|
|
42
48
|
}
|
|
43
49
|
if (command === "generate") {
|
|
44
50
|
const parsed = splitFlags(rest);
|
|
45
|
-
const
|
|
46
|
-
stdout(
|
|
47
|
-
` : `${path}
|
|
51
|
+
const written = await generateProject(cwd, parsed.name ?? "migration", parsed.flags);
|
|
52
|
+
stdout(written === undefined ? `no changes
|
|
53
|
+
` : `${written.path}
|
|
48
54
|
`);
|
|
55
|
+
if (written !== undefined)
|
|
56
|
+
stdout(formatFindings(written.findings));
|
|
49
57
|
return;
|
|
50
58
|
}
|
|
51
59
|
if (command === "dev") {
|
|
52
|
-
const { devProject } = await import("../shared/
|
|
60
|
+
const { devProject } = await import("../shared/yjw02enp.js");
|
|
53
61
|
stdout(await devProject(cwd));
|
|
54
62
|
return;
|
|
55
63
|
}
|
|
56
64
|
if (command === "push") {
|
|
57
|
-
const { pushProject } = await import("../shared/
|
|
65
|
+
const { pushProject } = await import("../shared/kj680nxv.js");
|
|
58
66
|
stdout(await pushProject(cwd, invoke(splitFlags(rest))));
|
|
59
67
|
return;
|
|
60
68
|
}
|
|
@@ -65,23 +73,44 @@ async function run(argv, io) {
|
|
|
65
73
|
if (parsed.name === undefined) {
|
|
66
74
|
throw new OkmError2("invalid", "okm migrate plan needs a name.");
|
|
67
75
|
}
|
|
68
|
-
|
|
76
|
+
const planned = await planProject(cwd, parsed.name, parsed.flags, invoke(parsed));
|
|
77
|
+
stdout(planned.text);
|
|
78
|
+
stdout(formatFindings(planned.findings));
|
|
79
|
+
if (hasError(planned.findings))
|
|
80
|
+
throw lintRefusal(planned.findings);
|
|
69
81
|
return;
|
|
70
82
|
}
|
|
71
83
|
if (sub === "apply") {
|
|
72
|
-
const { applyProject } = await import("../shared/
|
|
73
|
-
stdout(await applyProject(cwd, invoke(parsed)));
|
|
84
|
+
const { applyProject } = await import("../shared/kj680nxv.js");
|
|
85
|
+
stdout(await applyProject(cwd, invoke(parsed), stdout));
|
|
74
86
|
return;
|
|
75
87
|
}
|
|
76
88
|
if (sub === "status") {
|
|
77
|
-
const { statusProject } = await import("../shared/
|
|
89
|
+
const { statusProject } = await import("../shared/c5g81ngb.js");
|
|
78
90
|
stdout(await statusProject(cwd, invoke(parsed)));
|
|
79
91
|
return;
|
|
80
92
|
}
|
|
93
|
+
if (sub === "check") {
|
|
94
|
+
const { checkMigrations } = await import("../shared/2321avmn.js");
|
|
95
|
+
const provision = parsed.flags.includes("--provision") || rest.slice(1).includes("--provision");
|
|
96
|
+
stdout(await checkMigrations(cwd, invoke(parsed), { provision }));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (command === "seed") {
|
|
101
|
+
const parsed = splitFlags(rest);
|
|
102
|
+
if (parsed.name === undefined) {
|
|
103
|
+
throw new OkmError2("invalid", "okm seed needs a file.", {
|
|
104
|
+
fix: { summary: "Pass okm seed <file>." }
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const { seedProject } = await import("../shared/ady1ms74.js");
|
|
108
|
+
stdout(await seedProject(cwd, parsed.name, invoke(parsed)));
|
|
109
|
+
return;
|
|
81
110
|
}
|
|
82
111
|
if (command === "doctor") {
|
|
83
112
|
const parsed = splitFlags(rest);
|
|
84
|
-
const { doctorProject } = await import("../shared/
|
|
113
|
+
const { doctorProject } = await import("../shared/84ptn35q.js");
|
|
85
114
|
stdout(await doctorProject(cwd, parsed.name, invoke(parsed)));
|
|
86
115
|
return;
|
|
87
116
|
}
|
|
@@ -93,7 +122,7 @@ async function run(argv, io) {
|
|
|
93
122
|
});
|
|
94
123
|
}
|
|
95
124
|
if (sub === "list" || sub === "check") {
|
|
96
|
-
const { extProject } = await import("../shared/
|
|
125
|
+
const { extProject } = await import("../shared/tny2t3sm.js");
|
|
97
126
|
stdout(await extProject(cwd, sub, invoke(splitFlags(rest.slice(1)))));
|
|
98
127
|
return;
|
|
99
128
|
}
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A run holds a session advisory lock. Transactional steps of one migration
|
|
5
5
|
* share a transaction. Steps Postgres cannot run there (`CREATE INDEX
|
|
6
|
-
* CONCURRENTLY`, `ADD VALUE`, `VACUUM`) are their own
|
|
7
|
-
*
|
|
6
|
+
* CONCURRENTLY`, `ADD VALUE`, `VACUUM`) and backfill steps are their own
|
|
7
|
+
* units. {@link runTarget} runs those units. Each finished step is a history
|
|
8
|
+
* row. A later apply skips those rows. A backfill records each committed
|
|
9
|
+
* batch in `okm_backfill` and resumes from that boundary.
|
|
8
10
|
*/
|
|
11
|
+
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
12
|
+
import { type BackfillBatch } from "./runner.js";
|
|
13
|
+
import { type ReferenceTable } from "./reference.js";
|
|
9
14
|
import { type PlanStep } from "./plan.js";
|
|
10
15
|
import type { StoredMigration } from "./files.js";
|
|
11
16
|
import { type InvokeFlags } from "./policy.js";
|
|
@@ -51,6 +56,35 @@ export type ApplyRequest = {
|
|
|
51
56
|
* on this schema before `SET ROLE`. The default is `public`.
|
|
52
57
|
*/
|
|
53
58
|
readonly schema?: string;
|
|
59
|
+
/** Milliseconds to wait after each committed backfill batch. */
|
|
60
|
+
readonly backfillPauseMs?: number;
|
|
61
|
+
/** `statement_timeout` for one backfill batch. Falls back to {@link ApplyRequest.statementTimeoutMs}. */
|
|
62
|
+
readonly backfillStatementTimeoutMs?: number;
|
|
63
|
+
/** One line after each committed backfill batch. */
|
|
64
|
+
readonly onProgress?: (line: string) => void;
|
|
65
|
+
/** Runs after each committed backfill batch, before the pause. */
|
|
66
|
+
readonly afterBatch?: (batch: BackfillBatch) => void | Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Migrations directory to lint before any DDL.
|
|
69
|
+
*
|
|
70
|
+
* Only a migration with a step missing from `okm_history` is checked.
|
|
71
|
+
* `push` omits this: its steps are not files.
|
|
72
|
+
*/
|
|
73
|
+
readonly lintDirectory?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Head snapshot.
|
|
76
|
+
*
|
|
77
|
+
* Set by `okm migrate apply` and `provision()`. An empty target installs
|
|
78
|
+
* this catalog instead of replaying files. `only` refuses a target that
|
|
79
|
+
* is not empty (OKM1851) and does not fall through to replay.
|
|
80
|
+
*/
|
|
81
|
+
readonly snapshot?: {
|
|
82
|
+
readonly catalog: Catalog;
|
|
83
|
+
readonly migrationId: string;
|
|
84
|
+
readonly catalogHash: string;
|
|
85
|
+
readonly reference: readonly ReferenceTable[];
|
|
86
|
+
readonly only?: boolean;
|
|
87
|
+
};
|
|
54
88
|
};
|
|
55
89
|
/** Migrations that ran at least one step in this invocation. */
|
|
56
90
|
export type ApplyReport = {
|
|
@@ -74,8 +108,9 @@ export declare function applyUnits(migration: StoredMigration, done: ReadonlySet
|
|
|
74
108
|
* Applies pending migrations on one target.
|
|
75
109
|
*
|
|
76
110
|
* The connection is reserved for the whole run so the advisory lock stays
|
|
77
|
-
* session-scoped. Lock timeout retries that step
|
|
78
|
-
*
|
|
111
|
+
* session-scoped. Lock timeout retries that step, including `NOT VALID`,
|
|
112
|
+
* `VALIDATE CONSTRAINT`, `SET NOT NULL`, `ADD CONSTRAINT … USING INDEX`, and
|
|
113
|
+
* a concurrent index create or drop. Any other failure stops at that step.
|
|
79
114
|
*
|
|
80
115
|
* @param request - Target, flags, and the migrations to consider
|
|
81
116
|
* @returns The target and the migration ids that ran
|
|
@@ -84,11 +119,15 @@ export declare function applyTarget(request: ApplyRequest): Promise<ApplyReport>
|
|
|
84
119
|
/**
|
|
85
120
|
* Applies the migrations in the project to the selected target.
|
|
86
121
|
*
|
|
122
|
+
* Pending migrations are linted after `okm_history` is read and before any
|
|
123
|
+
* DDL. A migration the target already applied is left as it ran.
|
|
124
|
+
*
|
|
87
125
|
* @param cwd - Project directory
|
|
88
126
|
* @param flags - `--target`, protection, pooler, and timeouts
|
|
127
|
+
* @param write - Receives each backfill progress line, including the newline
|
|
89
128
|
* @returns Text for stdout, including the target name
|
|
90
129
|
*/
|
|
91
|
-
export declare function applyProject(cwd: string, flags: InvokeFlags): Promise<string>;
|
|
130
|
+
export declare function applyProject(cwd: string, flags: InvokeFlags, write?: (text: string) => void): Promise<string>;
|
|
92
131
|
/**
|
|
93
132
|
* Prototype sync. Blocked on a protected target.
|
|
94
133
|
*
|
|
@@ -100,3 +139,18 @@ export declare function applyProject(cwd: string, flags: InvokeFlags): Promise<s
|
|
|
100
139
|
* @returns Text for stdout
|
|
101
140
|
*/
|
|
102
141
|
export declare function pushProject(cwd: string, flags: InvokeFlags): Promise<string>;
|
|
142
|
+
/**
|
|
143
|
+
* Pause and statement timeout `defineConfig({ backfill })` sets for a run.
|
|
144
|
+
*
|
|
145
|
+
* Omitted fields stay unset so the runner uses its built-in defaults.
|
|
146
|
+
*
|
|
147
|
+
* @param backfill - Config block, when the project set one
|
|
148
|
+
* @returns The fields {@link applyTarget} reads
|
|
149
|
+
*/
|
|
150
|
+
export declare function backfillTiming(backfill: {
|
|
151
|
+
readonly pauseMs?: number;
|
|
152
|
+
readonly statementTimeoutMs?: number;
|
|
153
|
+
} | undefined): {
|
|
154
|
+
readonly backfillPauseMs?: number;
|
|
155
|
+
readonly backfillStatementTimeoutMs?: number;
|
|
156
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backfill step header and the per-batch predicate (D195).
|
|
3
|
+
*
|
|
4
|
+
* The migration file keeps one `UPDATE`. `-- backfill` names the table, the
|
|
5
|
+
* primary key, and the batch size. `$1` is the exclusive lower bound and `$2`
|
|
6
|
+
* is the inclusive upper bound. Null means that side is open. The runner
|
|
7
|
+
* fills those bounds. This module does not run the batches.
|
|
8
|
+
*/
|
|
9
|
+
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Rows in one batch when the plan and the config omit `batchSize`.
|
|
12
|
+
*
|
|
13
|
+
* A step header `batch=` replaces this for that step.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_BACKFILL_BATCH_SIZE = 1000;
|
|
16
|
+
/**
|
|
17
|
+
* Milliseconds to wait after a committed batch when the config omits `pauseMs`.
|
|
18
|
+
*
|
|
19
|
+
* Zero runs the next batch immediately. Set a pause to rate-limit a large table.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_BACKFILL_PAUSE_MS = 0;
|
|
22
|
+
/**
|
|
23
|
+
* `statement_timeout` for one batch when the config omits `statementTimeoutMs`.
|
|
24
|
+
*
|
|
25
|
+
* This is separate from the apply timeout on DDL. A timed-out batch rolls
|
|
26
|
+
* back and leaves the previous checkpoint.
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS = 30000;
|
|
29
|
+
/** One primary-key column, quoted, with the catalog type used as a cast. */
|
|
30
|
+
export type BackfillColumn = {
|
|
31
|
+
/** Quoted identifier. */
|
|
32
|
+
readonly quoted: string;
|
|
33
|
+
/** Catalog `dataType`, safe to place after `::`. */
|
|
34
|
+
readonly dataType: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* What `-- backfill` records.
|
|
38
|
+
*
|
|
39
|
+
* `key` is quoted identifiers in primary-key order. Types live in the
|
|
40
|
+
* statement's casts, not in the header.
|
|
41
|
+
*/
|
|
42
|
+
export type BackfillSpec = {
|
|
43
|
+
/** Quoted schema-qualified table, as in the statement. */
|
|
44
|
+
readonly table: string;
|
|
45
|
+
/** Quoted key columns. */
|
|
46
|
+
readonly key: readonly string[];
|
|
47
|
+
/** Rows per batch. The header value, which overrides config. */
|
|
48
|
+
readonly batch: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Batch size the planner writes into a new step.
|
|
52
|
+
*
|
|
53
|
+
* @param requested - `defineConfig({ backfill: { batchSize } })`, when set
|
|
54
|
+
* @returns A positive integer. The built-in default is 1000
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Field for {@link resolveBatchSize} when the config may omit it.
|
|
58
|
+
*
|
|
59
|
+
* Exact optional properties reject `batchSize: undefined`.
|
|
60
|
+
*
|
|
61
|
+
* @param batchSize - Configured size, when set
|
|
62
|
+
* @returns `{ batchSize }` or an empty object
|
|
63
|
+
*/
|
|
64
|
+
export declare function batchSizeField(batchSize: number | undefined): {
|
|
65
|
+
readonly batchSize: number;
|
|
66
|
+
} | Record<string, never>;
|
|
67
|
+
/**
|
|
68
|
+
* Batch size the planner writes into a new step.
|
|
69
|
+
*
|
|
70
|
+
* @param requested - `defineConfig({ backfill: { batchSize } })`, when set
|
|
71
|
+
* @returns A positive integer. The built-in default is 1000
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveBatchSize(requested: number | undefined): number;
|
|
74
|
+
/**
|
|
75
|
+
* Prints the header comment.
|
|
76
|
+
*
|
|
77
|
+
* @param spec - Table, key, and batch size
|
|
78
|
+
* @returns One line, without a trailing newline
|
|
79
|
+
*/
|
|
80
|
+
export declare function formatBackfillHeader(spec: BackfillSpec): string;
|
|
81
|
+
/**
|
|
82
|
+
* Reads a `-- backfill` line.
|
|
83
|
+
*
|
|
84
|
+
* @param line - One comment line
|
|
85
|
+
* @returns The spec, or `undefined` when the line is not a backfill header
|
|
86
|
+
*/
|
|
87
|
+
export declare function parseBackfillHeader(line: string): BackfillSpec | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Primary key of `table`, or OKM1546 when the table has none.
|
|
90
|
+
*
|
|
91
|
+
* A backfill walks this key. It does not count rows and it does not scan.
|
|
92
|
+
*
|
|
93
|
+
* @param catalogs - Before and after catalogs. The first key found wins
|
|
94
|
+
* @param table - Unqualified table name
|
|
95
|
+
* @returns Columns in key order, with cast types
|
|
96
|
+
*/
|
|
97
|
+
export declare function primaryKeyColumns(catalogs: readonly Catalog[], table: string): readonly BackfillColumn[];
|
|
98
|
+
/**
|
|
99
|
+
* Predicate that limits an idempotent `UPDATE` to one key range.
|
|
100
|
+
*
|
|
101
|
+
* `$1` is exclusive and `$2` is inclusive. Either null leaves that side open.
|
|
102
|
+
* One column compares as itself. A composite key compares as a row, with the
|
|
103
|
+
* bound stored as a JSON array of text.
|
|
104
|
+
*
|
|
105
|
+
* @param columns - Primary key in order
|
|
106
|
+
* @returns SQL fragment with no leading `and`
|
|
107
|
+
*/
|
|
108
|
+
export declare function keyRangePredicate(columns: readonly BackfillColumn[]): string;
|
|
109
|
+
/**
|
|
110
|
+
* How many batches `rows` take at `batch` size.
|
|
111
|
+
*
|
|
112
|
+
* Zero rows is zero batches. The count is `ceil(rows / batch)`.
|
|
113
|
+
*
|
|
114
|
+
* @param rows - Estimated row count
|
|
115
|
+
* @param batch - Batch size from the step header
|
|
116
|
+
* @returns The batch count a plan line prints
|
|
117
|
+
*/
|
|
118
|
+
export declare function aboutBatchCount(rows: number, batch: number): number;
|
|
119
|
+
/**
|
|
120
|
+
* Keyset query that returns the last key of the next batch.
|
|
121
|
+
*
|
|
122
|
+
* `$1` is the previous boundary, null for the first batch. `$2` is
|
|
123
|
+
* `batch - 1`. No row means this batch is the last one.
|
|
124
|
+
*
|
|
125
|
+
* @param table - Quoted schema-qualified table
|
|
126
|
+
* @param columns - Primary key, with cast types
|
|
127
|
+
* @returns One statement
|
|
128
|
+
*/
|
|
129
|
+
export declare function boundaryQuery(table: string, columns: readonly BackfillColumn[]): string;
|
|
130
|
+
/**
|
|
131
|
+
* Cast spelling safe to interpolate after `::`.
|
|
132
|
+
*
|
|
133
|
+
* @param dataType - Catalog or `format_type` spelling
|
|
134
|
+
* @returns The same spelling
|
|
135
|
+
*/
|
|
136
|
+
export declare function checkedCast(dataType: string): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `okm migrate check` proves a migration history is sound (D196).
|
|
3
|
+
*
|
|
4
|
+
* The command replays every file into a scratch schema through
|
|
5
|
+
* {@link applyTarget}, then plans the introspected schema back to that
|
|
6
|
+
* file's catalog. It also checks the previous catalog, the head, and the
|
|
7
|
+
* linter. Nothing here changes apply.
|
|
8
|
+
*/
|
|
9
|
+
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
10
|
+
import { type MigrationClass } from "./classify.js";
|
|
11
|
+
import { type MigrationPlan } from "./plan.js";
|
|
12
|
+
import { type InvokeFlags } from "./policy.js";
|
|
13
|
+
/** Prefix of the scratch schema. The random suffix makes the name unique. */
|
|
14
|
+
export declare const CHECK_SCHEMA_PREFIX = "okm_check_";
|
|
15
|
+
/**
|
|
16
|
+
* Runs `okm migrate check` for the project in `cwd`.
|
|
17
|
+
*
|
|
18
|
+
* A protected target is refused before any statement. The scratch schema is
|
|
19
|
+
* dropped before this returns, including when a check fails. `--allow-protected`
|
|
20
|
+
* does not lift the refusal.
|
|
21
|
+
*
|
|
22
|
+
* @param cwd - Project directory
|
|
23
|
+
* @param flags - `--target` and pooler. Protection is ignored
|
|
24
|
+
* @param options - `--provision` also compares a snapshot install with a full replay
|
|
25
|
+
* @returns One summary line, ending in a newline
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkMigrations(cwd: string, flags: InvokeFlags, options?: {
|
|
28
|
+
readonly provision?: boolean;
|
|
29
|
+
}): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Objects in `before` that `after` no longer satisfies.
|
|
32
|
+
*
|
|
33
|
+
* A table, column, constraint, or type that disappeared is a gap. A column
|
|
34
|
+
* whose type changed is a gap. Nullability tightened without a default is a
|
|
35
|
+
* gap. An added object is not.
|
|
36
|
+
*
|
|
37
|
+
* @param before - Previous release catalog
|
|
38
|
+
* @param after - Catalog the migration stores
|
|
39
|
+
* @returns One sentence per gap, in catalog order
|
|
40
|
+
*/
|
|
41
|
+
export declare function previousCatalogGaps(before: Catalog, after: Catalog): readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* Class of a migration, from step kinds.
|
|
44
|
+
*
|
|
45
|
+
* A step with no kind is planned from `before` to `after` instead. The
|
|
46
|
+
* `-- class:` comment is not read.
|
|
47
|
+
*
|
|
48
|
+
* @param plan - Parsed migration file
|
|
49
|
+
* @param before - Catalog before the migration
|
|
50
|
+
* @param after - Catalog the file stores
|
|
51
|
+
* @returns The strictest recomputed class
|
|
52
|
+
*/
|
|
53
|
+
export declare function recomputedMigrationClass(plan: MigrationPlan, before: Catalog, after: Catalog): MigrationClass;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a previous-catalog gap is allowed.
|
|
56
|
+
*
|
|
57
|
+
* The recomputed class must be `contract`. A file whose steps are classified
|
|
58
|
+
* `expand`, including a hand-edited `-- class:` line, does not allow the gap.
|
|
59
|
+
*
|
|
60
|
+
* @param plan - Parsed migration file
|
|
61
|
+
* @param before - Catalog before the migration
|
|
62
|
+
* @param after - Catalog the file stores
|
|
63
|
+
* @returns `true` when the gap may stand
|
|
64
|
+
*/
|
|
65
|
+
export declare function migrationAllowsPreviousGaps(plan: MigrationPlan, before: Catalog, after: Catalog): boolean;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expand, contract, or raw SQL.
|
|
3
|
+
*
|
|
4
|
+
* `unclassified` is only for a statement the planner did not classify.
|
|
5
|
+
* The protected-target policy treats that the same as `contract`.
|
|
6
|
+
*/
|
|
7
|
+
export type MigrationClass = "expand" | "contract" | "unclassified";
|
|
8
|
+
/**
|
|
9
|
+
* One operation the planner can emit.
|
|
10
|
+
*
|
|
11
|
+
* A new kind belongs in {@link STEP_CLASS}. The classification test lists
|
|
12
|
+
* every key, so a kind without a class fails that test.
|
|
13
|
+
*/
|
|
14
|
+
export declare const STEP_CLASS: {
|
|
15
|
+
readonly "create-table": "expand";
|
|
16
|
+
readonly "drop-table": "contract";
|
|
17
|
+
readonly "add-column": "expand";
|
|
18
|
+
readonly "add-column-required": "contract";
|
|
19
|
+
readonly "drop-column": "contract";
|
|
20
|
+
readonly "set-column-type": "contract";
|
|
21
|
+
readonly "set-not-null": "contract";
|
|
22
|
+
readonly "drop-not-null": "expand";
|
|
23
|
+
readonly "set-default": "expand";
|
|
24
|
+
readonly "drop-default": "contract";
|
|
25
|
+
readonly "add-identity": "expand";
|
|
26
|
+
readonly "drop-identity": "contract";
|
|
27
|
+
readonly "set-identity-always": "contract";
|
|
28
|
+
readonly "set-identity-by-default": "expand";
|
|
29
|
+
readonly "add-constraint": "expand";
|
|
30
|
+
readonly "validate-constraint": "expand";
|
|
31
|
+
readonly "drop-constraint": "contract";
|
|
32
|
+
readonly "drop-not-null-check": "expand";
|
|
33
|
+
readonly "rename-constraint": "contract";
|
|
34
|
+
readonly "widen-check": "expand";
|
|
35
|
+
readonly "narrow-check": "contract";
|
|
36
|
+
readonly "create-index": "expand";
|
|
37
|
+
readonly "drop-index": "contract";
|
|
38
|
+
readonly "rename-index": "contract";
|
|
39
|
+
readonly "create-sequence": "expand";
|
|
40
|
+
readonly "drop-sequence": "contract";
|
|
41
|
+
readonly "create-enum": "expand";
|
|
42
|
+
readonly "recreate-enum": "contract";
|
|
43
|
+
readonly "add-enum-value": "expand";
|
|
44
|
+
readonly "rename-enum": "contract";
|
|
45
|
+
readonly "drop-enum": "contract";
|
|
46
|
+
readonly "set-enum-column": "contract";
|
|
47
|
+
readonly "create-domain": "expand";
|
|
48
|
+
readonly "drop-domain": "contract";
|
|
49
|
+
readonly "add-domain-check": "expand";
|
|
50
|
+
readonly "validate-domain-check": "expand";
|
|
51
|
+
readonly "drop-domain-check": "contract";
|
|
52
|
+
readonly "rename-domain-check": "expand";
|
|
53
|
+
readonly "create-extension": "expand";
|
|
54
|
+
readonly "drop-extension": "contract";
|
|
55
|
+
readonly "update-extension": "expand";
|
|
56
|
+
readonly "move-extension": "contract";
|
|
57
|
+
readonly "create-function": "expand";
|
|
58
|
+
readonly "drop-function": "contract";
|
|
59
|
+
readonly "replace-function": "expand";
|
|
60
|
+
readonly "create-trigger": "expand";
|
|
61
|
+
readonly "drop-trigger": "contract";
|
|
62
|
+
readonly "create-view": "expand";
|
|
63
|
+
readonly "drop-view": "contract";
|
|
64
|
+
readonly "replace-view": "expand";
|
|
65
|
+
readonly "create-matview": "expand";
|
|
66
|
+
readonly "drop-matview": "contract";
|
|
67
|
+
readonly "refresh-matview": "expand";
|
|
68
|
+
readonly "create-role": "expand";
|
|
69
|
+
readonly "alter-role": "contract";
|
|
70
|
+
readonly grant: "expand";
|
|
71
|
+
readonly revoke: "contract";
|
|
72
|
+
readonly "grant-default": "expand";
|
|
73
|
+
readonly "revoke-default": "contract";
|
|
74
|
+
readonly "rename-table": "contract";
|
|
75
|
+
readonly "rename-column": "contract";
|
|
76
|
+
readonly "rename-sequence": "contract";
|
|
77
|
+
readonly "backfill-expand": "expand";
|
|
78
|
+
readonly "backfill-contract": "contract";
|
|
79
|
+
readonly "raw-sql": "unclassified";
|
|
80
|
+
};
|
|
81
|
+
/** A planner operation. */
|
|
82
|
+
export type StepKind = keyof typeof STEP_CLASS;
|
|
83
|
+
/** Every kind, in table order. */
|
|
84
|
+
export declare const STEP_KINDS: readonly StepKind[];
|
|
85
|
+
/**
|
|
86
|
+
* Class for one kind.
|
|
87
|
+
*
|
|
88
|
+
* @param kind - Planner operation
|
|
89
|
+
* @returns The class stored on the step
|
|
90
|
+
*/
|
|
91
|
+
export declare function classOf(kind: StepKind): MigrationClass;
|
|
92
|
+
/**
|
|
93
|
+
* Strictest class in a plan.
|
|
94
|
+
*
|
|
95
|
+
* `contract` wins over `unclassified`, which wins over `expand`. An empty
|
|
96
|
+
* list is `expand`.
|
|
97
|
+
*
|
|
98
|
+
* @param classes - One class per step
|
|
99
|
+
* @returns The class of the plan header
|
|
100
|
+
*/
|
|
101
|
+
export declare function strictestClass(classes: readonly MigrationClass[]): MigrationClass;
|
|
102
|
+
/**
|
|
103
|
+
* Reports whether a plan comment names a kind.
|
|
104
|
+
*
|
|
105
|
+
* @param value - Text after `-- kind: `
|
|
106
|
+
* @returns `true` when the text is a {@link StepKind}
|
|
107
|
+
*/
|
|
108
|
+
export declare function isStepKind(value: string): value is StepKind;
|
|
@@ -23,8 +23,8 @@ export declare function formatFailure(error: OkmError): string;
|
|
|
23
23
|
* Runs one `okm` invocation.
|
|
24
24
|
*
|
|
25
25
|
* `--version` stays in the bin. This function handles `build`, `check`,
|
|
26
|
-
* `generate`, `dev`, `push`, `ext list`, `ext check`, `doctor`, and
|
|
27
|
-
* `apply`, and `
|
|
26
|
+
* `generate`, `dev`, `push`, `seed`, `ext list`, `ext check`, `doctor`, and
|
|
27
|
+
* `migrate plan`, `apply`, `status`, and `check`.
|
|
28
28
|
*
|
|
29
29
|
* @param argv - Arguments after the program name
|
|
30
30
|
* @param io - Working directory and stdout. Defaults to the process
|
|
@@ -36,6 +36,28 @@ export type MigrateConfig = {
|
|
|
36
36
|
readonly lock?: number;
|
|
37
37
|
readonly statement?: number;
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* First migration id that `okm migrate check` lints.
|
|
41
|
+
*
|
|
42
|
+
* Files before this id in apply order are the adoption baseline: a project
|
|
43
|
+
* that turns the linter on later does not fail on old unreasoned drops.
|
|
44
|
+
* The id itself is linted. `okm migrate apply` still lints every pending
|
|
45
|
+
* migration.
|
|
46
|
+
*/
|
|
47
|
+
readonly lintFrom?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Batched backfill (D195).
|
|
50
|
+
*
|
|
51
|
+
* `batchSize` is what a new plan writes as `batch=`. A step header overrides
|
|
52
|
+
* it at apply time. `pauseMs` waits between committed batches. `statementTimeoutMs`
|
|
53
|
+
* is `statement_timeout` for each batch. Omitted fields use the built-in
|
|
54
|
+
* defaults: 1000 rows, no pause, and 30 seconds.
|
|
55
|
+
*/
|
|
56
|
+
readonly backfill?: {
|
|
57
|
+
readonly batchSize?: number;
|
|
58
|
+
readonly pauseMs?: number;
|
|
59
|
+
readonly statementTimeoutMs?: number;
|
|
60
|
+
};
|
|
39
61
|
/**
|
|
40
62
|
* Migration role and application role.
|
|
41
63
|
*
|
|
@@ -11,6 +11,9 @@ import { type InvokeFlags } from "./policy.js";
|
|
|
11
11
|
/**
|
|
12
12
|
* Explains one code, or lists the triggers on each table.
|
|
13
13
|
*
|
|
14
|
+
* A role check connects as the read-only `check` class. A protected target
|
|
15
|
+
* is allowed. The command does not change the database.
|
|
16
|
+
*
|
|
14
17
|
* @param cwd - Project directory
|
|
15
18
|
* @param code - Spec code, when the caller passed one
|
|
16
19
|
* @returns Text for stdout, including the trailing newline
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
* A database that has never been pushed has no `okm_meta` and is left alone.
|
|
5
5
|
* After push, the introspected catalog is planned back to the schema. Each
|
|
6
6
|
* view is reprinted on this server first, so a `pg_get_viewdef` spelling is
|
|
7
|
-
* not drift. Any remaining statement is OKM1520. `okm_meta
|
|
8
|
-
* are omitted by the planner.
|
|
7
|
+
* not drift. Any remaining statement is OKM1520. `okm_meta`, `okm_history`,
|
|
8
|
+
* and `okm_backfill` are omitted by the planner.
|
|
9
9
|
*/
|
|
10
|
+
import { type Sql } from "postgres";
|
|
10
11
|
import type { Catalog } from "../../contracts/catalog/types.js";
|
|
12
|
+
import { type CatalogQuery, type IntrospectOptions } from "../../dialects/pg/introspect.js";
|
|
11
13
|
import type { RolesInput } from "../../dialects/pg/role/index.js";
|
|
12
14
|
/**
|
|
13
15
|
* Refuses when the connected database differs from the schema.
|
|
@@ -17,3 +19,17 @@ import type { RolesInput } from "../../dialects/pg/role/index.js";
|
|
|
17
19
|
* @param roles - `defineConfig({ roles })`, when set
|
|
18
20
|
*/
|
|
19
21
|
export declare function assertAuthorDrift(url: string, author: Catalog, roles?: RolesInput): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Turns a postgres.js handle into the query shape introspection uses.
|
|
24
|
+
*
|
|
25
|
+
* @param sql - One connection
|
|
26
|
+
* @returns A {@link CatalogQuery}
|
|
27
|
+
*/
|
|
28
|
+
export declare function catalogQuery(sql: Sql): CatalogQuery;
|
|
29
|
+
/**
|
|
30
|
+
* Role names introspection should read, when the config names them.
|
|
31
|
+
*
|
|
32
|
+
* @param roles - `defineConfig({ roles })`, when set
|
|
33
|
+
* @returns The introspect option, or `undefined` when roles are not configured
|
|
34
|
+
*/
|
|
35
|
+
export declare function managedRoleOptions(roles: RolesInput | undefined): IntrospectOptions | undefined;
|