@intentius/sql-yodeler 0.0.0-stage → 0.1.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.
Files changed (264) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +110 -2
  3. package/dist/bin.d.ts +2 -0
  4. package/dist/bin.js +14 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chant/apply.d.ts +51 -0
  7. package/dist/chant/apply.js +104 -0
  8. package/dist/chant/apply.js.map +1 -0
  9. package/dist/chant/build.d.ts +34 -0
  10. package/dist/chant/build.js +48 -0
  11. package/dist/chant/build.js.map +1 -0
  12. package/dist/chant/drift.d.ts +44 -0
  13. package/dist/chant/drift.js +91 -0
  14. package/dist/chant/drift.js.map +1 -0
  15. package/dist/chant/plan.d.ts +45 -0
  16. package/dist/chant/plan.js +61 -0
  17. package/dist/chant/plan.js.map +1 -0
  18. package/dist/chant/process.d.ts +41 -0
  19. package/dist/chant/process.js +87 -0
  20. package/dist/chant/process.js.map +1 -0
  21. package/dist/cli.d.ts +4 -0
  22. package/dist/cli.js +59 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/command.d.ts +17 -0
  25. package/dist/command.js +13 -0
  26. package/dist/command.js.map +1 -0
  27. package/dist/commands/apply.d.ts +3 -0
  28. package/dist/commands/apply.js +306 -0
  29. package/dist/commands/apply.js.map +1 -0
  30. package/dist/commands/drift.d.ts +3 -0
  31. package/dist/commands/drift.js +38 -0
  32. package/dist/commands/drift.js.map +1 -0
  33. package/dist/commands/index.d.ts +3 -0
  34. package/dist/commands/index.js +22 -0
  35. package/dist/commands/index.js.map +1 -0
  36. package/dist/commands/init.d.ts +3 -0
  37. package/dist/commands/init.js +135 -0
  38. package/dist/commands/init.js.map +1 -0
  39. package/dist/commands/lint.d.ts +3 -0
  40. package/dist/commands/lint.js +193 -0
  41. package/dist/commands/lint.js.map +1 -0
  42. package/dist/commands/new.d.ts +3 -0
  43. package/dist/commands/new.js +168 -0
  44. package/dist/commands/new.js.map +1 -0
  45. package/dist/commands/plan.d.ts +3 -0
  46. package/dist/commands/plan.js +171 -0
  47. package/dist/commands/plan.js.map +1 -0
  48. package/dist/commands/rebase.d.ts +3 -0
  49. package/dist/commands/rebase.js +167 -0
  50. package/dist/commands/rebase.js.map +1 -0
  51. package/dist/commands/repair.d.ts +3 -0
  52. package/dist/commands/repair.js +117 -0
  53. package/dist/commands/repair.js.map +1 -0
  54. package/dist/commands/status.d.ts +7 -0
  55. package/dist/commands/status.js +54 -0
  56. package/dist/commands/status.js.map +1 -0
  57. package/dist/config/define.d.ts +50 -0
  58. package/dist/config/define.js +20 -0
  59. package/dist/config/define.js.map +1 -0
  60. package/dist/config/index.d.ts +11 -0
  61. package/dist/config/index.js +12 -0
  62. package/dist/config/index.js.map +1 -0
  63. package/dist/config/load.d.ts +17 -0
  64. package/dist/config/load.js +102 -0
  65. package/dist/config/load.js.map +1 -0
  66. package/dist/config/settings.d.ts +40 -0
  67. package/dist/config/settings.js +88 -0
  68. package/dist/config/settings.js.map +1 -0
  69. package/dist/env-args.d.ts +18 -0
  70. package/dist/env-args.js +62 -0
  71. package/dist/env-args.js.map +1 -0
  72. package/dist/history/client.d.ts +17 -0
  73. package/dist/history/client.js +19 -0
  74. package/dist/history/client.js.map +1 -0
  75. package/dist/history/ddl.d.ts +58 -0
  76. package/dist/history/ddl.js +100 -0
  77. package/dist/history/ddl.js.map +1 -0
  78. package/dist/history/index.d.ts +25 -0
  79. package/dist/history/index.js +26 -0
  80. package/dist/history/index.js.map +1 -0
  81. package/dist/history/lock.d.ts +100 -0
  82. package/dist/history/lock.js +224 -0
  83. package/dist/history/lock.js.map +1 -0
  84. package/dist/history/postgres/ddl.d.ts +22 -0
  85. package/dist/history/postgres/ddl.js +47 -0
  86. package/dist/history/postgres/ddl.js.map +1 -0
  87. package/dist/history/postgres/index.d.ts +12 -0
  88. package/dist/history/postgres/index.js +13 -0
  89. package/dist/history/postgres/index.js.map +1 -0
  90. package/dist/history/postgres/lock.d.ts +48 -0
  91. package/dist/history/postgres/lock.js +95 -0
  92. package/dist/history/postgres/lock.js.map +1 -0
  93. package/dist/history/postgres/store.d.ts +39 -0
  94. package/dist/history/postgres/store.js +68 -0
  95. package/dist/history/postgres/store.js.map +1 -0
  96. package/dist/history/rows.d.ts +96 -0
  97. package/dist/history/rows.js +73 -0
  98. package/dist/history/rows.js.map +1 -0
  99. package/dist/history/store.d.ts +72 -0
  100. package/dist/history/store.js +134 -0
  101. package/dist/history/store.js.map +1 -0
  102. package/dist/history/topology.d.ts +41 -0
  103. package/dist/history/topology.js +54 -0
  104. package/dist/history/topology.js.map +1 -0
  105. package/dist/index.d.ts +10 -0
  106. package/dist/index.js +10 -0
  107. package/dist/index.js.map +1 -0
  108. package/dist/init/adopt.d.ts +70 -0
  109. package/dist/init/adopt.js +138 -0
  110. package/dist/init/adopt.js.map +1 -0
  111. package/dist/init/baseline.d.ts +61 -0
  112. package/dist/init/baseline.js +88 -0
  113. package/dist/init/baseline.js.map +1 -0
  114. package/dist/init/files.d.ts +23 -0
  115. package/dist/init/files.js +77 -0
  116. package/dist/init/files.js.map +1 -0
  117. package/dist/init/index.d.ts +16 -0
  118. package/dist/init/index.js +17 -0
  119. package/dist/init/index.js.map +1 -0
  120. package/dist/init/live.d.ts +28 -0
  121. package/dist/init/live.js +45 -0
  122. package/dist/init/live.js.map +1 -0
  123. package/dist/lint/check.d.ts +81 -0
  124. package/dist/lint/check.js +208 -0
  125. package/dist/lint/check.js.map +1 -0
  126. package/dist/lint/config.d.ts +21 -0
  127. package/dist/lint/config.js +43 -0
  128. package/dist/lint/config.js.map +1 -0
  129. package/dist/lint/index.d.ts +31 -0
  130. package/dist/lint/index.js +43 -0
  131. package/dist/lint/index.js.map +1 -0
  132. package/dist/lint/project-rules.d.ts +19 -0
  133. package/dist/lint/project-rules.js +32 -0
  134. package/dist/lint/project-rules.js.map +1 -0
  135. package/dist/lint/report.d.ts +7 -0
  136. package/dist/lint/report.js +48 -0
  137. package/dist/lint/report.js.map +1 -0
  138. package/dist/lint/rules.d.ts +53 -0
  139. package/dist/lint/rules.js +42 -0
  140. package/dist/lint/rules.js.map +1 -0
  141. package/dist/lint/silences.d.ts +49 -0
  142. package/dist/lint/silences.js +91 -0
  143. package/dist/lint/silences.js.map +1 -0
  144. package/dist/lint/statements.d.ts +38 -0
  145. package/dist/lint/statements.js +120 -0
  146. package/dist/lint/statements.js.map +1 -0
  147. package/dist/migrations/checksum.d.ts +56 -0
  148. package/dist/migrations/checksum.js +72 -0
  149. package/dist/migrations/checksum.js.map +1 -0
  150. package/dist/migrations/create.d.ts +73 -0
  151. package/dist/migrations/create.js +119 -0
  152. package/dist/migrations/create.js.map +1 -0
  153. package/dist/migrations/directory.d.ts +119 -0
  154. package/dist/migrations/directory.js +225 -0
  155. package/dist/migrations/directory.js.map +1 -0
  156. package/dist/migrations/document.d.ts +83 -0
  157. package/dist/migrations/document.js +82 -0
  158. package/dist/migrations/document.js.map +1 -0
  159. package/dist/migrations/index.d.ts +20 -0
  160. package/dist/migrations/index.js +21 -0
  161. package/dist/migrations/index.js.map +1 -0
  162. package/dist/pr/comment.d.ts +32 -0
  163. package/dist/pr/comment.js +99 -0
  164. package/dist/pr/comment.js.map +1 -0
  165. package/dist/pr/forge.d.ts +50 -0
  166. package/dist/pr/forge.js +105 -0
  167. package/dist/pr/forge.js.map +1 -0
  168. package/dist/pr/model.d.ts +91 -0
  169. package/dist/pr/model.js +113 -0
  170. package/dist/pr/model.js.map +1 -0
  171. package/dist/pr/render.d.ts +27 -0
  172. package/dist/pr/render.js +246 -0
  173. package/dist/pr/render.js.map +1 -0
  174. package/dist/project.d.ts +62 -0
  175. package/dist/project.js +92 -0
  176. package/dist/project.js.map +1 -0
  177. package/dist/rebase/index.d.ts +121 -0
  178. package/dist/rebase/index.js +390 -0
  179. package/dist/rebase/index.js.map +1 -0
  180. package/dist/rebase/repair.d.ts +41 -0
  181. package/dist/rebase/repair.js +68 -0
  182. package/dist/rebase/repair.js.map +1 -0
  183. package/dist/replay/clickhouse.d.ts +33 -0
  184. package/dist/replay/clickhouse.js +106 -0
  185. package/dist/replay/clickhouse.js.map +1 -0
  186. package/dist/replay/index.d.ts +36 -0
  187. package/dist/replay/index.js +54 -0
  188. package/dist/replay/index.js.map +1 -0
  189. package/dist/replay/postgres.d.ts +31 -0
  190. package/dist/replay/postgres.js +140 -0
  191. package/dist/replay/postgres.js.map +1 -0
  192. package/dist/replay/replay.d.ts +114 -0
  193. package/dist/replay/replay.js +98 -0
  194. package/dist/replay/replay.js.map +1 -0
  195. package/dist/replay/report.d.ts +18 -0
  196. package/dist/replay/report.js +72 -0
  197. package/dist/replay/report.js.map +1 -0
  198. package/dist/steps/backfill.d.ts +128 -0
  199. package/dist/steps/backfill.js +307 -0
  200. package/dist/steps/backfill.js.map +1 -0
  201. package/dist/steps/index.d.ts +52 -0
  202. package/dist/steps/index.js +52 -0
  203. package/dist/steps/index.js.map +1 -0
  204. package/dist/steps/kinds.d.ts +101 -0
  205. package/dist/steps/kinds.js +143 -0
  206. package/dist/steps/kinds.js.map +1 -0
  207. package/dist/steps/postgres-migration.d.ts +95 -0
  208. package/dist/steps/postgres-migration.js +149 -0
  209. package/dist/steps/postgres-migration.js.map +1 -0
  210. package/dist/steps/rebuild.d.ts +97 -0
  211. package/dist/steps/rebuild.js +162 -0
  212. package/dist/steps/rebuild.js.map +1 -0
  213. package/dist/steps/run.d.ts +45 -0
  214. package/dist/steps/run.js +72 -0
  215. package/dist/steps/run.js.map +1 -0
  216. package/dist/topology/render.d.ts +17 -0
  217. package/dist/topology/render.js +17 -0
  218. package/dist/topology/render.js.map +1 -0
  219. package/dist/version.d.ts +2 -0
  220. package/dist/version.js +11 -0
  221. package/dist/version.js.map +1 -0
  222. package/dist/versioned/approval.d.ts +35 -0
  223. package/dist/versioned/approval.js +53 -0
  224. package/dist/versioned/approval.js.map +1 -0
  225. package/dist/versioned/clickhouse.d.ts +24 -0
  226. package/dist/versioned/clickhouse.js +34 -0
  227. package/dist/versioned/clickhouse.js.map +1 -0
  228. package/dist/versioned/digest.d.ts +77 -0
  229. package/dist/versioned/digest.js +69 -0
  230. package/dist/versioned/digest.js.map +1 -0
  231. package/dist/versioned/driver.d.ts +41 -0
  232. package/dist/versioned/driver.js +13 -0
  233. package/dist/versioned/driver.js.map +1 -0
  234. package/dist/versioned/engine.d.ts +152 -0
  235. package/dist/versioned/engine.js +277 -0
  236. package/dist/versioned/engine.js.map +1 -0
  237. package/dist/versioned/explain.d.ts +123 -0
  238. package/dist/versioned/explain.js +289 -0
  239. package/dist/versioned/explain.js.map +1 -0
  240. package/dist/versioned/index.d.ts +37 -0
  241. package/dist/versioned/index.js +54 -0
  242. package/dist/versioned/index.js.map +1 -0
  243. package/dist/versioned/op.d.ts +62 -0
  244. package/dist/versioned/op.js +153 -0
  245. package/dist/versioned/op.js.map +1 -0
  246. package/dist/versioned/postgres/catalog.d.ts +33 -0
  247. package/dist/versioned/postgres/catalog.js +34 -0
  248. package/dist/versioned/postgres/catalog.js.map +1 -0
  249. package/dist/versioned/postgres/driver.d.ts +69 -0
  250. package/dist/versioned/postgres/driver.js +113 -0
  251. package/dist/versioned/postgres/driver.js.map +1 -0
  252. package/dist/versioned/postgres/executor.d.ts +104 -0
  253. package/dist/versioned/postgres/executor.js +172 -0
  254. package/dist/versioned/postgres/executor.js.map +1 -0
  255. package/dist/versioned/postgres/index.d.ts +11 -0
  256. package/dist/versioned/postgres/index.js +12 -0
  257. package/dist/versioned/postgres/index.js.map +1 -0
  258. package/dist/versioned/report.d.ts +22 -0
  259. package/dist/versioned/report.js +81 -0
  260. package/dist/versioned/report.js.map +1 -0
  261. package/dist/versioned/state.d.ts +108 -0
  262. package/dist/versioned/state.js +153 -0
  263. package/dist/versioned/state.js.map +1 -0
  264. package/package.json +51 -4
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The replay check's result for people (text) and for tools (JSON), and its
3
+ * exit code.
4
+ */
5
+ import { type ReplayChange, type ReplayResult } from "./replay.js";
6
+ /** 0 when every migration replays to its recorded schema; 1 for a difference or a statement the server refused; 2 for a step the replay cannot run. */
7
+ export declare function replayExitCode(result: ReplayResult): number;
8
+ /** One difference: the object, the field, and the replay's value against the record's. */
9
+ export declare function describeChange(c: ReplayChange): string;
10
+ /** The failure in one paragraph, naming the first migration that fails; `undefined` when the replay matched. For `yodel lint`. */
11
+ export declare function replayFailure(result: ReplayResult): {
12
+ migration: string;
13
+ message: string;
14
+ } | undefined;
15
+ /** The result as text. */
16
+ export declare function renderReplay(result: ReplayResult): string;
17
+ /** The result as JSON. */
18
+ export declare function replayJson(result: ReplayResult): Record<string, unknown>;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The replay check's result for people (text) and for tools (JSON), and its
3
+ * exit code.
4
+ */
5
+ import { MIGRATIONS_DIR, MIGRATION_JSON, MIGRATION_SQL } from "../migrations/index.js";
6
+ import { UNREPLAYABLE } from "./replay.js";
7
+ /** 0 when every migration replays to its recorded schema; 1 for a difference or a statement the server refused; 2 for a step the replay cannot run. */
8
+ export function replayExitCode(result) {
9
+ if (result.outcome === "matches")
10
+ return 0;
11
+ if (result.outcome === "unreplayable")
12
+ return UNREPLAYABLE;
13
+ return 1;
14
+ }
15
+ const shown = (v) => (v === undefined ? "(absent)" : v);
16
+ /** One difference: the object, the field, and the replay's value against the record's. */
17
+ export function describeChange(c) {
18
+ return `${c.object}: ${c.field}: replay has ${shown(c.before)}, ${MIGRATION_JSON} records ${shown(c.after)} (${c.rule})`;
19
+ }
20
+ /** The failure in one paragraph, naming the first migration that fails; `undefined` when the replay matched. For `yodel lint`. */
21
+ export function replayFailure(result) {
22
+ const before = result.matched.length > 0 ? ` The ${String(result.matched.length)} migration${result.matched.length === 1 ? "" : "s"} before it replayed to ${result.matched.length === 1 ? "its" : "their"} recorded schema${result.matched.length === 1 ? "" : "s"}.` : "";
23
+ switch (result.outcome) {
24
+ case "matches":
25
+ return undefined;
26
+ case "differs": {
27
+ const m = result.migration;
28
+ const n = result.changes.length;
29
+ const skipped = m.skipped.length > 0 ? `\nSteps not replayed: ${m.skipped.join("; ")}.` : "";
30
+ return {
31
+ migration: m.id,
32
+ message: `${m.id}: replaying the history through it does not give the schema it records (${MIGRATIONS_DIR}/${m.id}/${MIGRATION_JSON}); ${String(n)} difference${n === 1 ? "" : "s"}:\n` +
33
+ result.changes.map((c) => ` ${describeChange(c)}`).join("\n") +
34
+ `\n${before.trim() ? `${before.trim()}\n` : ""}` +
35
+ `Its statements (the steps in ${MIGRATION_JSON}, which ${MIGRATION_SQL} shows) and its recorded schema disagree: make the statements produce the schema, or record the schema they produce, then yodel repair the migration where it is already applied.${skipped}`,
36
+ };
37
+ }
38
+ case "failed":
39
+ return {
40
+ migration: result.migration,
41
+ message: `${result.migration}: statement ${String(result.step)} failed in the replay: ${result.error}.${before} The history does not apply to an empty database as written.`,
42
+ };
43
+ case "unreplayable":
44
+ return {
45
+ migration: result.migration,
46
+ message: `${result.migration}: step ${String(result.step)} cannot be replayed: ${result.reason}.${before} The migrations from it on are not checked.`,
47
+ };
48
+ }
49
+ }
50
+ /** The result as text. */
51
+ export function renderReplay(result) {
52
+ const lines = [`Replay of ${String(result.total)} migration${result.total === 1 ? "" : "s"} (${result.dialect}) into ${result.where}`];
53
+ if (result.scope.length > 0)
54
+ lines.push(` ${result.dialect === "postgres" ? "schemas" : "databases"}: ${result.scope.join(", ")}${result.reset ? " (emptied first)" : ""}${result.kept ? " (left in place)" : ""}`);
55
+ for (const m of result.matched) {
56
+ const extra = [...m.ran.map((r) => `ran ${r}`), ...m.skipped.map((s) => `skipped ${s}`)];
57
+ lines.push(` ok ${m.id} (${String(m.statements)} statement${m.statements === 1 ? "" : "s"}${extra.length > 0 ? `; ${extra.join("; ")}` : ""})`);
58
+ }
59
+ const failure = replayFailure(result);
60
+ if (!failure) {
61
+ lines.push(result.total === 0 ? "No migrations to replay." : "Every migration replays to its recorded schema.");
62
+ return `${lines.join("\n")}\n`;
63
+ }
64
+ lines.push(` ${result.outcome.padEnd(7)} ${failure.migration}`, "", failure.message);
65
+ return `${lines.join("\n")}\n`;
66
+ }
67
+ /** The result as JSON. */
68
+ export function replayJson(result) {
69
+ const failure = replayFailure(result);
70
+ return { ...result, ok: result.outcome === "matches", ...(failure ? { message: failure.message } : {}) };
71
+ }
72
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/replay/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * A backfill step: a data migration the author writes, run inside the
3
+ * migration between the statements around it, resumable from receipts.
4
+ *
5
+ * The step names a module in the migration's directory (`backfill.ts` by
6
+ * default) that exports a chant Op as `op` (or as its default export). Each
7
+ * step of its phases is an `effect()` step: one batch of the backfill, with
8
+ * its own receipt. The nested steps run SQL on the environment's server with
9
+ * the `yodelSql` activity yodel provides, or any activity chant has:
10
+ *
11
+ * import { EffectReceipt } from "@intentius/chant";
12
+ * import { Op, activity, effect, phase } from "@intentius/chant/op";
13
+ *
14
+ * const months = ["202601", "202602", "202603"];
15
+ * const batch = (m: string) =>
16
+ * effect(EffectReceipt(`country-${m}`, { effect: `country/${m}`, flavor: "existence" }), [
17
+ * activity("yodelSql", { sql: `ALTER TABLE shop.orders UPDATE country = ... WHERE toYYYYMM(ts) = ${m}` }, "atMostOnce"),
18
+ * ]);
19
+ *
20
+ * export const op = Op({ name: "backfill-country", overview: "...", phases: [phase("Backfill", months.map(batch))] });
21
+ *
22
+ * How a run goes is chant's `effect()` cycle, run by chant's local executor:
23
+ * for each batch, read its receipt; skip the batch when the receipt is there
24
+ * (an earlier run finished it); otherwise run its steps and write the
25
+ * receipt last, only when they all succeeded. A run that fails or is killed
26
+ * part way leaves the finished batches' receipts, so the next `yodel apply`
27
+ * resumes at the first batch without one and never redoes a finished one.
28
+ * A batch that was running when it stopped runs again: make each batch safe
29
+ * to repeat (an idempotent `UPDATE`, or a delete of its rows before the
30
+ * insert), as the rebuild's backfill does per partition.
31
+ *
32
+ * The receipts are rows on the environment's server, addressed
33
+ * `yodel/<env>/<migration id>/<effect>`, so the same effect name in two
34
+ * migrations is two receipts:
35
+ *
36
+ * - ClickHouse: chant's receipts table (`chant_receipts.receipts`, the one
37
+ * `ClickHouseRebuildOp` uses).
38
+ * - Postgres: `<history schema>.__chant_receipts`, chant's Postgres receipt
39
+ * table (the one `PostgresMigrationOp` keeps in the migrated table's
40
+ * schema), in the schema yodel's history is in. Each batch runs in one
41
+ * transaction on the apply's connection, and its receipt is written last
42
+ * inside it: the batch's statements and its receipt commit together or
43
+ * not at all, so a run killed anywhere leaves no batch half done, and a
44
+ * resumed backfill runs no batch twice. A batch's statements must be ones
45
+ * Postgres runs in a transaction block (no `CREATE INDEX CONCURRENTLY`,
46
+ * no `VACUUM`). The environment's `lockTimeoutMs` and `scanTimeoutMs` are
47
+ * the batch's `lock_timeout` and `statement_timeout`, and `yodelSql`'s
48
+ * `settings` are set for the batch's transaction (`set_config(..., true)`).
49
+ *
50
+ * The module is one of the migration's own files: it counts in the
51
+ * migration's checksum, so the plan digest an approval binds covers it, and
52
+ * an edit after it ran is a checksum mismatch. It is imported from the
53
+ * migration's directory, so it imports packages only (no relative imports).
54
+ * Its Op has no gates (the migration's gate approves it).
55
+ */
56
+ import type { OpConfig } from "@intentius/chant/op/types";
57
+ import type { SqlDialect } from "@intentius/chant-lexicon-sql/dialects";
58
+ import type { ReceiptStore } from "@intentius/chant/op/receipt-store";
59
+ import type { StepRecord } from "@intentius/chant/op/local-executor";
60
+ import type { ClickHousePort } from "../history/index.js";
61
+ import type { PgClient } from "../history/postgres/index.js";
62
+ import type { PgTimeouts } from "../versioned/postgres/executor.js";
63
+ import { type RunOpOptions } from "./run.js";
64
+ import { type BackfillStep } from "./kinds.js";
65
+ /** The activity a backfill's steps run SQL with: `activity("yodelSql", { sql, settings? })`. */
66
+ export declare const SQL_ACTIVITY = "yodelSql";
67
+ /** The step `yodel new --backfill` adds to a migration. */
68
+ export declare function backfillStep(migrationName: string, file?: string): BackfillStep;
69
+ /** Text only the template holds: a backfill still carrying it has not been written. */
70
+ export declare const TEMPLATE_MARK = "write the backfill of";
71
+ /** What `yodel new --backfill <file>` writes at `<file>` when there is none yet. */
72
+ export declare function backfillTemplate(name: string, dialect?: SqlDialect): string;
73
+ /** The Op config a module exports as `op` or as its default export. */
74
+ export declare function exportedOp(mod: Record<string, unknown>): OpConfig | undefined;
75
+ /** What is wrong with a backfill's Op, if anything: every phase step is an effect() step, and there are no gates. */
76
+ export declare function backfillProblems(config: OpConfig): string[];
77
+ /** Imports a backfill module and returns its Op, or throws naming what is wrong. `text` busts the import cache when the file changed. */
78
+ export declare function loadBackfill(path: string, text?: string): Promise<OpConfig>;
79
+ export interface BackfillSummary {
80
+ op: "backfill";
81
+ file: string;
82
+ /** The Op's name. */
83
+ name: string;
84
+ /** Batches (effect steps) the Op has. */
85
+ effects: number;
86
+ /** Batches skipped: their receipt was there (an earlier run finished them). */
87
+ skipped: number;
88
+ /** Batches run, and their receipt written, in this run. */
89
+ ran: number;
90
+ }
91
+ /** A receipt store whose every effect is under `prefix`, so receipts of different migrations never meet. */
92
+ export declare function prefixedStore(store: ReceiptStore, prefix: string): ReceiptStore;
93
+ /** The receipt store on a ClickHouse server: chant's, in `chant_receipts.receipts`. */
94
+ export declare function serverReceipts(endpoint: {
95
+ url: string;
96
+ user?: string;
97
+ password?: string;
98
+ }, environment: string): Promise<ReceiptStore>;
99
+ /**
100
+ * The receipt store on a Postgres server: chant's, in `<schema>.__chant_receipts`,
101
+ * over `client` (so a receipt written inside a batch's transaction commits
102
+ * with it). Creates the table when it is not there.
103
+ */
104
+ export declare function postgresServerReceipts(client: PgClient, schema: string, environment: string): Promise<ReceiptStore>;
105
+ /** A backfill on Postgres: the connection its batches run on, and the environment's timeouts. */
106
+ export interface PgBackfillServer {
107
+ client: PgClient;
108
+ timeouts: Pick<PgTimeouts, "lockTimeoutMs" | "scanTimeoutMs">;
109
+ }
110
+ export interface RunBackfillOptions extends Omit<RunOpOptions, "activities" | "cwd"> {
111
+ projectDir: string;
112
+ migrationId: string;
113
+ /** The migration's directory. */
114
+ dir: string;
115
+ /** The module's text as the migration read it (and checksummed). */
116
+ text?: string | undefined;
117
+ /** The ClickHouse server the SQL runs on; or `postgres`. */
118
+ port?: ClickHousePort | undefined;
119
+ /** The Postgres server the SQL runs on, a transaction per batch; or `port`. */
120
+ postgres?: PgBackfillServer | undefined;
121
+ receipts: ReceiptStore;
122
+ log?: (line: string) => void;
123
+ }
124
+ /** Runs a backfill step to the end, counting batches as they skip or finish. */
125
+ export declare function runBackfill(file: string, run: RunBackfillOptions): Promise<{
126
+ summary: BackfillSummary;
127
+ records: StepRecord[];
128
+ }>;
@@ -0,0 +1,307 @@
1
+ /**
2
+ * A backfill step: a data migration the author writes, run inside the
3
+ * migration between the statements around it, resumable from receipts.
4
+ *
5
+ * The step names a module in the migration's directory (`backfill.ts` by
6
+ * default) that exports a chant Op as `op` (or as its default export). Each
7
+ * step of its phases is an `effect()` step: one batch of the backfill, with
8
+ * its own receipt. The nested steps run SQL on the environment's server with
9
+ * the `yodelSql` activity yodel provides, or any activity chant has:
10
+ *
11
+ * import { EffectReceipt } from "@intentius/chant";
12
+ * import { Op, activity, effect, phase } from "@intentius/chant/op";
13
+ *
14
+ * const months = ["202601", "202602", "202603"];
15
+ * const batch = (m: string) =>
16
+ * effect(EffectReceipt(`country-${m}`, { effect: `country/${m}`, flavor: "existence" }), [
17
+ * activity("yodelSql", { sql: `ALTER TABLE shop.orders UPDATE country = ... WHERE toYYYYMM(ts) = ${m}` }, "atMostOnce"),
18
+ * ]);
19
+ *
20
+ * export const op = Op({ name: "backfill-country", overview: "...", phases: [phase("Backfill", months.map(batch))] });
21
+ *
22
+ * How a run goes is chant's `effect()` cycle, run by chant's local executor:
23
+ * for each batch, read its receipt; skip the batch when the receipt is there
24
+ * (an earlier run finished it); otherwise run its steps and write the
25
+ * receipt last, only when they all succeeded. A run that fails or is killed
26
+ * part way leaves the finished batches' receipts, so the next `yodel apply`
27
+ * resumes at the first batch without one and never redoes a finished one.
28
+ * A batch that was running when it stopped runs again: make each batch safe
29
+ * to repeat (an idempotent `UPDATE`, or a delete of its rows before the
30
+ * insert), as the rebuild's backfill does per partition.
31
+ *
32
+ * The receipts are rows on the environment's server, addressed
33
+ * `yodel/<env>/<migration id>/<effect>`, so the same effect name in two
34
+ * migrations is two receipts:
35
+ *
36
+ * - ClickHouse: chant's receipts table (`chant_receipts.receipts`, the one
37
+ * `ClickHouseRebuildOp` uses).
38
+ * - Postgres: `<history schema>.__chant_receipts`, chant's Postgres receipt
39
+ * table (the one `PostgresMigrationOp` keeps in the migrated table's
40
+ * schema), in the schema yodel's history is in. Each batch runs in one
41
+ * transaction on the apply's connection, and its receipt is written last
42
+ * inside it: the batch's statements and its receipt commit together or
43
+ * not at all, so a run killed anywhere leaves no batch half done, and a
44
+ * resumed backfill runs no batch twice. A batch's statements must be ones
45
+ * Postgres runs in a transaction block (no `CREATE INDEX CONCURRENTLY`,
46
+ * no `VACUUM`). The environment's `lockTimeoutMs` and `scanTimeoutMs` are
47
+ * the batch's `lock_timeout` and `statement_timeout`, and `yodelSql`'s
48
+ * `settings` are set for the batch's transaction (`set_config(..., true)`).
49
+ *
50
+ * The module is one of the migration's own files: it counts in the
51
+ * migration's checksum, so the plan digest an approval binds covers it, and
52
+ * an edit after it ran is a checksum mismatch. It is imported from the
53
+ * migration's directory, so it imports packages only (no relative imports).
54
+ * Its Op has no gates (the migration's gate approves it).
55
+ */
56
+ import { existsSync, readFileSync } from "node:fs";
57
+ import { createHash } from "node:crypto";
58
+ import { join } from "node:path";
59
+ import { pathToFileURL } from "node:url";
60
+ import { YodelError } from "../project.js";
61
+ import { gatesOf, runOp, StepOpFailed } from "./run.js";
62
+ import { BACKFILL_FILE, BACKFILL_OP } from "./kinds.js";
63
+ /** The activity a backfill's steps run SQL with: `activity("yodelSql", { sql, settings? })`. */
64
+ export const SQL_ACTIVITY = "yodelSql";
65
+ /** The step `yodel new --backfill` adds to a migration. */
66
+ export function backfillStep(migrationName, file = BACKFILL_FILE) {
67
+ return {
68
+ kind: "op",
69
+ object: "backfill",
70
+ type: "Yodel::Backfill",
71
+ name: migrationName,
72
+ op: BACKFILL_OP,
73
+ importPath: `./${file}`,
74
+ options: { file },
75
+ declaration: `the Op exported by ${file} in this migration's directory`,
76
+ changes: [],
77
+ detail: `a data backfill: the effect() steps of the Op in ${file}, each a batch with its own receipt; yodel apply runs it after the statements before it and resumes it from its receipts`,
78
+ };
79
+ }
80
+ /** Text only the template holds: a backfill still carrying it has not been written. */
81
+ export const TEMPLATE_MARK = "write the backfill of";
82
+ /** What `yodel new --backfill <file>` writes at `<file>` when there is none yet. */
83
+ export function backfillTemplate(name, dialect = "clickhouse") {
84
+ const todo = dialect === "postgres"
85
+ ? `DO $$ BEGIN RAISE EXCEPTION 'write the backfill of ${name} for batch \${b}'; END $$`
86
+ : `SELECT throwIf(1, 'write the backfill of ${name} for batch \${b}')`;
87
+ return `/**
88
+ * The backfill of migration ${name}. yodel apply runs the Op this file
89
+ * exports, after the migration's statements before the step and before
90
+ * those after it. Each effect() is one batch with its own receipt: a rerun
91
+ * after a failure skips the batches that finished. ${dialect === "postgres"
92
+ ? `Each batch is one transaction
93
+ * with its receipt, so a batch that was running when a run stopped left
94
+ * nothing and runs again whole.`
95
+ : `Make each batch safe to
96
+ * run twice (the one running when a run stopped runs again).`}
97
+ *
98
+ * This file is copied into the migration's directory and counts in its
99
+ * checksum. Import packages only, no relative paths. No gates: the
100
+ * migration's own gate approves it.
101
+ */
102
+ import { EffectReceipt } from "@intentius/chant";
103
+ import { Op, activity, effect, phase } from "@intentius/chant/op";
104
+
105
+ // The batches: months, id ranges, partitions.
106
+ const batches: string[] = ["202601", "202602"];
107
+
108
+ const batch = (b: string) =>
109
+ effect(EffectReceipt(\`${name}-\${b}\`, { effect: \`${name}/\${b}\`, flavor: "existence" }), [
110
+ // TODO: the SQL for one batch.
111
+ activity("${SQL_ACTIVITY}", { sql: \`${todo}\` }, "atMostOnce"),
112
+ ]);
113
+
114
+ export const op = Op({
115
+ name: "backfill-${name}",
116
+ overview: "Backfill for migration ${name}",
117
+ phases: [phase("Backfill", batches.map(batch))],
118
+ });
119
+ `;
120
+ }
121
+ const isRecord = (v) => typeof v === "object" && v !== null;
122
+ /** The Op config a module exports as `op` or as its default export. */
123
+ export function exportedOp(mod) {
124
+ for (const value of [mod.op, mod.default]) {
125
+ const props = isRecord(value) ? value.props : undefined;
126
+ if (isRecord(props) && typeof props.name === "string" && Array.isArray(props.phases))
127
+ return props;
128
+ }
129
+ return undefined;
130
+ }
131
+ /** What is wrong with a backfill's Op, if anything: every phase step is an effect() step, and there are no gates. */
132
+ export function backfillProblems(config) {
133
+ const problems = [];
134
+ const steps = config.phases.flatMap((p) => p.steps.map((s) => ({ phase: p.name, s })));
135
+ if (steps.length === 0)
136
+ problems.push("its Op has no steps");
137
+ for (const { phase, s } of steps) {
138
+ if (s.kind !== "effect")
139
+ problems.push(`phase "${phase}" has a ${s.kind === "gate" ? `gate (${s.gate ?? "unnamed"})` : `${s.fn} step`} outside an effect(): every step of a backfill is an effect() with its receipt, so a rerun resumes`);
140
+ }
141
+ const gates = gatesOf(config);
142
+ if (gates.length > 0)
143
+ problems.push(`it has a gate (${gates.join(", ")}); the migration's own gate approves the backfill`);
144
+ return problems;
145
+ }
146
+ /** Imports a backfill module and returns its Op, or throws naming what is wrong. `text` busts the import cache when the file changed. */
147
+ export async function loadBackfill(path, text) {
148
+ if (!existsSync(path))
149
+ throw new YodelError(`${path}: no such file; the backfill step runs the Op it exports`);
150
+ const body = text ?? readFileSync(path, "utf8");
151
+ const version = createHash("sha256").update(body).digest("hex").slice(0, 16);
152
+ let mod;
153
+ try {
154
+ mod = (await import(`${pathToFileURL(path).href}?yodel=${version}`));
155
+ }
156
+ catch (e) {
157
+ throw new YodelError(`${path}: could not load it: ${e instanceof Error ? e.message : String(e)}`);
158
+ }
159
+ const config = exportedOp(mod);
160
+ if (!config)
161
+ throw new YodelError(`${path} exports no Op (export const op = Op({ ... }), or a default export)`);
162
+ const problems = backfillProblems(config);
163
+ if (problems.length > 0)
164
+ throw new YodelError(`${path}: ${problems.join("; ")}`);
165
+ return config;
166
+ }
167
+ /** A receipt store whose every effect is under `prefix`, so receipts of different migrations never meet. */
168
+ export function prefixedStore(store, prefix) {
169
+ return {
170
+ read: (r) => store.read({ ...r, effect: `${prefix}${r.effect}` }),
171
+ write: (r, expectation) => store.write({ ...r, effect: `${prefix}${r.effect}` }, expectation),
172
+ };
173
+ }
174
+ /** The receipt store on a ClickHouse server: chant's, in `chant_receipts.receipts`. */
175
+ export async function serverReceipts(endpoint, environment) {
176
+ const { clickhouseReceiptStore } = await import("@intentius/chant-lexicon-sql/clickhouse/rebuild/receipts");
177
+ return clickhouseReceiptStore(endpoint, { stack: "yodel", env: environment });
178
+ }
179
+ /**
180
+ * The receipt store on a Postgres server: chant's, in `<schema>.__chant_receipts`,
181
+ * over `client` (so a receipt written inside a batch's transaction commits
182
+ * with it). Creates the table when it is not there.
183
+ */
184
+ export async function postgresServerReceipts(client, schema, environment) {
185
+ const { postgresReceiptStore } = await import("@intentius/chant-lexicon-sql/postgres/migrate/receipts");
186
+ // The store never closes the apply's connection.
187
+ const store = postgresReceiptStore({ query: (sql, params) => client.query(sql, params), end: () => Promise.resolve() }, schema, { stack: "yodel", env: environment });
188
+ await store.ensure();
189
+ return store;
190
+ }
191
+ function clickhouseBatches(port) {
192
+ return {
193
+ next: () => Promise.resolve(),
194
+ sql: async (sql, settings) => (await port.query(sql, settings ? { settings } : {})).length,
195
+ done: () => Promise.resolve(),
196
+ abandon: () => Promise.resolve(),
197
+ };
198
+ }
199
+ /** Each batch in one transaction, begun at its first statement and committed after its receipt. */
200
+ function postgresBatches(server) {
201
+ const { client, timeouts } = server;
202
+ let open = false;
203
+ /** Why this batch's transaction was rolled back: its later statements (an activity's retry) do not run without the earlier ones. */
204
+ let lost;
205
+ const rollback = async () => {
206
+ if (!open)
207
+ return;
208
+ open = false;
209
+ await client.query("ROLLBACK").catch(() => undefined);
210
+ };
211
+ return {
212
+ async next() {
213
+ await rollback();
214
+ lost = undefined;
215
+ },
216
+ async sql(sql, settings) {
217
+ if (lost !== undefined)
218
+ throw new Error(`this batch's transaction was rolled back (${lost}); the batch runs again whole on the next yodel apply`);
219
+ try {
220
+ if (!open) {
221
+ await client.query("BEGIN");
222
+ open = true;
223
+ await client.query(`SET LOCAL lock_timeout = '${String(Math.max(0, Math.floor(timeouts.lockTimeoutMs)))}ms'`);
224
+ await client.query(`SET LOCAL statement_timeout = '${String(Math.max(0, Math.floor(timeouts.scanTimeoutMs)))}ms'`);
225
+ }
226
+ for (const [name, value] of Object.entries(settings ?? {}))
227
+ await client.query("SELECT pg_catalog.set_config($1, $2, true)", [name, value]);
228
+ return (await client.query(sql)).length;
229
+ }
230
+ catch (e) {
231
+ lost = e instanceof Error ? e.message : String(e);
232
+ await rollback();
233
+ throw e;
234
+ }
235
+ },
236
+ async done() {
237
+ if (!open)
238
+ return;
239
+ open = false;
240
+ await client.query("COMMIT");
241
+ },
242
+ abandon: rollback,
243
+ };
244
+ }
245
+ /** Runs a backfill step to the end, counting batches as they skip or finish. */
246
+ export async function runBackfill(file, run) {
247
+ const config = await loadBackfill(join(run.dir, file), run.text);
248
+ const summary = { op: "backfill", file, name: config.name, effects: config.phases.reduce((n, p) => n + p.steps.length, 0), skipped: 0, ran: 0 };
249
+ const session = run.postgres ? postgresBatches(run.postgres) : run.port ? clickhouseBatches(run.port) : undefined;
250
+ if (!session)
251
+ throw new YodelError("a backfill step needs the environment's server to run its SQL on, and has none");
252
+ const { receiptActivities } = await import("@intentius/chant/op/receipt-store");
253
+ const store = receiptActivities(prefixedStore(run.receipts, `${run.migrationId}/`));
254
+ const activities = {
255
+ receiptRead: async (args) => {
256
+ // A new batch: nothing of an earlier one is left open.
257
+ await session.next();
258
+ const r = await store.receiptRead(args);
259
+ if (r.applied)
260
+ summary.skipped++;
261
+ return r;
262
+ },
263
+ receiptWrite: async (args) => {
264
+ let r;
265
+ try {
266
+ r = await store.receiptWrite(args);
267
+ await session.done();
268
+ }
269
+ catch (e) {
270
+ await session.abandon();
271
+ throw e;
272
+ }
273
+ summary.ran++;
274
+ return r;
275
+ },
276
+ receiptStaleness: (args) => store.receiptStaleness(args),
277
+ [SQL_ACTIVITY]: async (args) => {
278
+ const sql = args.sql;
279
+ if (typeof sql !== "string" || sql.trim() === "")
280
+ throw new Error(`${SQL_ACTIVITY}: needs { sql: "<statement>" }`);
281
+ const settings = isRecord(args.settings) ? Object.fromEntries(Object.entries(args.settings).map(([k, v]) => [k, String(v)])) : undefined;
282
+ run.log?.(` ${sql.split("\n")[0] ?? ""}`);
283
+ return { rows: await session.sql(sql, settings) };
284
+ },
285
+ };
286
+ try {
287
+ const records = await runOp(config, {
288
+ cwd: run.projectDir,
289
+ environment: run.environment,
290
+ activities,
291
+ ...(run.base ? { base: run.base } : {}),
292
+ ...(run.signal ? { signal: run.signal } : {}),
293
+ ...(run.onRecord ? { onRecord: run.onRecord } : {}),
294
+ });
295
+ return { summary, records };
296
+ }
297
+ catch (e) {
298
+ if (e instanceof StepOpFailed)
299
+ e.summary = summary;
300
+ throw e;
301
+ }
302
+ finally {
303
+ // A batch stopped part way (a failed step that was not ours, an abort) leaves nothing behind.
304
+ await session.abandon();
305
+ }
306
+ }
307
+ //# sourceMappingURL=backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfill.js","sourceRoot":"","sources":["../../src/steps/backfill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Op steps inside a migration (#12): what `yodel apply` runs besides SQL.
3
+ *
4
+ * planStep(step) statement, rebuild, backfill, or refused (with why)
5
+ * stepSha(step, files) the hash a step's history rows record
6
+ * runRebuild(...) a ClickHouseRebuildOp step (./rebuild.ts)
7
+ * runBackfill(...) a backfill step, an Op of effect() batches (./backfill.ts)
8
+ * runPgMigration(...) a PostgresMigrationOp step (./postgres-migration.ts)
9
+ * defaultStepRunner what the versioned apply calls for an Op step
10
+ */
11
+ import type { ReceiptStore } from "@intentius/chant/op/receipt-store";
12
+ import type { ClickHouseEndpoint } from "@intentius/chant-lexicon-sql/clickhouse/http";
13
+ import type { ClickHousePort } from "../history/index.js";
14
+ import type { Migration } from "../migrations/index.js";
15
+ import { type PgBackfillServer } from "./backfill.js";
16
+ import type { StepPlan } from "./kinds.js";
17
+ export * from "./kinds.js";
18
+ export * from "./run.js";
19
+ export * from "./rebuild.js";
20
+ export * from "./backfill.js";
21
+ export * from "./postgres-migration.js";
22
+ /** What running one Op step needs. */
23
+ export interface StepContext {
24
+ environment: string;
25
+ /** The project directory, which holds chant.config.ts. */
26
+ projectDir: string;
27
+ /** The ClickHouse server a backfill's SQL runs on; absent on Postgres. */
28
+ port?: ClickHousePort | undefined;
29
+ /** Postgres: the apply's connection a backfill's SQL runs on, its timeouts, and the schema its receipts are kept in (the history's). */
30
+ postgres?: (PgBackfillServer & {
31
+ receiptsSchema: string;
32
+ }) | undefined;
33
+ /** Postgres: the environment's lock timeout, for a PostgresMigrationOp step whose options set none. */
34
+ lockTimeoutMs?: number | undefined;
35
+ /** The server, for chant's receipt store; the port is enough for everything else. */
36
+ endpoint?: ClickHouseEndpoint | undefined;
37
+ migration: Migration;
38
+ index: number;
39
+ plan: Exclude<StepPlan, {
40
+ run: "statement";
41
+ } | {
42
+ run: "refused";
43
+ }>;
44
+ log: (line: string) => void;
45
+ /** A backfill's receipt store; default chant's on the server. */
46
+ receipts?: ReceiptStore | undefined;
47
+ }
48
+ /** Runs one Op step to the end and returns the summary its history row records. Throws `StepOpFailed` when it stops part way. */
49
+ export type StepRunner = (ctx: StepContext) => Promise<{
50
+ summary: unknown;
51
+ }>;
52
+ export declare const defaultStepRunner: StepRunner;