@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,153 @@
1
+ /**
2
+ * The approval: a versioned apply runs as a chant Op, behind a gate bound to
3
+ * the plan digest, the same machinery #6's declarative apply runs through.
4
+ *
5
+ * The project declares one Op per environment:
6
+ *
7
+ * Plan shell("yodel apply <env> --digest", { id: "plan" })
8
+ * prints the digest of the pending migrations and the live schema
9
+ * Approve gate(..., { plan: stepOutput(plan, "stdout") })
10
+ * a resolution counts only for that digest (`chant approve <op> <gate> --plan <digest>`)
11
+ * Apply shell("yodel apply <env> --execute", { env: { YODEL_APPROVED_PLAN: stepOutput(plan, "stdout") } })
12
+ * takes the lock, computes the digest again, refuses if it moved, applies
13
+ *
14
+ * `yodel apply <env>` runs it with `chant run <op>`, as #6 runs an ApplyOp.
15
+ * The gate ledger is chant's (the `chant/lifecycle` branch); yodel keeps no
16
+ * approval store of its own. The digest is checked twice: by the gate, on
17
+ * every run, against the approval; and by the Apply step, under the lock,
18
+ * against the digest the gate passed, so a change between the two (another
19
+ * runner applying, a table altered out of band) refuses too.
20
+ *
21
+ * `chant run <op>` run directly (in CI) needs `yodel` on PATH, e.g.
22
+ * `PATH="$PWD/node_modules/.bin:$PATH"`. When `yodel apply` runs it, yodel
23
+ * puts a `yodel` that runs itself first on the child's PATH.
24
+ */
25
+ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
26
+ import { tmpdir } from "node:os";
27
+ import { delimiter, extname, join } from "node:path";
28
+ import { fileURLToPath } from "node:url";
29
+ import { YodelError } from "../project.js";
30
+ import { chantInstall, runChant } from "../chant/process.js";
31
+ import { GATED, readApplyRun } from "../chant/apply.js";
32
+ /** The env var the Apply step reads the approved digest from. */
33
+ export const APPROVED_PLAN_ENV = "YODEL_APPROVED_PLAN";
34
+ /** Passes `--allow-out-of-order` through `chant run` to the Op's steps. */
35
+ export const ALLOW_OUT_OF_ORDER_ENV = "YODEL_ALLOW_OUT_OF_ORDER";
36
+ const command = (verb) => new RegExp(`^(?:npx\\s+)?yodel\\s+apply\\s+(\\S+)\\s+${verb}(?:\\s|$)`);
37
+ const DIGEST = command("--digest");
38
+ const EXECUTE = command("--execute");
39
+ const steps = (config) => config.phases.flatMap((p) => p.steps);
40
+ function shellCmd(step) {
41
+ if (step.kind !== "activity" || step.fn !== "shellCmd")
42
+ return undefined;
43
+ const cmd = step.args?.cmd;
44
+ return typeof cmd === "string" ? { cmd: cmd.trim(), ...(step.id ? { id: step.id } : {}) } : undefined;
45
+ }
46
+ /** The parts of a migrations Op for `env`, when `config` is one. */
47
+ export function migrateOpParts(config, env) {
48
+ const all = steps(config);
49
+ const plan = all.map(shellCmd).find((s) => s && DIGEST.exec(s.cmd)?.[1] === env);
50
+ const execute = all.map(shellCmd).find((s) => s && EXECUTE.exec(s.cmd)?.[1] === env);
51
+ if (!plan || !execute)
52
+ return undefined;
53
+ const gate = all.find((s) => {
54
+ if (s.kind !== "gate" || s.plan === undefined || typeof s.plan === "string")
55
+ return false;
56
+ return s.plan.step === plan.id;
57
+ });
58
+ return gate?.kind === "gate" ? { gate: gate.gate, quorum: gate.approval !== undefined } : { gate: undefined, quorum: false };
59
+ }
60
+ /** How to declare the Op `yodel apply` looks for. */
61
+ export function migrateOpExample(env) {
62
+ return [
63
+ `// ops/migrate-${env}.op.ts`,
64
+ `import { Op, phase, gate, shell, stepOutput } from "@intentius/chant/op";`,
65
+ ``,
66
+ `const plan = shell("yodel apply ${env} --digest", { id: "plan" });`,
67
+ `const digest = stepOutput(plan, "stdout");`,
68
+ ``,
69
+ `export const op = Op({`,
70
+ ` name: "migrate-${env}",`,
71
+ ` overview: "Apply the pending migrations to ${env}, behind an approval bound to their plan digest",`,
72
+ ` phases: [`,
73
+ ` phase("Plan", [plan]),`,
74
+ ` phase("Approve", [gate("approve-migrate-${env}", { plan: digest })]),`,
75
+ ` phase("Apply", [shell("yodel apply ${env} --execute", { env: { ${APPROVED_PLAN_ENV}: digest }, timeout: "2h" })]),`,
76
+ ` ],`,
77
+ `});`,
78
+ ].join("\n");
79
+ }
80
+ /** Picks the migrations Op for `env` from discovered Ops. Pure. */
81
+ export function selectMigrateOp(ops, project, env, name) {
82
+ const found = [...ops.values()].flatMap((o) => {
83
+ const parts = migrateOpParts(o.config, env.name);
84
+ return parts ? [{ ...o, parts }] : [];
85
+ });
86
+ const chosen = name ? found.filter((o) => o.config.name === name) : found;
87
+ if (chosen.length === 0) {
88
+ if (name && ops.has(name))
89
+ throw new YodelError(`Op "${name}" is not a migrations Op for environment ${env.name} (a Plan step running "yodel apply ${env.name} --digest", a gate bound to it, an Apply step running "yodel apply ${env.name} --execute")`);
90
+ throw new YodelError(name
91
+ ? `no Op named "${name}" in ${project.dir}`
92
+ : `no Op applies the migrations to environment ${env.name} in ${project.dir}; declare one, for example:\n\n${migrateOpExample(env.name)}`);
93
+ }
94
+ if (chosen.length > 1)
95
+ throw new YodelError(`${String(chosen.length)} Ops apply the migrations to ${env.name} (${chosen.map((o) => o.config.name).join(", ")}); pick one with --op <name>`);
96
+ const [op] = chosen;
97
+ if (!op.parts.gate) {
98
+ throw new YodelError(`Op "${op.config.name}" (${op.filePath}) has no approval gate bound to its Plan step's digest, and yodel only applies behind one; give it gate(..., { plan: stepOutput(plan, "stdout") })`);
99
+ }
100
+ return { name: op.config.name, filePath: op.filePath, gate: op.parts.gate, quorum: op.parts.quorum };
101
+ }
102
+ export async function findMigrateOp(project, env, name) {
103
+ const { discoverOps } = await import("@intentius/chant/op/discover");
104
+ let found;
105
+ try {
106
+ found = await discoverOps({ cwd: project.dir });
107
+ }
108
+ catch (e) {
109
+ throw new YodelError(`could not discover Ops in ${project.dir}: ${e instanceof Error ? e.message : String(e)}`);
110
+ }
111
+ if (found.errors.length > 0 && found.ops.size === 0)
112
+ throw new YodelError(`could not discover Ops in ${project.dir}:\n${found.errors.join("\n")}`);
113
+ return selectMigrateOp(found.ops, project, env, name);
114
+ }
115
+ /** This yodel's own entry point: `bin.js` when built, `bin.ts` from source. */
116
+ function selfEntry() {
117
+ const here = fileURLToPath(import.meta.url);
118
+ return fileURLToPath(new URL(`../bin${extname(here)}`, import.meta.url));
119
+ }
120
+ /** A directory holding a `yodel` that runs this yodel, for a child's PATH. */
121
+ export function yodelShim() {
122
+ const dir = mkdtempSync(join(tmpdir(), "yodel-shim-"));
123
+ const q = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
124
+ const script = `#!/bin/sh\nexec ${q(process.execPath)} --import ${q(chantInstall().loader)} ${q(selfEntry())} "$@"\n`;
125
+ const path = join(dir, "yodel");
126
+ writeFileSync(path, script);
127
+ chmodSync(path, 0o755);
128
+ return { dir, dispose: () => { rmSync(dir, { recursive: true, force: true }); } };
129
+ }
130
+ /** Runs the migrations Op with `chant run`, with this yodel on PATH and `--allow-out-of-order` passed through. */
131
+ export async function runMigrateOp(project, op, options) {
132
+ const shim = yodelShim();
133
+ try {
134
+ const base = options.env ?? process.env;
135
+ // The approved digest only ever comes from the gate, never from the caller's environment.
136
+ const rest = Object.fromEntries(Object.entries(base).filter(([k]) => k !== APPROVED_PLAN_ENV && k !== ALLOW_OUT_OF_ORDER_ENV));
137
+ const env = {
138
+ ...rest,
139
+ PATH: [shim.dir, base.PATH ?? ""].filter(Boolean).join(delimiter),
140
+ ...(options.allowOutOfOrder ? { [ALLOW_OUT_OF_ORDER_ENV]: "1" } : {}),
141
+ };
142
+ const result = await runChant(["run", op.name, "--gated-exit", String(GATED)], {
143
+ cwd: project.dir,
144
+ env,
145
+ ...(options.onOutput ? { onStdout: options.onOutput, onStderr: options.onOutput } : {}),
146
+ });
147
+ return readApplyRun(result.code, `${result.stdout}\n${result.stderr}`);
148
+ }
149
+ finally {
150
+ shim.dispose();
151
+ }
152
+ }
153
+ //# sourceMappingURL=op.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"op.js","sourceRoot":"","sources":["../../src/versioned/op.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The live-schema fingerprint a Postgres plan digest covers: every object
3
+ * chant's catalog reader sees in the environment's schemas
4
+ * (`sql.profiles.<env>.schemas`; every schema but the server's own when it
5
+ * names none), by its type, schema, name, the statements that create it as
6
+ * the server prints them, and its comment. The history schema is left out,
7
+ * so recording a run does not move the digest, and so are chant's own
8
+ * working objects (a migration's new column, its receipts), which the reader
9
+ * skips.
10
+ *
11
+ * It is the same reader `chant sql plan` and the applier diff against, so a
12
+ * change the plan would see (a column added out of band, an index dropped, a
13
+ * default changed) is a new fingerprint, and an approval given before it
14
+ * does not carry over.
15
+ */
16
+ import type { PgClient } from "../../history/postgres/index.js";
17
+ export interface PgLiveScope {
18
+ /** The schemas in scope; every schema but the server's own when undefined. */
19
+ schemas: string[] | undefined;
20
+ /** Left out of the fingerprint. */
21
+ historySchema: string;
22
+ }
23
+ export interface PgCatalogEntry {
24
+ type: string;
25
+ schema: string;
26
+ name: string;
27
+ statement: string;
28
+ comment: string;
29
+ }
30
+ /** Reads what the fingerprint covers, in a stable order. */
31
+ export declare function readPgCatalog(client: PgClient, scope: PgLiveScope): Promise<PgCatalogEntry[]>;
32
+ /** `sha256:<hex>` over the catalog's canonical JSON. */
33
+ export declare function pgCatalogFingerprint(entries: readonly PgCatalogEntry[]): string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The live-schema fingerprint a Postgres plan digest covers: every object
3
+ * chant's catalog reader sees in the environment's schemas
4
+ * (`sql.profiles.<env>.schemas`; every schema but the server's own when it
5
+ * names none), by its type, schema, name, the statements that create it as
6
+ * the server prints them, and its comment. The history schema is left out,
7
+ * so recording a run does not move the digest, and so are chant's own
8
+ * working objects (a migration's new column, its receipts), which the reader
9
+ * skips.
10
+ *
11
+ * It is the same reader `chant sql plan` and the applier diff against, so a
12
+ * change the plan would see (a column added out of band, an index dropped, a
13
+ * default changed) is a new fingerprint, and an approval given before it
14
+ * does not carry over.
15
+ */
16
+ import { createHash } from "node:crypto";
17
+ /** Byte order, so the order never depends on the machine's locale. */
18
+ const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
19
+ /** Reads what the fingerprint covers, in a stable order. */
20
+ export async function readPgCatalog(client, scope) {
21
+ const { readLiveSchema } = await import("@intentius/chant-lexicon-sql/postgres/live/catalog");
22
+ const schemas = scope.schemas?.filter((s) => s !== scope.historySchema);
23
+ const objects = await readLiveSchema(client, schemas ? { schemas } : {});
24
+ return objects
25
+ .filter((o) => o.schema !== scope.historySchema && !(o.type === "Postgres::Schema" && o.name === scope.historySchema))
26
+ .map((o) => ({ type: o.type, schema: o.schema ?? "", name: o.name, statement: o.statement, comment: o.comment ?? "" }))
27
+ .sort((a, b) => cmp(a.type, b.type) || cmp(a.schema, b.schema) || cmp(a.name, b.name));
28
+ }
29
+ /** `sha256:<hex>` over the catalog's canonical JSON. */
30
+ export function pgCatalogFingerprint(entries) {
31
+ const canonical = entries.map((e) => [e.type, e.schema, e.name, e.statement, e.comment]);
32
+ return `sha256:${createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex")}`;
33
+ }
34
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../../src/versioned/postgres/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAkBzC,sEAAsE;AACtE,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAgB,EAAE,KAAkB;IACtE,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,oDAAoD,CAAC,CAAC;IAC9F,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAA8C,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjH,OAAO,OAAO;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,kBAAkB,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,aAAa,CAAC,CAAC;SACrH,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;SACtH,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,oBAAoB,CAAC,OAAkC;IACrE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACzF,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAClG,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * The versioned apply on Postgres (#13): binding an environment's server,
3
+ * its history, its lock and its statements.
4
+ *
5
+ * Settings:
6
+ *
7
+ * the environment's history database (`yodel.config.ts`
8
+ * `environments.<env>.history.database`, else YODEL_HISTORY_DATABASE,
9
+ * default `yodeler`) is the history's schema: `<schema>.history`, and the lock's key
10
+ * YODEL_LOCK_RETRIES how many more times a statement that hit the lock timeout is tried (default 5)
11
+ *
12
+ * and from `sql.profiles.<env>`, as chant's applier reads them:
13
+ *
14
+ * lockTimeoutMs lock_timeout for every statement (default 5000)
15
+ * statementTimeoutMs statement_timeout for a catalog change (default 60000)
16
+ * scanTimeoutMs statement_timeout for a statement that reads or rewrites rows (default none)
17
+ * schemas the schemas the plan digest's fingerprint covers (default every schema but the server's own)
18
+ *
19
+ * Two connections: the work connection, opened on first use, on which the
20
+ * history is read and written and the statements run; and the lock's,
21
+ * opened when the lock is taken and closed when it is released.
22
+ */
23
+ import type { PostgresEndpoint } from "@intentius/chant-lexicon-sql/postgres/live/client";
24
+ import { type Environment, type Project } from "../../project.js";
25
+ import { PostgresHistoryStore, type PgLockConnection } from "../../history/postgres/index.js";
26
+ import type { VersionedDriver } from "../driver.js";
27
+ import { type PgTimeouts } from "./executor.js";
28
+ /** Opens a connection to the server: chant's `connectPostgres`, or a fake in the tests. */
29
+ export type PgConnect = (endpoint: PostgresEndpoint, applicationName: string) => Promise<PgLockConnection>;
30
+ export interface PgSettings {
31
+ historySchema: string;
32
+ timeouts: PgTimeouts;
33
+ lockRetries: number;
34
+ }
35
+ export declare function pgSettings(target: {
36
+ timeouts?: Partial<PgTimeouts>;
37
+ }, env?: NodeJS.ProcessEnv, historySchema?: string): PgSettings;
38
+ /** A connection opened on its first query, so a verb that reads nothing opens none. */
39
+ export declare class LazyConnection implements PgLockConnection {
40
+ private readonly open;
41
+ private conn;
42
+ constructor(open: () => Promise<PgLockConnection>);
43
+ query<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): Promise<T[]>;
44
+ end(): Promise<void>;
45
+ }
46
+ /** Everything the versioned verbs need about one Postgres environment. */
47
+ export interface PostgresEnv {
48
+ endpoint: PostgresEndpoint;
49
+ /** `sql.profiles.<env>.schemas`. */
50
+ schemas: string[] | undefined;
51
+ defaultSchema: string;
52
+ settings: PgSettings;
53
+ client: LazyConnection;
54
+ store: PostgresHistoryStore;
55
+ driver: VersionedDriver;
56
+ }
57
+ /** Binds `env`'s Postgres server (`sql.profiles.<env>`) and its history. Connects on first use. */
58
+ export declare function openPostgres(project: Project, env: Environment, processEnv?: NodeJS.ProcessEnv, historySchema?: string, connect?: PgConnect): Promise<PostgresEnv>;
59
+ export interface PostgresDriverInput {
60
+ client: LazyConnection;
61
+ /** Opens the lock's own connection. */
62
+ connect: () => Promise<PgLockConnection>;
63
+ settings: PgSettings;
64
+ schemas: string[] | undefined;
65
+ store: PostgresHistoryStore;
66
+ sleep?: (ms: number) => Promise<void>;
67
+ retryBaseMs?: number;
68
+ }
69
+ export declare function postgresDriver(input: PostgresDriverInput): VersionedDriver;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The versioned apply on Postgres (#13): binding an environment's server,
3
+ * its history, its lock and its statements.
4
+ *
5
+ * Settings:
6
+ *
7
+ * the environment's history database (`yodel.config.ts`
8
+ * `environments.<env>.history.database`, else YODEL_HISTORY_DATABASE,
9
+ * default `yodeler`) is the history's schema: `<schema>.history`, and the lock's key
10
+ * YODEL_LOCK_RETRIES how many more times a statement that hit the lock timeout is tried (default 5)
11
+ *
12
+ * and from `sql.profiles.<env>`, as chant's applier reads them:
13
+ *
14
+ * lockTimeoutMs lock_timeout for every statement (default 5000)
15
+ * statementTimeoutMs statement_timeout for a catalog change (default 60000)
16
+ * scanTimeoutMs statement_timeout for a statement that reads or rewrites rows (default none)
17
+ * schemas the schemas the plan digest's fingerprint covers (default every schema but the server's own)
18
+ *
19
+ * Two connections: the work connection, opened on first use, on which the
20
+ * history is read and written and the statements run; and the lock's,
21
+ * opened when the lock is taken and closed when it is released.
22
+ */
23
+ import { YodelError } from "../../project.js";
24
+ import { DEFAULT_HISTORY_DATABASE } from "../../history/index.js";
25
+ import { acquireAdvisoryLock, PostgresHistoryStore } from "../../history/postgres/index.js";
26
+ import { pgCatalogFingerprint, readPgCatalog } from "./catalog.js";
27
+ import { DEFAULT_LOCK_RETRIES, DEFAULT_PG_TIMEOUTS, PgStatementError, runPgStatement } from "./executor.js";
28
+ export function pgSettings(target, env = process.env, historySchema = env.YODEL_HISTORY_DATABASE || DEFAULT_HISTORY_DATABASE) {
29
+ const retries = env.YODEL_LOCK_RETRIES ? Number(env.YODEL_LOCK_RETRIES) : DEFAULT_LOCK_RETRIES;
30
+ if (!(Number.isInteger(retries) && retries >= 0))
31
+ throw new YodelError(`YODEL_LOCK_RETRIES must be a whole number of retries, got ${JSON.stringify(env.YODEL_LOCK_RETRIES)}`);
32
+ const t = target.timeouts ?? {};
33
+ return {
34
+ historySchema,
35
+ timeouts: {
36
+ lockTimeoutMs: t.lockTimeoutMs ?? DEFAULT_PG_TIMEOUTS.lockTimeoutMs,
37
+ statementTimeoutMs: t.statementTimeoutMs ?? DEFAULT_PG_TIMEOUTS.statementTimeoutMs,
38
+ scanTimeoutMs: t.scanTimeoutMs ?? DEFAULT_PG_TIMEOUTS.scanTimeoutMs,
39
+ },
40
+ lockRetries: retries,
41
+ };
42
+ }
43
+ const chantConnect = async (endpoint, applicationName) => {
44
+ const { connectPostgres } = await import("@intentius/chant-lexicon-sql/postgres/live/client");
45
+ return connectPostgres(endpoint, { applicationName });
46
+ };
47
+ /** A connection opened on its first query, so a verb that reads nothing opens none. */
48
+ export class LazyConnection {
49
+ open;
50
+ conn;
51
+ constructor(open) {
52
+ this.open = open;
53
+ }
54
+ async query(sql, params) {
55
+ this.conn ??= this.open();
56
+ return (await this.conn).query(sql, params);
57
+ }
58
+ async end() {
59
+ const c = this.conn;
60
+ this.conn = undefined;
61
+ if (c)
62
+ await (await c.catch(() => undefined))?.end();
63
+ }
64
+ }
65
+ /** Binds `env`'s Postgres server (`sql.profiles.<env>`) and its history. Connects on first use. */
66
+ export async function openPostgres(project, env, processEnv = process.env, historySchema, connect = chantConnect) {
67
+ const { resolvePostgresTarget, isUnresolvedTarget } = await import("@intentius/chant-lexicon-sql/postgres/live/bind");
68
+ const target = resolvePostgresTarget({ environment: env.name, config: project.config, env: processEnv });
69
+ if (isUnresolvedTarget(target))
70
+ throw new YodelError(target.detail);
71
+ const settings = pgSettings(target, processEnv, historySchema);
72
+ const client = new LazyConnection(() => connect(target.endpoint, "yodel"));
73
+ const store = new PostgresHistoryStore(client, { schema: settings.historySchema });
74
+ const schemas = target.schemas;
75
+ const driver = postgresDriver({ client, connect: () => connect(target.endpoint, "yodel lock"), settings, schemas, store });
76
+ return { endpoint: target.endpoint, schemas, defaultSchema: target.defaultSchema, settings, client, store, driver };
77
+ }
78
+ export function postgresDriver(input) {
79
+ const { client, settings } = input;
80
+ return {
81
+ dialect: "postgres",
82
+ async acquireLock(holder) {
83
+ return acquireAdvisoryLock(await input.connect(), { schema: settings.historySchema, holder });
84
+ },
85
+ async liveFingerprint() {
86
+ return pgCatalogFingerprint(await readPgCatalog(client, { schemas: input.schemas, historySchema: settings.historySchema }));
87
+ },
88
+ async runStatement(step, run) {
89
+ try {
90
+ await runPgStatement(client, step, {
91
+ timeouts: settings.timeouts,
92
+ retries: settings.lockRetries,
93
+ succeeded: async (note) => {
94
+ await run.succeeded(note);
95
+ },
96
+ log: run.log,
97
+ ...(input.sleep ? { sleep: input.sleep } : {}),
98
+ ...(input.retryBaseMs !== undefined ? { retryBaseMs: input.retryBaseMs } : {}),
99
+ });
100
+ }
101
+ catch (e) {
102
+ if (e instanceof PgStatementError)
103
+ throw new Error(e.error, { cause: e });
104
+ throw e;
105
+ }
106
+ },
107
+ stepContext() {
108
+ return { lockTimeoutMs: settings.timeouts.lockTimeoutMs, postgres: { client, timeouts: settings.timeouts, receiptsSchema: settings.historySchema } };
109
+ },
110
+ close: () => client.end(),
111
+ };
112
+ }
113
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/versioned/postgres/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Sending one migration statement to Postgres, the way chant's applier does
3
+ * (`postgres/apply/apply.ts`), with the history row that says it succeeded.
4
+ *
5
+ * ## Timeouts and retries
6
+ *
7
+ * Every statement runs with its own `lock_timeout` (default 5 s, the
8
+ * profile's `lockTimeoutMs`) and `statement_timeout` (the profile's
9
+ * `statementTimeoutMs`, default 60 s, for a catalog change; `scanTimeoutMs`,
10
+ * default none, for one that reads or rewrites rows). A statement blocked
11
+ * behind another session's lock gives up when the lock timeout passes
12
+ * (SQLSTATE 55P03) instead of queueing every later query on the table
13
+ * behind it, and is tried again after a pause that doubles each time (250
14
+ * ms, 500 ms, ...), up to `retries` more times (default 5). Any other error
15
+ * fails it at once.
16
+ *
17
+ * ## Transactions
18
+ *
19
+ * A statement chant marks `transactional` runs in a transaction of its own:
20
+ *
21
+ * BEGIN; SET LOCAL lock_timeout ...; SET LOCAL statement_timeout ...;
22
+ * <statement>; INSERT INTO <history> (... 'succeeded' ...); COMMIT
23
+ *
24
+ * so the statement and its `succeeded` row commit together or not at all.
25
+ * A runner killed anywhere in it leaves either both or neither: a statement
26
+ * whose latest row is `started` did not commit, and is sent again safely.
27
+ *
28
+ * A statement that may not run in a transaction block (`CREATE INDEX
29
+ * CONCURRENTLY`, `DROP INDEX CONCURRENTLY`) or that chant keeps out of one
30
+ * (`ALTER TYPE ... ADD VALUE`, whose label cannot be used in the transaction
31
+ * that adds it) runs alone, with `SET` for the session first; its
32
+ * `succeeded` row is written right after it, in a transaction of its own.
33
+ * A runner killed between the two leaves the statement run and its latest
34
+ * row `started`, and the next run sends it again.
35
+ *
36
+ * ## A failed CONCURRENTLY build
37
+ *
38
+ * `CREATE INDEX CONCURRENTLY` that fails (a lock timeout, a duplicate on a
39
+ * unique index, a cancel) leaves the index behind, INVALID: it is in the
40
+ * catalog, is maintained on every write, and is never used, and the next
41
+ * `CREATE INDEX` of that name fails because it exists. So before every
42
+ * attempt, the first, a retry, or a resume in a later run, and after every
43
+ * failure, an INVALID index of that name is dropped (`DROP INDEX
44
+ * CONCURRENTLY IF EXISTS`), and only an INVALID one: a valid index of that
45
+ * name is never touched.
46
+ */
47
+ import { YodelError } from "../../project.js";
48
+ import type { PgClient } from "../../history/postgres/index.js";
49
+ /** SQLSTATE lock_not_available: the lock timeout passed. */
50
+ export declare const LOCK_NOT_AVAILABLE = "55P03";
51
+ export interface PgTimeouts {
52
+ lockTimeoutMs: number;
53
+ /** For a statement that changes only the catalog. 0 is no limit. */
54
+ statementTimeoutMs: number;
55
+ /** For a statement that reads or rewrites rows. 0 is no limit. */
56
+ scanTimeoutMs: number;
57
+ }
58
+ export declare const DEFAULT_PG_TIMEOUTS: PgTimeouts;
59
+ export declare const DEFAULT_LOCK_RETRIES = 5;
60
+ export declare const DEFAULT_RETRY_BASE_MS = 250;
61
+ /** The parts of a migration's statement step the executor reads. */
62
+ export interface PgStatement {
63
+ sql: string;
64
+ /** chant's class: `rewrite`, `validate` and `concurrently` read or rewrite rows. */
65
+ class: string;
66
+ /** False: outside any transaction. */
67
+ transactional: boolean;
68
+ }
69
+ export interface PgStatementOptions {
70
+ timeouts: PgTimeouts;
71
+ /** How many more times a statement that hit the lock timeout is tried. */
72
+ retries: number;
73
+ /** The first pause before a retry, in ms; it doubles each time. */
74
+ retryBaseMs?: number;
75
+ /**
76
+ * Writes the `succeeded` row on the client, given the note it carries
77
+ * (the retries it took, the INVALID index it dropped). Called inside the
78
+ * statement's transaction when it has one.
79
+ */
80
+ succeeded: (note: string) => Promise<void>;
81
+ log: (line: string) => void;
82
+ sleep?: (ms: number) => Promise<void>;
83
+ }
84
+ export interface PgStatementResult {
85
+ attempts: number;
86
+ /** INVALID indexes dropped before or between attempts. */
87
+ droppedInvalid: string[];
88
+ note: string;
89
+ }
90
+ /** A statement failed; `error` is the server's message. */
91
+ export declare class PgStatementError extends YodelError {
92
+ readonly error: string;
93
+ readonly code: string | undefined;
94
+ readonly attempts: number;
95
+ constructor(error: string, code: string | undefined, attempts: number);
96
+ }
97
+ /** Whether a statement of `cls` reads or rewrites rows (chant's `scansRows`), so it runs with the scan timeout. */
98
+ export declare const scansRows: (cls: string) => boolean;
99
+ /** The index a `CREATE INDEX CONCURRENTLY` builds, `schema.name` quoted, when the statement names its table's schema. */
100
+ export declare function concurrentIndex(sql: string): string | undefined;
101
+ /** Drops `index` when it is there and INVALID. Returns whether it dropped it. */
102
+ export declare function dropInvalidIndex(client: PgClient, index: string, timeouts: PgTimeouts, log: (line: string) => void): Promise<boolean>;
103
+ /** Runs one statement to success or failure; see the module comment. */
104
+ export declare function runPgStatement(client: PgClient, step: PgStatement, options: PgStatementOptions): Promise<PgStatementResult>;