@intentius/sql-yodeler 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/README.md +2 -2
  2. package/dist/apply-steps/index.d.ts +65 -0
  3. package/dist/apply-steps/index.js +106 -0
  4. package/dist/apply-steps/index.js.map +1 -0
  5. package/dist/approval-mode/index.d.ts +78 -0
  6. package/dist/approval-mode/index.js +105 -0
  7. package/dist/approval-mode/index.js.map +1 -0
  8. package/dist/chant/access.d.ts +84 -0
  9. package/dist/chant/access.js +155 -0
  10. package/dist/chant/access.js.map +1 -0
  11. package/dist/chant/drift.d.ts +17 -1
  12. package/dist/chant/drift.js +55 -2
  13. package/dist/chant/drift.js.map +1 -1
  14. package/dist/chant/plan.d.ts +18 -2
  15. package/dist/chant/plan.js +21 -3
  16. package/dist/chant/plan.js.map +1 -1
  17. package/dist/ci/login.d.ts +78 -0
  18. package/dist/ci/login.js +199 -0
  19. package/dist/ci/login.js.map +1 -0
  20. package/dist/ci/render.d.ts +64 -10
  21. package/dist/ci/render.js +385 -88
  22. package/dist/ci/render.js.map +1 -1
  23. package/dist/cleanup/index.d.ts +92 -0
  24. package/dist/cleanup/index.js +213 -0
  25. package/dist/cleanup/index.js.map +1 -0
  26. package/dist/commands/apply.js +178 -20
  27. package/dist/commands/apply.js.map +1 -1
  28. package/dist/commands/approve.d.ts +13 -0
  29. package/dist/commands/approve.js +206 -24
  30. package/dist/commands/approve.js.map +1 -1
  31. package/dist/commands/ci.js +12 -0
  32. package/dist/commands/ci.js.map +1 -1
  33. package/dist/commands/cleanup.d.ts +13 -0
  34. package/dist/commands/cleanup.js +158 -0
  35. package/dist/commands/cleanup.js.map +1 -0
  36. package/dist/commands/config.d.ts +3 -0
  37. package/dist/commands/config.js +136 -0
  38. package/dist/commands/config.js.map +1 -0
  39. package/dist/commands/drift.js +33 -3
  40. package/dist/commands/drift.js.map +1 -1
  41. package/dist/commands/index.js +6 -0
  42. package/dist/commands/index.js.map +1 -1
  43. package/dist/commands/init.d.ts +11 -0
  44. package/dist/commands/init.js +98 -10
  45. package/dist/commands/init.js.map +1 -1
  46. package/dist/commands/lint.js +67 -10
  47. package/dist/commands/lint.js.map +1 -1
  48. package/dist/commands/new.js +135 -54
  49. package/dist/commands/new.js.map +1 -1
  50. package/dist/commands/plan.js +39 -5
  51. package/dist/commands/plan.js.map +1 -1
  52. package/dist/commands/test.d.ts +3 -0
  53. package/dist/commands/test.js +164 -0
  54. package/dist/commands/test.js.map +1 -0
  55. package/dist/config/define.d.ts +175 -2
  56. package/dist/config/define.js +2 -0
  57. package/dist/config/define.js.map +1 -1
  58. package/dist/config/load.js +78 -10
  59. package/dist/config/load.js.map +1 -1
  60. package/dist/config/settings.d.ts +20 -0
  61. package/dist/config/settings.js +28 -0
  62. package/dist/config/settings.js.map +1 -1
  63. package/dist/credentials.d.ts +119 -0
  64. package/dist/credentials.js +119 -0
  65. package/dist/credentials.js.map +1 -0
  66. package/dist/exit.js +1 -1
  67. package/dist/exit.js.map +1 -1
  68. package/dist/index.d.ts +5 -1
  69. package/dist/index.js +2 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/init/adopt.js +1 -1
  72. package/dist/init/adopt.js.map +1 -1
  73. package/dist/init/clickhouse-access.d.ts +64 -0
  74. package/dist/init/clickhouse-access.js +211 -0
  75. package/dist/init/clickhouse-access.js.map +1 -0
  76. package/dist/init/index.d.ts +2 -0
  77. package/dist/init/index.js +2 -0
  78. package/dist/init/index.js.map +1 -1
  79. package/dist/init/live.d.ts +4 -0
  80. package/dist/init/live.js +16 -0
  81. package/dist/init/live.js.map +1 -1
  82. package/dist/init/second.d.ts +70 -0
  83. package/dist/init/second.js +149 -0
  84. package/dist/init/second.js.map +1 -0
  85. package/dist/lint/access.d.ts +18 -0
  86. package/dist/lint/access.js +34 -0
  87. package/dist/lint/access.js.map +1 -0
  88. package/dist/lint/annotations.d.ts +49 -0
  89. package/dist/lint/annotations.js +82 -0
  90. package/dist/lint/annotations.js.map +1 -0
  91. package/dist/lint/check.js +25 -7
  92. package/dist/lint/check.js.map +1 -1
  93. package/dist/lint/config.d.ts +7 -1
  94. package/dist/lint/config.js +11 -5
  95. package/dist/lint/config.js.map +1 -1
  96. package/dist/lint/index.d.ts +4 -0
  97. package/dist/lint/index.js +4 -0
  98. package/dist/lint/index.js.map +1 -1
  99. package/dist/lint/naming.d.ts +62 -0
  100. package/dist/lint/naming.js +138 -0
  101. package/dist/lint/naming.js.map +1 -0
  102. package/dist/lint/rules.d.ts +37 -4
  103. package/dist/lint/rules.js +16 -4
  104. package/dist/lint/rules.js.map +1 -1
  105. package/dist/lint/schema-checks.d.ts +63 -0
  106. package/dist/lint/schema-checks.js +128 -0
  107. package/dist/lint/schema-checks.js.map +1 -0
  108. package/dist/lint/statements.d.ts +22 -1
  109. package/dist/lint/statements.js +40 -1
  110. package/dist/lint/statements.js.map +1 -1
  111. package/dist/mcp/tools.d.ts +1 -1
  112. package/dist/mcp/tools.js +1 -1
  113. package/dist/mcp/tools.js.map +1 -1
  114. package/dist/migrations/checkpoint.js +0 -2
  115. package/dist/migrations/checkpoint.js.map +1 -1
  116. package/dist/migrations/checks.d.ts +19 -1
  117. package/dist/migrations/checks.js +15 -0
  118. package/dist/migrations/checks.js.map +1 -1
  119. package/dist/migrations/create.d.ts +13 -4
  120. package/dist/migrations/create.js +24 -9
  121. package/dist/migrations/create.js.map +1 -1
  122. package/dist/migrations/routines.d.ts +5 -15
  123. package/dist/migrations/routines.js +5 -30
  124. package/dist/migrations/routines.js.map +1 -1
  125. package/dist/pr/model.d.ts +4 -1
  126. package/dist/pr/model.js +13 -5
  127. package/dist/pr/model.js.map +1 -1
  128. package/dist/pr/render.d.ts +4 -1
  129. package/dist/pr/render.js +22 -8
  130. package/dist/pr/render.js.map +1 -1
  131. package/dist/pr/tenants.d.ts +37 -0
  132. package/dist/pr/tenants.js +138 -0
  133. package/dist/pr/tenants.js.map +1 -0
  134. package/dist/probe.d.ts +27 -0
  135. package/dist/probe.js +106 -0
  136. package/dist/probe.js.map +1 -0
  137. package/dist/project.d.ts +16 -0
  138. package/dist/project.js +29 -1
  139. package/dist/project.js.map +1 -1
  140. package/dist/replay/clickhouse.d.ts +31 -0
  141. package/dist/replay/clickhouse.js +103 -6
  142. package/dist/replay/clickhouse.js.map +1 -1
  143. package/dist/replay/replay.d.ts +5 -2
  144. package/dist/replay/replay.js.map +1 -1
  145. package/dist/report/audit.d.ts +1 -1
  146. package/dist/report/audit.js +20 -3
  147. package/dist/report/audit.js.map +1 -1
  148. package/dist/schemas.d.ts +3 -1
  149. package/dist/schemas.js +2 -0
  150. package/dist/schemas.js.map +1 -1
  151. package/dist/sources/ddl.d.ts +15 -20
  152. package/dist/sources/ddl.js +34 -328
  153. package/dist/sources/ddl.js.map +1 -1
  154. package/dist/sources/index.js +2 -2
  155. package/dist/sources/index.js.map +1 -1
  156. package/dist/steps/backfill.d.ts +40 -5
  157. package/dist/steps/backfill.js +128 -8
  158. package/dist/steps/backfill.js.map +1 -1
  159. package/dist/steps/index.d.ts +19 -1
  160. package/dist/steps/index.js +34 -7
  161. package/dist/steps/index.js.map +1 -1
  162. package/dist/steps/kinds.d.ts +42 -4
  163. package/dist/steps/kinds.js +88 -14
  164. package/dist/steps/kinds.js.map +1 -1
  165. package/dist/steps/postgres-migration.d.ts +3 -0
  166. package/dist/steps/postgres-migration.js +7 -3
  167. package/dist/steps/postgres-migration.js.map +1 -1
  168. package/dist/steps/rebuild.d.ts +15 -6
  169. package/dist/steps/rebuild.js +12 -9
  170. package/dist/steps/rebuild.js.map +1 -1
  171. package/dist/tenants/index.d.ts +2 -1
  172. package/dist/tenants/index.js +3 -4
  173. package/dist/tenants/index.js.map +1 -1
  174. package/dist/tenants/wave.d.ts +128 -0
  175. package/dist/tenants/wave.js +254 -0
  176. package/dist/tenants/wave.js.map +1 -0
  177. package/dist/testing/define.d.ts +62 -0
  178. package/dist/testing/define.js +32 -0
  179. package/dist/testing/define.js.map +1 -0
  180. package/dist/testing/files.d.ts +5 -0
  181. package/dist/testing/files.js +18 -0
  182. package/dist/testing/files.js.map +1 -0
  183. package/dist/testing/report.d.ts +14 -0
  184. package/dist/testing/report.js +83 -0
  185. package/dist/testing/report.js.map +1 -0
  186. package/dist/testing/run.d.ts +104 -0
  187. package/dist/testing/run.js +328 -0
  188. package/dist/testing/run.js.map +1 -0
  189. package/dist/versioned/approval.d.ts +6 -0
  190. package/dist/versioned/approval.js +11 -5
  191. package/dist/versioned/approval.js.map +1 -1
  192. package/dist/versioned/checks.d.ts +8 -2
  193. package/dist/versioned/checks.js +34 -13
  194. package/dist/versioned/checks.js.map +1 -1
  195. package/dist/versioned/driver.d.ts +2 -2
  196. package/dist/versioned/engine.d.ts +10 -0
  197. package/dist/versioned/engine.js +8 -4
  198. package/dist/versioned/engine.js.map +1 -1
  199. package/dist/versioned/explain.d.ts +2 -0
  200. package/dist/versioned/explain.js +3 -1
  201. package/dist/versioned/explain.js.map +1 -1
  202. package/dist/versioned/op.d.ts +4 -0
  203. package/dist/versioned/op.js +5 -2
  204. package/dist/versioned/op.js.map +1 -1
  205. package/dist/versioned/state.js +1 -1
  206. package/dist/versioned/state.js.map +1 -1
  207. package/dist/waves/index.d.ts +58 -13
  208. package/dist/waves/index.js +109 -24
  209. package/dist/waves/index.js.map +1 -1
  210. package/dist/waves/record.d.ts +10 -0
  211. package/dist/waves/record.js.map +1 -1
  212. package/package.json +8 -7
  213. package/schemas/apply.schema.json +1 -1
  214. package/schemas/cleanup.schema.json +40 -0
  215. package/schemas/init.schema.json +51 -22
  216. package/schemas/lint-rules.schema.json +4 -0
  217. package/schemas/new.schema.json +41 -3
  218. package/schemas/plan.schema.json +39 -0
  219. package/schemas/report.schema.json +1 -1
  220. package/schemas/test.schema.json +41 -0
@@ -5,6 +5,7 @@
5
5
  * migrationSilences(m) a migration's `-- yodel:allow` lines, as the history records them
6
6
  * updateChecksum(m) writes migration.json's checksum to match its files
7
7
  * renderLint / lintJson the output; lintExitCode the exit code
8
+ * workflowCommands / codeQuality the findings as forge annotations (#129)
8
9
  *
9
10
  * The rules are in `./rules.ts`, how a silence is read in `./silences.ts`.
10
11
  */
@@ -13,12 +14,15 @@ import { join } from "node:path";
13
14
  import { MIGRATION_JSON, serializeDocument, verifyChecksum } from "../migrations/index.js";
14
15
  import { silenceableRules } from "./rules.js";
15
16
  import { mapMigrationSql, silencesJson } from "./silences.js";
17
+ export * from "./annotations.js";
16
18
  export * from "./check.js";
17
19
  export * from "./config.js";
18
20
  export * from "./report.js";
19
21
  export * from "./rules.js";
20
22
  export * from "./silences.js";
21
23
  export * from "./statements.js";
24
+ export * from "./naming.js";
25
+ export { SCHEMA_RULES, isSchemaRule, schemaDiagnostics, chantLevel } from "./schema-checks.js";
22
26
  /** The well-formed `-- yodel:allow` lines of a migration, each with the step it is above. */
23
27
  export function silencesOf(m) {
24
28
  return mapMigrationSql(m.files.sql, m.document.steps, new Set(silenceableRules())).silences;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lint/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;AAE5E,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAEhC,6FAA6F;AAC7F,MAAM,UAAU,UAAU,CAAC,CAAwC;IACjE,OAAO,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,CAAS,gBAAgB,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtG,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAwC,EAAU,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,CAAY;IACzC,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,cAAc,WAAW,CAAC,CAAC;IACvD,aAAa,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,GAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAA6B,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5H,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lint/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;AAE5E,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAyB,MAAM,oBAAoB,CAAC;AAEtH,6FAA6F;AAC7F,MAAM,UAAU,UAAU,CAAC,CAAwC;IACjE,OAAO,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,CAAS,gBAAgB,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtG,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAwC,EAAU,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,CAAY;IACzC,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,cAAc,WAAW,CAAC,CAAC;IACvD,aAAa,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,GAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAA6B,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5H,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `naming` (#51): names a migration gives that break the project's naming
3
+ * rules, set per kind of object in `yodel.config.ts`:
4
+ *
5
+ * ```ts
6
+ * export default defineConfig({
7
+ * lint: { naming: { table: "snake_case", column: "snake_case", index: "[a-z_]+_idx", constraint: "[a-z_]+_(pkey|key|fkey|check)" } },
8
+ * });
9
+ * ```
10
+ *
11
+ * A rule is a preset (`snake_case`, `camelCase`, `PascalCase`) or a regular
12
+ * expression the whole name must match. Only the names a migration adds are
13
+ * checked: a table, column, index or named constraint in its recorded schema
14
+ * that its parent's does not have. A name already there is not reported
15
+ * again, so turning a rule on does not fail the migrations already written.
16
+ * An unnamed constraint (the server names it) is not checked.
17
+ *
18
+ * Read from the recorded schemas with the lexicon's own parser: the names
19
+ * are the server's (Postgres folds an unquoted name to lower case). Each
20
+ * finding is at the migration's first step on the object, so a
21
+ * `-- yodel:allow naming <reason>` line above it silences it.
22
+ */
23
+ import type { BuildOutput } from "../chant/build.js";
24
+ import type { Migration } from "../migrations/index.js";
25
+ export declare const NAMING_KINDS: readonly ["table", "column", "index", "constraint"];
26
+ export type NamingKind = (typeof NAMING_KINDS)[number];
27
+ export declare const NAMING_PRESETS: Readonly<Record<string, string>>;
28
+ /** One kind's rule, as `yodel.config.ts` gives it and compiled. */
29
+ export interface NamingRule {
30
+ /** As written: a preset name or a regular expression. */
31
+ rule: string;
32
+ pattern: RegExp;
33
+ }
34
+ export type NamingSettings = Partial<Record<NamingKind, NamingRule>>;
35
+ /** Reads `lint.naming`; a key that is not a kind, or a rule that is not a preset or a valid expression, is an error naming `where`. */
36
+ export declare function namingSettings(raw: unknown, where?: string): NamingSettings;
37
+ /** A name a recorded schema gives. */
38
+ interface Named {
39
+ kind: NamingKind;
40
+ /** Unique within the schema: the object, and for a column, index or constraint its table. */
41
+ key: string;
42
+ name: string;
43
+ /** The declaration's export, which a step on the object names. */
44
+ export: string;
45
+ /** How a message names it: `table shop.orders`, `column note of shop.orders`. */
46
+ label: string;
47
+ }
48
+ /** The names of a recorded schema, by kind; `[]` for one the lexicon cannot read (lint reports that on its own). */
49
+ export declare function schemaNames(build: BuildOutput, options?: {
50
+ defaultDatabase?: string | undefined;
51
+ defaultSchema?: string | undefined;
52
+ }): Named[];
53
+ export interface NamingFinding {
54
+ rule: "naming";
55
+ migration: string;
56
+ step?: number;
57
+ object?: string;
58
+ message: string;
59
+ }
60
+ /** The names each migration adds that break `settings`. */
61
+ export declare function namingFindings(migrations: readonly Migration[], settings: NamingSettings): NamingFinding[];
62
+ export {};
@@ -0,0 +1,138 @@
1
+ /**
2
+ * `naming` (#51): names a migration gives that break the project's naming
3
+ * rules, set per kind of object in `yodel.config.ts`:
4
+ *
5
+ * ```ts
6
+ * export default defineConfig({
7
+ * lint: { naming: { table: "snake_case", column: "snake_case", index: "[a-z_]+_idx", constraint: "[a-z_]+_(pkey|key|fkey|check)" } },
8
+ * });
9
+ * ```
10
+ *
11
+ * A rule is a preset (`snake_case`, `camelCase`, `PascalCase`) or a regular
12
+ * expression the whole name must match. Only the names a migration adds are
13
+ * checked: a table, column, index or named constraint in its recorded schema
14
+ * that its parent's does not have. A name already there is not reported
15
+ * again, so turning a rule on does not fail the migrations already written.
16
+ * An unnamed constraint (the server names it) is not checked.
17
+ *
18
+ * Read from the recorded schemas with the lexicon's own parser: the names
19
+ * are the server's (Postgres folds an unquoted name to lower case). Each
20
+ * finding is at the migration's first step on the object, so a
21
+ * `-- yodel:allow naming <reason>` line above it silences it.
22
+ */
23
+ import { schemaFromBuildOutput } from "@intentius/chant-lexicon-sql/clickhouse/plan/schema";
24
+ import { pgSchemaFromBuildOutput } from "@intentius/chant-lexicon-sql/postgres/plan/schema";
25
+ import { YodelError } from "../project.js";
26
+ export const NAMING_KINDS = ["table", "column", "index", "constraint"];
27
+ export const NAMING_PRESETS = {
28
+ snake_case: "[a-z][a-z0-9]*(_[a-z0-9]+)*",
29
+ camelCase: "[a-z][a-zA-Z0-9]*",
30
+ PascalCase: "[A-Z][a-zA-Z0-9]*",
31
+ };
32
+ const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
33
+ /** Reads `lint.naming`; a key that is not a kind, or a rule that is not a preset or a valid expression, is an error naming `where`. */
34
+ export function namingSettings(raw, where = "yodel.config.ts") {
35
+ if (raw === undefined)
36
+ return {};
37
+ if (!isRecord(raw))
38
+ throw new YodelError(`${where}: lint.naming: expected an object of ${NAMING_KINDS.join(", ")}: a preset (${Object.keys(NAMING_PRESETS).join(", ")}) or a regular expression`);
39
+ const out = {};
40
+ for (const [kind, rule] of Object.entries(raw)) {
41
+ if (!NAMING_KINDS.includes(kind))
42
+ throw new YodelError(`${where}: lint.naming.${kind}: not a kind of object (the kinds: ${NAMING_KINDS.join(", ")})`);
43
+ if (typeof rule !== "string" || rule === "")
44
+ throw new YodelError(`${where}: lint.naming.${kind}: expected a preset (${Object.keys(NAMING_PRESETS).join(", ")}) or a regular expression`);
45
+ const source = NAMING_PRESETS[rule] ?? rule;
46
+ let pattern;
47
+ try {
48
+ pattern = new RegExp(`^(?:${source})$`, "u");
49
+ }
50
+ catch (e) {
51
+ throw new YodelError(`${where}: lint.naming.${kind}: ${JSON.stringify(rule)} is not a preset or a valid regular expression: ${e instanceof Error ? e.message : String(e)}`);
52
+ }
53
+ out[kind] = { rule, pattern };
54
+ }
55
+ return out;
56
+ }
57
+ /** The names of a recorded schema, by kind; `[]` for one the lexicon cannot read (lint reports that on its own). */
58
+ export function schemaNames(build, options = {}) {
59
+ if (!Array.isArray(build.objects))
60
+ return [];
61
+ const out = [];
62
+ try {
63
+ if (build.dialect === "postgres") {
64
+ for (const o of pgSchemaFromBuildOutput(JSON.stringify(build), options.defaultSchema ?? "public")) {
65
+ const c = o.canonical;
66
+ const qualified = c.schema ? `${c.schema}.${c.name}` : c.name;
67
+ const exp = c.exportName ?? o.key;
68
+ if (c.kind === "table") {
69
+ out.push({ kind: "table", key: qualified, name: c.name, export: exp, label: `table ${qualified}` });
70
+ for (const col of c.columns)
71
+ out.push({ kind: "column", key: `${qualified}.${col.name}`, name: col.name, export: exp, label: `column ${col.name} of ${qualified}` });
72
+ for (const k of c.constraints)
73
+ if (k.name)
74
+ out.push({ kind: "constraint", key: `${qualified}.${k.name}`, name: k.name, export: exp, label: `constraint ${k.name} on ${qualified}` });
75
+ }
76
+ else if (c.kind === "index") {
77
+ out.push({ kind: "index", key: qualified, name: c.name, export: exp, label: `index ${qualified}` });
78
+ }
79
+ }
80
+ }
81
+ else {
82
+ for (const o of schemaFromBuildOutput(JSON.stringify(build), options.defaultDatabase ?? "default")) {
83
+ const c = o.canonical;
84
+ if (c.kind !== "table")
85
+ continue;
86
+ const qualified = c.database ? `${c.database}.${c.name}` : c.name;
87
+ out.push({ kind: "table", key: qualified, name: c.name, export: o.key, label: `table ${qualified}` });
88
+ for (const col of c.columns)
89
+ out.push({ kind: "column", key: `${qualified}.${col.name}`, name: col.name, export: o.key, label: `column ${col.name} of ${qualified}` });
90
+ for (const name of Object.keys(c.indexes))
91
+ out.push({ kind: "index", key: `${qualified}.${name}`, name, export: o.key, label: `index ${name} on ${qualified}` });
92
+ for (const name of Object.keys(c.constraints))
93
+ out.push({ kind: "constraint", key: `${qualified}.${name}`, name, export: o.key, label: `constraint ${name} on ${qualified}` });
94
+ }
95
+ }
96
+ }
97
+ catch {
98
+ return [];
99
+ }
100
+ return out;
101
+ }
102
+ const PLURAL = { table: "tables", column: "columns", index: "indexes", constraint: "constraints" };
103
+ /** The names each migration adds that break `settings`. */
104
+ export function namingFindings(migrations, settings) {
105
+ if (Object.keys(settings).length === 0)
106
+ return [];
107
+ const byId = new Map(migrations.map((m) => [m.id, m]));
108
+ const cache = new Map();
109
+ const names = (m) => {
110
+ let n = cache.get(m.id);
111
+ if (!n) {
112
+ n = schemaNames(m.document.schema, { defaultDatabase: m.document.defaultDatabase, defaultSchema: m.document.defaultSchema });
113
+ cache.set(m.id, n);
114
+ }
115
+ return n;
116
+ };
117
+ const out = [];
118
+ for (const m of migrations) {
119
+ const parent = m.parent === null ? undefined : byId.get(m.parent);
120
+ const had = new Set((parent ? names(parent) : []).map((n) => `${n.kind}\u0000${n.key}`));
121
+ for (const n of names(m)) {
122
+ const rule = settings[n.kind];
123
+ if (!rule || had.has(`${n.kind}\u0000${n.key}`) || rule.pattern.test(n.name))
124
+ continue;
125
+ const step = m.document.steps.findIndex((s) => s.object === n.export);
126
+ const at = step >= 0 ? m.document.steps[step] : undefined;
127
+ out.push({
128
+ rule: "naming",
129
+ migration: m.id,
130
+ message: `${step >= 0 ? `step ${String(step)}: ` : ""}${n.label}: ${JSON.stringify(n.name)} does not match the naming rule for ${PLURAL[n.kind]} (${NAMING_PRESETS[rule.rule] ? `${rule.rule}, ` : ""}${NAMING_PRESETS[rule.rule] ?? rule.rule})`,
131
+ ...(step >= 0 ? { step } : {}),
132
+ ...(at ? { object: at.name } : {}),
133
+ });
134
+ }
135
+ }
136
+ return out;
137
+ }
138
+ //# sourceMappingURL=naming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/lint/naming.ts"],"names":[],"mappings":"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"}
@@ -9,12 +9,18 @@
9
9
  * config lowers them and nothing silences them, because a migration that
10
10
  * breaks one cannot be applied safely or cannot be checked at all.
11
11
  * - Statement rules, from the classes chant's classifier gives each step:
12
- * `destructive`, `pg-rewrite`, `pg-lock`, `pg-signature`, `ch-mutation`,
13
- * `ch-rebuild`.
12
+ * `destructive`, `pg-rewrite`, `pg-lock`, `pg-signature`, `pg-access`,
13
+ * `pg-rls-not-forced` (a warning), `ch-mutation`, `ch-rebuild`,
14
+ * `ch-access`, `data-dependent` (a warning: a statement that can fail
15
+ * on the rows already there, with the pre-check yodel apply runs first),
16
+ * and `naming` (the names a migration adds, against `lint.naming`).
14
17
  * Errors by default; a line `-- yodel:allow <rule> <reason>` above the
15
18
  * statement in `migration.sql` silences one for that statement, and the
16
19
  * reason is recorded in the history when the migration applies.
17
20
  *
21
+ * The lexicon's schema checks (`SQLPG101`, `SQLCH113`, ...) are rules too,
22
+ * in `./schema-checks.ts`: silenceable, at the level the check gives.
23
+ *
18
24
  * The rest (`refused-step`, `pg-routine-in-use`, `migration-sql`,
19
25
  * `silence`) say a migration will not apply as written or a silence is
20
26
  * wrong. Their level can be
@@ -33,6 +39,8 @@ export interface LintRule {
33
39
  configurable: boolean;
34
40
  /** The dialect it applies to; both when absent. */
35
41
  dialect?: "clickhouse" | "postgres";
42
+ /** One of the sql lexicon's schema checks (`./schema-checks.ts`). */
43
+ schema?: boolean;
36
44
  }
37
45
  export declare const LINT_RULES: {
38
46
  readonly fork: LintRule;
@@ -52,8 +60,33 @@ export declare const LINT_RULES: {
52
60
  readonly configurable: true;
53
61
  readonly dialect: "postgres";
54
62
  };
63
+ readonly "pg-access": LintRule;
64
+ readonly "pg-rls-not-forced": {
65
+ readonly id: "pg-rls-not-forced";
66
+ readonly summary: "Postgres: a table's row-level security is enabled but not forced, so the table's owner (often the role that applies) bypasses its policies";
67
+ readonly level: "warning";
68
+ readonly silenceable: true;
69
+ readonly configurable: true;
70
+ readonly dialect: "postgres";
71
+ };
72
+ readonly "data-dependent": {
73
+ readonly id: "data-dependent";
74
+ readonly summary: "Postgres: a statement that can fail on the rows already there (a unique index over duplicates, SET NOT NULL over NULLs, a check or foreign key some row breaks); yodel apply runs its pre-check first and refuses when it counts any";
75
+ readonly level: "warning";
76
+ readonly silenceable: true;
77
+ readonly configurable: true;
78
+ readonly dialect: "postgres";
79
+ };
80
+ readonly naming: {
81
+ readonly id: "naming";
82
+ readonly summary: "a table, column, index or constraint the migration adds breaks yodel.config.ts lint.naming";
83
+ readonly level: "error";
84
+ readonly silenceable: true;
85
+ readonly configurable: true;
86
+ };
55
87
  readonly "ch-mutation": LintRule;
56
88
  readonly "ch-rebuild": LintRule;
89
+ readonly "ch-access": LintRule;
57
90
  readonly "refused-step": LintRule;
58
91
  readonly "migration-sql": LintRule;
59
92
  readonly silence: LintRule;
@@ -61,5 +94,5 @@ export declare const LINT_RULES: {
61
94
  export type LintRuleId = keyof typeof LINT_RULES;
62
95
  export declare const ruleIds: LintRuleId[];
63
96
  export declare const isRuleId: (id: string) => id is LintRuleId;
64
- /** The rules a `-- yodel:allow` line can name. */
65
- export declare const silenceableRules: () => LintRuleId[];
97
+ /** The rules a `-- yodel:allow` line can name: the statement rules, and the lexicon's schema checks. */
98
+ export declare const silenceableRules: () => string[];
@@ -9,17 +9,24 @@
9
9
  * config lowers them and nothing silences them, because a migration that
10
10
  * breaks one cannot be applied safely or cannot be checked at all.
11
11
  * - Statement rules, from the classes chant's classifier gives each step:
12
- * `destructive`, `pg-rewrite`, `pg-lock`, `pg-signature`, `ch-mutation`,
13
- * `ch-rebuild`.
12
+ * `destructive`, `pg-rewrite`, `pg-lock`, `pg-signature`, `pg-access`,
13
+ * `pg-rls-not-forced` (a warning), `ch-mutation`, `ch-rebuild`,
14
+ * `ch-access`, `data-dependent` (a warning: a statement that can fail
15
+ * on the rows already there, with the pre-check yodel apply runs first),
16
+ * and `naming` (the names a migration adds, against `lint.naming`).
14
17
  * Errors by default; a line `-- yodel:allow <rule> <reason>` above the
15
18
  * statement in `migration.sql` silences one for that statement, and the
16
19
  * reason is recorded in the history when the migration applies.
17
20
  *
21
+ * The lexicon's schema checks (`SQLPG101`, `SQLCH113`, ...) are rules too,
22
+ * in `./schema-checks.ts`: silenceable, at the level the check gives.
23
+ *
18
24
  * The rest (`refused-step`, `pg-routine-in-use`, `migration-sql`,
19
25
  * `silence`) say a migration will not apply as written or a silence is
20
26
  * wrong. Their level can be
21
27
  * changed in `yodel.config.ts`, but they cannot be silenced.
22
28
  */
29
+ import { SCHEMA_RULES } from "./schema-checks.js";
23
30
  const integrity = (id, summary) => ({ id, summary, level: "error", silenceable: false, configurable: false });
24
31
  const statement = (id, summary, dialect) => ({ id, summary, level: "error", silenceable: true, configurable: true, ...(dialect ? { dialect } : {}) });
25
32
  const shape = (id, summary, level = "error") => ({ id, summary, level, silenceable: false, configurable: true });
@@ -34,14 +41,19 @@ export const LINT_RULES = {
34
41
  "pg-lock": statement("pg-lock", "Postgres: holds a lock that blocks writes for a whole scan or index build (an index without CONCURRENTLY, a constraint validated in place)", "postgres"),
35
42
  "pg-signature": statement("pg-signature", "Postgres: a function or procedure dropped and created with another signature or result; a caller written for the old one fails", "postgres"),
36
43
  "pg-routine-in-use": { id: "pg-routine-in-use", summary: "Postgres: a function dropped while a trigger the migration's schema declares still executes it; the DROP fails", level: "error", silenceable: false, configurable: true, dialect: "postgres" },
44
+ "pg-access": statement("pg-access", "Postgres: takes access away: a privilege revoked, a policy dropped, or row-level security turned off or no longer forced on the table's owner", "postgres"),
45
+ "pg-rls-not-forced": { id: "pg-rls-not-forced", summary: "Postgres: a table's row-level security is enabled but not forced, so the table's owner (often the role that applies) bypasses its policies", level: "warning", silenceable: true, configurable: true, dialect: "postgres" },
46
+ "data-dependent": { id: "data-dependent", summary: "Postgres: a statement that can fail on the rows already there (a unique index over duplicates, SET NOT NULL over NULLs, a check or foreign key some row breaks); yodel apply runs its pre-check first and refuses when it counts any", level: "warning", silenceable: true, configurable: true, dialect: "postgres" },
47
+ naming: { id: "naming", summary: "a table, column, index or constraint the migration adds breaks yodel.config.ts lint.naming", level: "error", silenceable: true, configurable: true },
37
48
  "ch-mutation": statement("ch-mutation", "ClickHouse: starts a mutation that rewrites existing parts in the background, with no rollback", "clickhouse"),
38
49
  "ch-rebuild": statement("ch-rebuild", "ClickHouse: a rebuild, every row copied into a new table and swapped in (ClickHouseRebuildOp)", "clickhouse"),
50
+ "ch-access": statement("ch-access", "ClickHouse: takes access away: a privilege or role revoked from a user or role (SQLCH274)", "clickhouse"),
39
51
  "refused-step": shape("refused-step", "a step yodel apply refuses to run: a manual step, or an Op step it does not run"),
40
52
  "migration-sql": shape("migration-sql", "migration.sql does not hold a statement as migration.json records it; apply runs migration.json's"),
41
53
  silence: shape("silence", "a yodel:allow line that is malformed, names a rule no statement can silence, has no reason, or silences nothing"),
42
54
  };
43
55
  export const ruleIds = Object.keys(LINT_RULES);
44
56
  export const isRuleId = (id) => Object.hasOwn(LINT_RULES, id);
45
- /** The rules a `-- yodel:allow` line can name. */
46
- export const silenceableRules = () => ruleIds.filter((id) => LINT_RULES[id].silenceable);
57
+ /** The rules a `-- yodel:allow` line can name: the statement rules, and the lexicon's schema checks. */
58
+ export const silenceableRules = () => [...ruleIds.filter((id) => LINT_RULES[id].silenceable), ...Object.keys(SCHEMA_RULES)];
47
59
  //# sourceMappingURL=rules.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/lint/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAkBH,MAAM,SAAS,GAAG,CAAC,EAAU,EAAE,OAAe,EAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;AACxI,MAAM,SAAS,GAAG,CAAC,EAAU,EAAE,OAAe,EAAE,OAA6B,EAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AACtM,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,OAAe,EAAE,QAAmB,OAAO,EAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAEtJ,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,iFAAiF,CAAC;IAC1G,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE,oHAAoH,CAAC;IACvJ,QAAQ,EAAE,SAAS,CAAC,UAAU,EAAE,wHAAwH,CAAC;IACzJ,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,EAAE,mGAAmG,CAAC;IACxJ,UAAU,EAAE,SAAS,CAAC,YAAY,EAAE,wIAAwI,CAAC;IAC7K,WAAW,EAAE,SAAS,CAAC,aAAa,EAAE,uFAAuF,CAAC;IAC9H,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,6GAA6G,EAAE,UAAU,CAAC;IAChK,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,4IAA4I,EAAE,UAAU,CAAC;IACzL,cAAc,EAAE,SAAS,CAAC,cAAc,EAAE,gIAAgI,EAAE,UAAU,CAAC;IACvL,mBAAmB,EAAE,EAAE,EAAE,EAAE,mBAAmB,EAAE,OAAO,EAAE,gHAAgH,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE;IACxP,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE,gGAAgG,EAAE,YAAY,CAAC;IACvJ,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,+FAA+F,EAAE,YAAY,CAAC;IACpJ,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,iFAAiF,CAAC;IACxH,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE,mGAAmG,CAAC;IAC5I,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,iHAAiH,CAAC;CACjG,CAAC;AAI9C,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAiB,CAAC;AAE/D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAoB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAExF,kDAAkD;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAiB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC"}
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/lint/rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The sql lexicon's schema checks (#51) inside `yodel lint`: the post-synth
3
+ * checks `chant lint` runs over a build (a table with no primary key, a
4
+ * foreign key with no index, a column type to avoid, an engine or setting
5
+ * the pinned server does not have), run here on each migration's recorded
6
+ * schema, so a project has one lint command and one configuration.
7
+ *
8
+ * A finding is reported in the migration whose recorded schema first has
9
+ * it: the parent's schema is checked too, and what it already had is not
10
+ * reported again. It is put at the migration's first step on the object it
11
+ * names, so a `-- yodel:allow <id> <reason>` line above that step silences
12
+ * it, as for a statement rule.
13
+ *
14
+ * Levels: each check's rule id (`SQLPG101`, `SQLCH113`) is a lint rule.
15
+ * `yodel.config.ts` `lint.rules` sets it first; else `chant.config.ts`
16
+ * `lint.rules`, which `chant lint` reads; else the level the check gives
17
+ * the finding (an `info` finding is a warning here).
18
+ *
19
+ * The checks read the objects as `chant build` serializes them (their
20
+ * columns, keys and engine besides the DDL); an object recorded with its
21
+ * DDL only is not checked.
22
+ */
23
+ import type { BuildOutput } from "../chant/build.js";
24
+ import type { Migration } from "../migrations/index.js";
25
+ import type { LevelSetting, LintLevel, LintRule } from "./rules.js";
26
+ export type Dialect = "clickhouse" | "postgres";
27
+ /** Every schema check the lexicon ships, as a lint rule: silenceable, its level set in config. */
28
+ export declare const SCHEMA_RULES: Readonly<Record<string, LintRule>>;
29
+ export declare const isSchemaRule: (id: string) => boolean;
30
+ /** One schema check's finding in a recorded schema. */
31
+ export interface SchemaDiagnostic {
32
+ rule: string;
33
+ /** The level the check gave it. */
34
+ level: LintLevel;
35
+ message: string;
36
+ /** The export it names, when it names one. */
37
+ entity?: string;
38
+ }
39
+ /** Runs every schema check over one recorded schema. A check that throws is reported as a finding of its own. */
40
+ export declare function schemaDiagnostics(schema: BuildOutput): SchemaDiagnostic[];
41
+ /** A level from `chant.config.ts` `lint.rules`: a severity string, or `[severity, options]`. */
42
+ export declare function chantLevel(value: unknown): LevelSetting | undefined;
43
+ /** A schema finding before its silence is applied: where it is, and its level unless `yodel.config.ts` sets the rule's. */
44
+ export interface SchemaFinding {
45
+ rule: string;
46
+ migration: string;
47
+ message: string;
48
+ step?: number;
49
+ object?: string;
50
+ /** Undefined when `yodel.config.ts` sets the rule's level, which then applies. */
51
+ level?: LintLevel;
52
+ }
53
+ /**
54
+ * The schema findings of every migration: each check's findings on its
55
+ * recorded schema that its parent's recorded schema does not have.
56
+ * `configured` are the rule ids `yodel.config.ts` sets; `chantRules` is
57
+ * `chant.config.ts`'s `lint.rules`.
58
+ */
59
+ export declare function schemaFindings(migrations: readonly Migration[], options: {
60
+ dialect?: Dialect | undefined;
61
+ configured: ReadonlySet<string>;
62
+ chantRules?: Record<string, unknown> | undefined;
63
+ }): SchemaFinding[];
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The sql lexicon's schema checks (#51) inside `yodel lint`: the post-synth
3
+ * checks `chant lint` runs over a build (a table with no primary key, a
4
+ * foreign key with no index, a column type to avoid, an engine or setting
5
+ * the pinned server does not have), run here on each migration's recorded
6
+ * schema, so a project has one lint command and one configuration.
7
+ *
8
+ * A finding is reported in the migration whose recorded schema first has
9
+ * it: the parent's schema is checked too, and what it already had is not
10
+ * reported again. It is put at the migration's first step on the object it
11
+ * names, so a `-- yodel:allow <id> <reason>` line above that step silences
12
+ * it, as for a statement rule.
13
+ *
14
+ * Levels: each check's rule id (`SQLPG101`, `SQLCH113`) is a lint rule.
15
+ * `yodel.config.ts` `lint.rules` sets it first; else `chant.config.ts`
16
+ * `lint.rules`, which `chant lint` reads; else the level the check gives
17
+ * the finding (an `info` finding is a warning here).
18
+ *
19
+ * The checks read the objects as `chant build` serializes them (their
20
+ * columns, keys and engine besides the DDL); an object recorded with its
21
+ * DDL only is not checked.
22
+ */
23
+ import { postSynthChecks } from "@intentius/chant-lexicon-sql/lint/post-synth";
24
+ const dialectOf = (id) => (id.startsWith("SQLCH") ? "clickhouse" : id.startsWith("SQLPG") ? "postgres" : undefined);
25
+ /** Every schema check the lexicon ships, as a lint rule: silenceable, its level set in config. */
26
+ export const SCHEMA_RULES = Object.fromEntries(postSynthChecks.map((c) => {
27
+ const dialect = dialectOf(c.id);
28
+ return [c.id, { id: c.id, summary: `schema check: ${c.description}`, level: "warning", silenceable: true, configurable: true, schema: true, ...(dialect ? { dialect } : {}) }];
29
+ }));
30
+ export const isSchemaRule = (id) => Object.hasOwn(SCHEMA_RULES, id);
31
+ /** Objects as `chant build` writes them carry their name besides the DDL; one with the DDL alone is left out. */
32
+ const serialized = (o) => typeof o.name === "string";
33
+ /** Runs every schema check over one recorded schema. A check that throws is reported as a finding of its own. */
34
+ export function schemaDiagnostics(schema) {
35
+ if (!Array.isArray(schema.objects))
36
+ return [];
37
+ const value = { ...schema, objects: schema.objects.filter(serialized) };
38
+ if (value.objects.length === 0)
39
+ return [];
40
+ const outputs = new Map([["sql", JSON.stringify(value)]]);
41
+ const ctx = {
42
+ outputs,
43
+ entities: new Map(),
44
+ docs: [{ lexicon: "sql", index: 0, format: "json", value }],
45
+ buildResult: { outputs, entities: new Map(), warnings: [], errors: [], sourceFileCount: 0 },
46
+ };
47
+ const out = [];
48
+ for (const check of postSynthChecks) {
49
+ const dialect = dialectOf(check.id);
50
+ if (dialect !== undefined && dialect !== schema.dialect)
51
+ continue;
52
+ let found;
53
+ try {
54
+ found = check.check(ctx);
55
+ }
56
+ catch (e) {
57
+ out.push({ rule: check.id, level: "error", message: `${check.id} could not run on the recorded schema: ${(e instanceof Error ? e.message : String(e)).split("\n")[0] ?? ""}` });
58
+ continue;
59
+ }
60
+ for (const d of found) {
61
+ out.push({ rule: d.checkId, level: d.severity === "error" ? "error" : "warning", message: d.message, ...(d.entity !== undefined ? { entity: d.entity } : {}) });
62
+ }
63
+ }
64
+ return out;
65
+ }
66
+ /** A level from `chant.config.ts` `lint.rules`: a severity string, or `[severity, options]`. */
67
+ export function chantLevel(value) {
68
+ const s = Array.isArray(value) ? value[0] : value;
69
+ if (s === "off")
70
+ return "off";
71
+ if (s === "error")
72
+ return "error";
73
+ if (s === "warning" || s === "info")
74
+ return "warning";
75
+ return undefined;
76
+ }
77
+ const keyOf = (d) => `${d.rule}\u0000${d.entity ?? ""}\u0000${d.message}`;
78
+ /**
79
+ * The schema findings of every migration: each check's findings on its
80
+ * recorded schema that its parent's recorded schema does not have.
81
+ * `configured` are the rule ids `yodel.config.ts` sets; `chantRules` is
82
+ * `chant.config.ts`'s `lint.rules`.
83
+ */
84
+ export function schemaFindings(migrations, options) {
85
+ const byId = new Map(migrations.map((m) => [m.id, m]));
86
+ const cache = new Map();
87
+ const diagnostics = (m) => {
88
+ let d = cache.get(m.id);
89
+ if (!d) {
90
+ const schema = m.document.schema;
91
+ d = schema.dialect === "clickhouse" || schema.dialect === "postgres" ? schemaDiagnostics(schema) : [];
92
+ cache.set(m.id, d);
93
+ }
94
+ return d;
95
+ };
96
+ const out = [];
97
+ for (const m of migrations) {
98
+ const d = m.document.dialect;
99
+ if (d !== "clickhouse" && d !== "postgres")
100
+ continue;
101
+ if (options.dialect !== undefined && d !== options.dialect)
102
+ continue;
103
+ const parent = m.parent === null ? undefined : byId.get(m.parent);
104
+ const before = new Set((parent ? diagnostics(parent) : []).map(keyOf));
105
+ for (const diag of diagnostics(m)) {
106
+ if (before.has(keyOf(diag)))
107
+ continue;
108
+ let level;
109
+ if (!options.configured.has(diag.rule)) {
110
+ const set = chantLevel(options.chantRules?.[diag.rule]);
111
+ if (set === "off")
112
+ continue;
113
+ level = set ?? diag.level;
114
+ }
115
+ const step = diag.entity === undefined ? -1 : m.document.steps.findIndex((s) => s.object === diag.entity);
116
+ out.push({
117
+ rule: diag.rule,
118
+ migration: m.id,
119
+ message: `${step >= 0 ? `step ${String(step)}: ` : ""}${diag.message}`,
120
+ ...(step >= 0 ? { step } : {}),
121
+ ...(step >= 0 ? { object: m.document.steps[step]?.name ?? diag.entity } : diag.entity !== undefined ? { object: diag.entity } : {}),
122
+ ...(level !== undefined ? { level } : {}),
123
+ });
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+ //# sourceMappingURL=schema-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-checks.js","sourceRoot":"","sources":["../../src/lint/schema-checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AAO/E,MAAM,SAAS,GAAG,CAAC,EAAU,EAAuB,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAEjJ,kGAAkG;AAClG,MAAM,CAAC,MAAM,YAAY,GAAuC,MAAM,CAAC,WAAW,CAChF,eAAe,CAAC,GAAG,CAAC,CAAC,CAAiB,EAAE,EAAE;IACxC,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAqB,CAAC,CAAC;AACpM,CAAC,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;AAYrF,iHAAiH;AACjH,MAAM,UAAU,GAAG,CAAC,CAAU,EAAW,EAAE,CAAC,OAAQ,CAAwB,CAAC,IAAI,KAAK,QAAQ,CAAC;AAE/F,iHAAiH;AACjH,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;IACxE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAqB;QAC5B,OAAO;QACP,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC3D,WAAW,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE;KAC5F,CAAC;IACF,MAAM,GAAG,GAAuB,EAAE,CAAC;IACnC,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,MAAM,CAAC,OAAO;YAAE,SAAS;QAClE,IAAI,KAA4B,CAAC;QACjC,IAAI,CAAC;YACH,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,EAAE,0CAA0C,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAChL,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAClK,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAK,CAAC,CAAC,CAAa,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/D,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,MAAM;QAAE,OAAO,SAAS,CAAC;IACtD,OAAO,SAAS,CAAC;AACnB,CAAC;AAaD,MAAM,KAAK,GAAG,CAAC,CAAmB,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,MAAM,IAAI,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;AAE5F;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,UAAgC,EAAE,OAA6H;IAC5L,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpD,MAAM,WAAW,GAAG,CAAC,CAAY,EAAsB,EAAE;QACvD,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACxB,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjC,CAAC,GAAI,MAAM,CAAC,OAAmB,KAAK,YAAY,IAAK,MAAM,CAAC,OAAmB,KAAK,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;IACF,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAkB,CAAC;QACxC,IAAI,CAAC,KAAK,YAAY,IAAI,CAAC,KAAK,UAAU;YAAE,SAAS;QACrD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,KAAK,OAAO,CAAC,OAAO;YAAE,SAAS;QACrE,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACvE,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YAClC,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAE,SAAS;YACtC,IAAI,KAA4B,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACxD,IAAI,GAAG,KAAK,KAAK;oBAAE,SAAS;gBAC5B,KAAK,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC;YAC5B,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1G,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,SAAS,EAAE,CAAC,CAAC,EAAE;gBACf,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE;gBACtE,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9B,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnI,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -5,9 +5,18 @@
5
5
  *
6
6
  * - `destructive`: a statement marked destructive (a table or column drop),
7
7
  * or an Op step one of whose changes is.
8
+ * - `pg-access`: a Postgres statement that takes access away (#45): a
9
+ * privilege revoked (SQLPG297), a policy dropped (SQLPG292), row-level
10
+ * security turned off or no longer forced (SQLPG293). A revoke on an
11
+ * object the same migration creates (`REVOKE EXECUTE ... FROM PUBLIC` on
12
+ * a new function) takes nothing from anyone and is left out.
8
13
  * - `ch-mutation`: a ClickHouse statement of class `rewrite`, or one that
9
14
  * waits for its mutation (a column type or TTL change).
10
15
  * - `ch-rebuild`: a `ClickHouseRebuildOp` step.
16
+ * - `ch-access`: a ClickHouse statement that takes access away (#46): a
17
+ * privilege or role revoked (SQLCH274). ClickHouse takes no `REVOKE`
18
+ * declaration, so each one revokes what a grantee holds and no
19
+ * declaration grants any more.
11
20
  * - `pg-rewrite`: a Postgres statement of class `rewrite` (ACCESS EXCLUSIVE
12
21
  * for a rewrite or a full scan).
13
22
  * - `pg-lock`: a Postgres statement that blocks writes for a whole build or
@@ -17,6 +26,12 @@
17
26
  * foreign key or domain constraint validated in place rather than added
18
27
  * `NOT VALID` (SQLPG219, SQLPG262).
19
28
  *
29
+ * - `data-dependent`: a Postgres statement chant marks as able to fail on
30
+ * the rows already there (its `precheck`: a unique index over
31
+ * duplicates, SET NOT NULL over NULLs, a check or foreign key a row
32
+ * breaks). The finding shows the pre-check, which yodel apply runs before
33
+ * the migration's first statement and refuses on when it is not 0.
34
+ *
20
35
  * `COMMENT ON` statements are skipped: chant writes one after a `CREATE`
21
36
  * with the same rule, and a comment is catalog only.
22
37
  *
@@ -32,7 +47,13 @@ export interface StepFinding {
32
47
  }
33
48
  /** What lint cannot read in a step, or `undefined`. */
34
49
  export declare function unreadableStep(dialect: Dialect, step: MigrationStep): string | undefined;
50
+ /** What a step is read with: the names of the objects its migration creates (`schema.table`, `schema.fn(args)`), from {@link createdObjects}. */
51
+ export interface StepContext {
52
+ created?: ReadonlySet<string>;
53
+ }
54
+ /** The objects a migration's statements create, by the name each step carries. */
55
+ export declare function createdObjects(steps: readonly MigrationStep[]): Set<string>;
35
56
  /** The statement-rule findings for one step, before levels and silences. */
36
- export declare function stepFindings(dialect: Dialect, step: MigrationStep): StepFinding[];
57
+ export declare function stepFindings(dialect: Dialect, step: MigrationStep, context?: StepContext): StepFinding[];
37
58
  /** Why yodel apply refuses a step, or `undefined` for one it runs. */
38
59
  export declare function refusedStep(step: MigrationStep): string | undefined;