@intentius/chant-lexicon-sql 0.124.31 → 0.124.32

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 (322) hide show
  1. package/dist/change-set.js +250 -0
  2. package/dist/clickhouse/access.js +722 -0
  3. package/dist/clickhouse/apply/apply.js +243 -0
  4. package/dist/clickhouse/apply/mutations.js +66 -0
  5. package/dist/clickhouse/apply/statements.js +545 -0
  6. package/dist/clickhouse/catalog-types.js +7 -0
  7. package/dist/clickhouse/container.js +152 -0
  8. package/dist/clickhouse/engine-syntax.js +159 -0
  9. package/dist/clickhouse/entities.js +593 -0
  10. package/dist/clickhouse/expressions.js +107 -0
  11. package/dist/clickhouse/http.js +55 -0
  12. package/dist/clickhouse/import/generator.js +192 -0
  13. package/dist/clickhouse/import/inlined.js +207 -0
  14. package/dist/clickhouse/import/ir.js +160 -0
  15. package/dist/clickhouse/import/live-export.js +131 -0
  16. package/dist/clickhouse/import/parser.js +17 -0
  17. package/dist/clickhouse/live/bind.js +116 -0
  18. package/dist/clickhouse/live/catalog.js +223 -0
  19. package/dist/clickhouse/live/describe-resources.js +91 -0
  20. package/dist/clickhouse/live/undeclarable.js +59 -0
  21. package/dist/clickhouse/overlays/codecs.js +57 -0
  22. package/dist/clickhouse/overlays/engines.js +101 -0
  23. package/dist/clickhouse/overlays/kinds.js +1 -0
  24. package/dist/clickhouse/overlays/projections.js +26 -0
  25. package/dist/clickhouse/overlays/skip-indexes.js +40 -0
  26. package/dist/clickhouse/overlays/ttl.js +34 -0
  27. package/dist/clickhouse/overlays/types.js +59 -0
  28. package/dist/clickhouse/ownership.js +83 -0
  29. package/dist/clickhouse/parser.js +674 -0
  30. package/dist/clickhouse/plan/commands.js +159 -0
  31. package/dist/clickhouse/plan/deep.js +345 -0
  32. package/dist/clickhouse/plan/diff.js +312 -0
  33. package/dist/clickhouse/plan/disruption.js +73 -0
  34. package/dist/clickhouse/plan/normalize.js +545 -0
  35. package/dist/clickhouse/plan/rebuild-handoff.js +57 -0
  36. package/dist/clickhouse/plan/report.js +26 -0
  37. package/dist/clickhouse/plan/rules.js +72 -0
  38. package/dist/clickhouse/plan/schema.js +67 -0
  39. package/dist/clickhouse/plan/server-format.js +35 -0
  40. package/dist/clickhouse/rebuild/backfill.js +265 -0
  41. package/dist/clickhouse/rebuild/engines.js +146 -0
  42. package/dist/clickhouse/rebuild/observe.js +260 -0
  43. package/dist/clickhouse/rebuild/op.js +152 -0
  44. package/dist/clickhouse/rebuild/partitions.js +65 -0
  45. package/dist/clickhouse/rebuild/receipts.js +120 -0
  46. package/dist/clickhouse/rebuild/replicas.js +121 -0
  47. package/dist/clickhouse/rebuild/shards.js +76 -0
  48. package/dist/clickhouse/rebuild/steps.js +383 -0
  49. package/dist/clickhouse/rebuild/verify.js +279 -0
  50. package/dist/clickhouse/row-types.js +151 -0
  51. package/dist/clickhouse/testing/fake-server.js +47 -0
  52. package/dist/clickhouse/testing/roundtrip-schema.js +25 -0
  53. package/dist/clickhouse/testing/writable-server.js +123 -0
  54. package/dist/clickhouse/tokens.js +23 -0
  55. package/dist/clickhouse/topology.js +463 -0
  56. package/dist/clickhouse/type-syntax.js +73 -0
  57. package/dist/clickhouse.js +25 -0
  58. package/dist/codegen/classifier-page.js +93 -0
  59. package/dist/codegen/clickhouse-module.js +249 -0
  60. package/dist/codegen/coverage-page.js +70 -0
  61. package/dist/codegen/docs-cli.js +3 -0
  62. package/dist/codegen/docs.js +222 -0
  63. package/dist/codegen/generate-cli.js +13 -0
  64. package/dist/codegen/generate.js +97 -0
  65. package/dist/codegen/package.js +28 -0
  66. package/dist/codegen/postgres-classifier-page.js +95 -0
  67. package/dist/codegen/postgres-module.js +418 -0
  68. package/dist/codegen/registry.js +52 -0
  69. package/dist/composites/audit-log-table.js +35 -0
  70. package/dist/composites/catalog.js +524 -0
  71. package/dist/composites/cdc-mirror.js +32 -0
  72. package/dist/composites/clickhouse.js +6 -0
  73. package/dist/composites/events-table.js +26 -0
  74. package/dist/composites/index.js +8 -0
  75. package/dist/composites/join-table.js +25 -0
  76. package/dist/composites/postgres.js +6 -0
  77. package/dist/composites/refreshed-view.js +24 -0
  78. package/dist/composites/replacing-table.js +28 -0
  79. package/dist/composites/rollup-view.js +29 -0
  80. package/dist/composites/sharded-table.js +27 -0
  81. package/dist/composites/soft-delete-table.js +34 -0
  82. package/dist/composites/tenant-table.js +26 -0
  83. package/dist/composites/typed.js +8 -0
  84. package/dist/config.js +206 -0
  85. package/dist/core/apply.js +93 -0
  86. package/dist/core/classifier.js +115 -0
  87. package/dist/core/container.js +69 -0
  88. package/dist/core/cursor.js +261 -0
  89. package/dist/core/diff.js +48 -0
  90. package/dist/core/entity.js +58 -0
  91. package/dist/core/find-templates.js +118 -0
  92. package/dist/core/foreign-tables.js +27 -0
  93. package/dist/core/handoff.js +32 -0
  94. package/dist/core/index.js +64 -0
  95. package/dist/core/interpolation.js +191 -0
  96. package/dist/core/normalize.js +43 -0
  97. package/dist/core/ownership.js +119 -0
  98. package/dist/core/receipts.js +22 -0
  99. package/dist/core/references.js +61 -0
  100. package/dist/core/statements.js +42 -0
  101. package/dist/core/template.js +79 -0
  102. package/dist/core/testing/http-stub.js +21 -0
  103. package/dist/core/tokens.js +202 -0
  104. package/dist/core/typed.js +40 -0
  105. package/dist/core/undeclarable.js +46 -0
  106. package/dist/coverage.js +161 -0
  107. package/dist/detect.js +32 -0
  108. package/dist/dialects.js +18 -0
  109. package/dist/files/build-root.js +72 -0
  110. package/dist/files/clickhouse.js +138 -0
  111. package/dist/files/common.js +58 -0
  112. package/dist/files/postgres.js +389 -0
  113. package/dist/generated/clickhouse-types.js +5 -0
  114. package/dist/generated/clickhouse.js +4361 -0
  115. package/dist/generated/postgres-types.js +227 -0
  116. package/dist/generated/postgres.js +3667 -0
  117. package/dist/import-generator.js +14 -0
  118. package/dist/import-parser.js +71 -0
  119. package/dist/index.js +24 -0
  120. package/dist/init-templates.js +290 -0
  121. package/dist/integrity.json +2 -2
  122. package/dist/lint/audit-catalog.js +31 -0
  123. package/dist/lint/audit-lineage.js +22 -0
  124. package/dist/lint/post-synth/audit-entries.js +34 -0
  125. package/dist/lint/post-synth/clickhouse-helpers.js +79 -0
  126. package/dist/lint/post-synth/clickhouse-type-check.js +162 -0
  127. package/dist/lint/post-synth/index.js +112 -0
  128. package/dist/lint/post-synth/postgres-audit-entries.js +35 -0
  129. package/dist/lint/post-synth/postgres-helpers.js +65 -0
  130. package/dist/lint/post-synth/postgres-names.js +507 -0
  131. package/dist/lint/post-synth/sql-helpers.js +23 -0
  132. package/dist/lint/post-synth/sql101.js +59 -0
  133. package/dist/lint/post-synth/sql102.js +34 -0
  134. package/dist/lint/post-synth/sqlch101.js +34 -0
  135. package/dist/lint/post-synth/sqlch102.js +23 -0
  136. package/dist/lint/post-synth/sqlch103.js +45 -0
  137. package/dist/lint/post-synth/sqlch104.js +41 -0
  138. package/dist/lint/post-synth/sqlch105.js +36 -0
  139. package/dist/lint/post-synth/sqlch106.js +27 -0
  140. package/dist/lint/post-synth/sqlch107.js +39 -0
  141. package/dist/lint/post-synth/sqlch108.js +28 -0
  142. package/dist/lint/post-synth/sqlch109.js +21 -0
  143. package/dist/lint/post-synth/sqlch110.js +31 -0
  144. package/dist/lint/post-synth/sqlch111.js +41 -0
  145. package/dist/lint/post-synth/sqlch112.js +28 -0
  146. package/dist/lint/post-synth/sqlch113.js +19 -0
  147. package/dist/lint/post-synth/sqlch114.js +26 -0
  148. package/dist/lint/post-synth/sqlch115.js +30 -0
  149. package/dist/lint/post-synth/sqlch116.js +22 -0
  150. package/dist/lint/post-synth/sqlch117.js +26 -0
  151. package/dist/lint/post-synth/sqlch118.js +27 -0
  152. package/dist/lint/post-synth/sqlch119.js +32 -0
  153. package/dist/lint/post-synth/sqlch120.js +20 -0
  154. package/dist/lint/post-synth/sqlch121.js +11 -0
  155. package/dist/lint/post-synth/sqlch122.js +11 -0
  156. package/dist/lint/post-synth/sqlch123.js +10 -0
  157. package/dist/lint/post-synth/sqlch124.js +21 -0
  158. package/dist/lint/post-synth/sqlch125.js +55 -0
  159. package/dist/lint/post-synth/sqlch126.js +62 -0
  160. package/dist/lint/post-synth/sqlch127.js +132 -0
  161. package/dist/lint/post-synth/sqlpg101.js +25 -0
  162. package/dist/lint/post-synth/sqlpg102.js +35 -0
  163. package/dist/lint/post-synth/sqlpg103.js +23 -0
  164. package/dist/lint/post-synth/sqlpg104.js +23 -0
  165. package/dist/lint/post-synth/sqlpg105.js +22 -0
  166. package/dist/lint/post-synth/sqlpg106.js +22 -0
  167. package/dist/lint/post-synth/sqlpg107.js +22 -0
  168. package/dist/lint/post-synth/sqlpg108.js +28 -0
  169. package/dist/lint/post-synth/sqlpg109.js +39 -0
  170. package/dist/lint/post-synth/sqlpg110.js +38 -0
  171. package/dist/lint/post-synth/sqlpg111.js +23 -0
  172. package/dist/lint/post-synth/sqlpg112.js +25 -0
  173. package/dist/lint/post-synth/sqlpg113.js +29 -0
  174. package/dist/lint/post-synth/sqlpg114.js +39 -0
  175. package/dist/lint/post-synth/sqlpg115.js +48 -0
  176. package/dist/lint/post-synth/sqlpg116.js +26 -0
  177. package/dist/lint/post-synth/sqlpg117.js +24 -0
  178. package/dist/lint/post-synth/sqlpg118.js +22 -0
  179. package/dist/lint/post-synth/sqlpg119.js +41 -0
  180. package/dist/lint/post-synth/sqlpg120.js +83 -0
  181. package/dist/lint/post-synth/sqlpg121.js +33 -0
  182. package/dist/lint/post-synth/sqlpg122.js +27 -0
  183. package/dist/lint/post-synth/sqlpg123.js +69 -0
  184. package/dist/lint/post-synth/sqlpg124.js +50 -0
  185. package/dist/lint/post-synth/sqlpg125.js +41 -0
  186. package/dist/lint/post-synth/sqlpg126.js +38 -0
  187. package/dist/lint/rules/index.js +12 -0
  188. package/dist/lint/rules/postgres-templates.js +27 -0
  189. package/dist/lint/rules/sqlch001.js +79 -0
  190. package/dist/lint/rules/sqlch002.js +124 -0
  191. package/dist/lint/rules/sqlch003.js +69 -0
  192. package/dist/lint/rules/sqlch004.js +83 -0
  193. package/dist/lint/rules/sqlpg001.js +68 -0
  194. package/dist/lint/rules/sqlpg002.js +69 -0
  195. package/dist/lint/rules/sqlpg003.js +58 -0
  196. package/dist/lint/rules/sqlpg004.js +77 -0
  197. package/dist/lint/rules/sqlpg005.js +86 -0
  198. package/dist/lint/rules/templates.js +25 -0
  199. package/dist/lint/rules/typed-names.js +132 -0
  200. package/dist/live-dialect.js +32 -0
  201. package/dist/lsp/catalog.js +44 -0
  202. package/dist/lsp/completions.js +136 -0
  203. package/dist/lsp/hover.js +115 -0
  204. package/dist/lsp/postgres-catalog.js +176 -0
  205. package/dist/lsp/postgres-template.js +171 -0
  206. package/dist/lsp/postgres.js +266 -0
  207. package/dist/lsp/refs.js +60 -0
  208. package/dist/lsp/registry.d.ts.map +1 -1
  209. package/dist/lsp/registry.js +19 -0
  210. package/dist/lsp/template.js +143 -0
  211. package/dist/manifest.json +1 -1
  212. package/dist/mcp/index.d.ts.map +1 -1
  213. package/dist/mcp/index.js +293 -0
  214. package/dist/mcp/postgres.js +117 -0
  215. package/dist/migration-statements.js +243 -0
  216. package/dist/op/activities/clickhouse-apply.js +83 -0
  217. package/dist/op/activities/clickhouse-emulator.js +53 -0
  218. package/dist/op/activities/clickhouse-rebuild.js +119 -0
  219. package/dist/op/activities/index.js +26 -0
  220. package/dist/op/activities/postgres-apply.js +126 -0
  221. package/dist/op/activities/postgres-emulator.js +70 -0
  222. package/dist/op/activities/postgres-migration.js +156 -0
  223. package/dist/op/activities/sql-exec.js +57 -0
  224. package/dist/op/activity-contracts.js +103 -0
  225. package/dist/package-cli.js +14 -0
  226. package/dist/plugin.js +259 -0
  227. package/dist/postgres/access/acl.js +344 -0
  228. package/dist/postgres/access/changes.js +30 -0
  229. package/dist/postgres/access/import.js +61 -0
  230. package/dist/postgres/access/live.js +96 -0
  231. package/dist/postgres/apply/apply.js +557 -0
  232. package/dist/postgres/apply/statements.js +882 -0
  233. package/dist/postgres/catalog-types.js +18 -0
  234. package/dist/postgres/container.js +58 -0
  235. package/dist/postgres/entities.js +1390 -0
  236. package/dist/postgres/entity-types.js +28 -0
  237. package/dist/postgres/import/generator.js +283 -0
  238. package/dist/postgres/import/ir.js +53 -0
  239. package/dist/postgres/import/live-export.js +83 -0
  240. package/dist/postgres/keywords.js +32 -0
  241. package/dist/postgres/live/bind.js +130 -0
  242. package/dist/postgres/live/catalog-kinds.js +301 -0
  243. package/dist/postgres/live/catalog.js +644 -0
  244. package/dist/postgres/live/client.js +151 -0
  245. package/dist/postgres/live/describe-resources.js +135 -0
  246. package/dist/postgres/live/undeclarable.js +111 -0
  247. package/dist/postgres/migrate/backfill.js +170 -0
  248. package/dist/postgres/migrate/batches.js +122 -0
  249. package/dist/postgres/migrate/carry.js +518 -0
  250. package/dist/postgres/migrate/handoff.js +59 -0
  251. package/dist/postgres/migrate/names.js +61 -0
  252. package/dist/postgres/migrate/observe.js +360 -0
  253. package/dist/postgres/migrate/op.js +178 -0
  254. package/dist/postgres/migrate/receipts.js +117 -0
  255. package/dist/postgres/migrate/replication.js +106 -0
  256. package/dist/postgres/migrate/steps.js +514 -0
  257. package/dist/postgres/overlays/columns.js +31 -0
  258. package/dist/postgres/overlays/constraints.js +52 -0
  259. package/dist/postgres/overlays/extension-objects.js +88 -0
  260. package/dist/postgres/overlays/indexes.js +25 -0
  261. package/dist/postgres/overlays/kinds.js +2 -0
  262. package/dist/postgres/overlays/partitioning.js +23 -0
  263. package/dist/postgres/overlays/relations.js +25 -0
  264. package/dist/postgres/overlays/storage.js +65 -0
  265. package/dist/postgres/overlays/type-ddl.js +36 -0
  266. package/dist/postgres/overlays/types.js +74 -0
  267. package/dist/postgres/parser.js +1824 -0
  268. package/dist/postgres/plan/commands.js +137 -0
  269. package/dist/postgres/plan/deep.js +324 -0
  270. package/dist/postgres/plan/diff.js +499 -0
  271. package/dist/postgres/plan/disruption.js +64 -0
  272. package/dist/postgres/plan/normalize.js +889 -0
  273. package/dist/postgres/plan/report.js +21 -0
  274. package/dist/postgres/plan/rules.js +115 -0
  275. package/dist/postgres/plan/schema.js +105 -0
  276. package/dist/postgres/plan/server-normalize.js +166 -0
  277. package/dist/postgres/providers/aurora.js +44 -0
  278. package/dist/postgres/providers/azure.js +53 -0
  279. package/dist/postgres/providers/cloud-sql.js +41 -0
  280. package/dist/postgres/providers/index.js +73 -0
  281. package/dist/postgres/providers/neon.js +44 -0
  282. package/dist/postgres/providers/rds.js +47 -0
  283. package/dist/postgres/providers/supabase.js +45 -0
  284. package/dist/postgres/providers/types.js +21 -0
  285. package/dist/postgres/row-types.js +107 -0
  286. package/dist/postgres/testing/fake-client.js +34 -0
  287. package/dist/postgres/testing/migration.js +48 -0
  288. package/dist/postgres/testing/roundtrip-schema.js +64 -0
  289. package/dist/postgres/testing/server.js +47 -0
  290. package/dist/postgres/testing/writable-server.js +160 -0
  291. package/dist/postgres/tokens.js +41 -0
  292. package/dist/postgres.js +15 -0
  293. package/dist/read/clickhouse.js +82 -0
  294. package/dist/read/objects.js +44 -0
  295. package/dist/read/postgres.js +104 -0
  296. package/dist/read.js +52 -0
  297. package/dist/receipts.js +207 -0
  298. package/dist/serializer.js +172 -0
  299. package/dist/skill-defs.js +93 -0
  300. package/dist/spec/catalog.js +191 -0
  301. package/dist/spec/fetch.d.ts.map +1 -1
  302. package/dist/spec/fetch.js +90 -0
  303. package/dist/spec/pin.js +42 -0
  304. package/dist/spec/postgres-catalog.js +171 -0
  305. package/dist/spec/postgres-fetch.js +91 -0
  306. package/dist/spec/postgres-pin.js +82 -0
  307. package/dist/sql-files.js +206 -0
  308. package/dist/sql-types-command.js +97 -0
  309. package/dist/sql-types.js +323 -0
  310. package/dist/token-source.js +156 -0
  311. package/dist/validate-cli.js +5 -0
  312. package/dist/validate.js +151 -0
  313. package/dist/workspace-outputs.js +191 -0
  314. package/package.json +7 -7
  315. package/src/codegen/package.ts +2 -2
  316. package/src/lint/rules/typescript-7.test.ts +2 -1
  317. package/src/lsp/catalog.ts +2 -2
  318. package/src/lsp/postgres-catalog.ts +2 -2
  319. package/src/lsp/registry.ts +2 -1
  320. package/src/mcp/index.ts +4 -2
  321. package/src/spec/fetch.ts +2 -2
  322. package/src/spec/postgres-fetch.ts +2 -2
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Names in a Postgres declaration, read against the target major's catalog:
3
+ * column types, function calls, index access methods and operator classes.
4
+ * SQLPG119 to SQLPG126 share it.
5
+ *
6
+ * The line every check here keeps: a name the catalog or the project can
7
+ * answer for is checked; a name qualified with a schema the project does not
8
+ * declare (`billing.money`, `pg_catalog.int4`) is a plain-text reference and
9
+ * left to the server; a name the core catalog lacks is a warning, not an
10
+ * error, when the project declares an extension, since extension objects are
11
+ * not in the catalog (`overlays/extension-objects.ts` covers the common ones).
12
+ */
13
+ import { COLUMN_TYPES, FUNCTIONS, INDEX_ACCESS_METHODS, KEYWORDS, OPERATOR_CLASSES, POSTGRES_MAJORS } from "../../generated/postgres.js";
14
+ import { EXTENSION_OBJECTS } from "../../postgres/overlays/extension-objects.js";
15
+ import { GRAMMAR_TYPE_SPELLINGS, SERIAL_TYPES, SERIAL_WIDTH_SPELLINGS, TYPE_SPELLINGS } from "../../postgres/overlays/types.js";
16
+ import { isTrivia, tokenizeText } from "../../postgres/tokens.js";
17
+ import { dialectObjects, recordedPostgresMajor } from "../../read/objects.js";
18
+ const presentAt = (p, major) => p === true || (Array.isArray(p) && p.includes(major));
19
+ const majorsOf = (p) => (Array.isArray(p) ? p : []);
20
+ const columnTypes = COLUMN_TYPES;
21
+ /** Every spelling of a built-in type (lower case, single spaces) to its `format_type()` name. */
22
+ const BUILTIN_SPELLINGS = (() => {
23
+ const m = new Map();
24
+ for (const [sql, spec] of Object.entries(columnTypes)) {
25
+ m.set(sql, sql);
26
+ if (spec.catalogName)
27
+ m.set(spec.catalogName, sql);
28
+ for (const a of spec.aliases)
29
+ if (!m.has(a))
30
+ m.set(a, sql);
31
+ }
32
+ for (const [a, sql] of [...Object.entries(TYPE_SPELLINGS), ...Object.entries(GRAMMAR_TYPE_SPELLINGS)])
33
+ if (!m.has(a))
34
+ m.set(a, sql);
35
+ // `char` alone is `character` (bpchar); quoted, `"char"` is the one-byte type.
36
+ m.set("char", "character");
37
+ return m;
38
+ })();
39
+ const SERIALS = { ...SERIAL_TYPES, ...SERIAL_WIDTH_SPELLINGS };
40
+ /** The words that may follow `interval`: its fields. */
41
+ const INTERVAL_FIELDS = new Set(["year", "month", "day", "hour", "minute", "second", "to"]);
42
+ /** The catalog category of a built-in type (`N` numeric, `S` string...). */
43
+ export const categoryOf = (canonical) => columnTypes[canonical]?.category;
44
+ /** The parameters a built-in type takes, from the overlay. */
45
+ export const parametersOf = (canonical) => columnTypes[canonical]?.parameters ?? [];
46
+ // ── Identifiers ────────────────────────────────────────────────────────
47
+ /** An identifier's value: a quoted one as written, a bare one folded to lower case. */
48
+ export function identValue(text) {
49
+ const t = text.trim();
50
+ if (t.startsWith('"') && t.endsWith('"') && t.length >= 2)
51
+ return t.slice(1, -1).replace(/""/g, '"');
52
+ return t.toLowerCase();
53
+ }
54
+ /** Split on commas at depth 0. */
55
+ export function splitTopLevel(text) {
56
+ const out = [];
57
+ let depth = 0;
58
+ let quote;
59
+ let cur = "";
60
+ for (const ch of text) {
61
+ if (quote) {
62
+ cur += ch;
63
+ if (ch === quote)
64
+ quote = undefined;
65
+ continue;
66
+ }
67
+ if (ch === '"' || ch === "'")
68
+ quote = ch;
69
+ else if (ch === "(" || ch === "[")
70
+ depth++;
71
+ else if (ch === ")" || ch === "]")
72
+ depth--;
73
+ else if (ch === "," && depth === 0) {
74
+ out.push(cur.trim());
75
+ cur = "";
76
+ continue;
77
+ }
78
+ cur += ch;
79
+ }
80
+ if (cur.trim())
81
+ out.push(cur.trim());
82
+ return out;
83
+ }
84
+ /** The bare column names of an `INCLUDE` list as the output keeps it (`b, "X"`, or `(b, c)`). */
85
+ export function includeColumns(text) {
86
+ if (!text)
87
+ return [];
88
+ const inner = text.trim().replace(/^\(/, "").replace(/\)$/, "");
89
+ return splitTopLevel(inner).filter((s) => /^("([^"]|"")+"|[A-Za-z_][\w$]*)$/.test(s)).map(identValue);
90
+ }
91
+ function significant(text) {
92
+ try {
93
+ return tokenizeText(text, 0).filter((t) => !isTrivia(t));
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ }
99
+ const isName = (t) => t?.kind === "ident" || t?.kind === "qident";
100
+ const isPunct = (t, p) => t?.kind === "punct" && t.text === p;
101
+ const nameOf = (t) => (t.kind === "qident" ? identValue(t.text) : t.text.toLowerCase());
102
+ export function projectOf(ctx) {
103
+ return projectFromObjects(dialectObjects(ctx, "postgres"), recordedPostgresMajor(ctx, POSTGRES_MAJORS[POSTGRES_MAJORS.length - 1]));
104
+ }
105
+ /** The project a set of output objects declares, at `major`: for a reader that holds the build output itself (`chant sql types`). */
106
+ export function projectFromObjects(objects, major) {
107
+ const byExport = new Map(objects.map((o) => [o.export, o]));
108
+ const schemas = new Set(objects.filter((o) => o.type === "Postgres::Schema").map((o) => o.name));
109
+ const exts = objects.filter((o) => o.type === "Postgres::Extension");
110
+ return {
111
+ major,
112
+ objects,
113
+ byExport,
114
+ schemas,
115
+ extensions: new Set(exts.map((o) => o.name)),
116
+ };
117
+ }
118
+ const TYPE_OBJECTS = new Set(["Postgres::Enum", "Postgres::CompositeType", "Postgres::RangeType", "Postgres::Domain", "Postgres::Table", "Postgres::View", "Postgres::MaterializedView"]);
119
+ const ROUTINES = new Set(["Postgres::Function", "Postgres::Procedure"]);
120
+ /** A range type's multirange type's name: as written (its last piece), else as Postgres derives it. */
121
+ function multirangeOf(o) {
122
+ if (o.type !== "Postgres::RangeType")
123
+ return undefined;
124
+ const written = o.multirangeTypeName;
125
+ if (written) {
126
+ const last = written.split(".").pop().trim();
127
+ return /^"/.test(last) ? last.slice(1, -1).replace(/""/g, '"') : last.toLowerCase();
128
+ }
129
+ const name = String(o.name);
130
+ const at = name.indexOf("range");
131
+ return at >= 0 ? `${name.slice(0, at)}multi${name.slice(at)}` : `${name}_multirange`;
132
+ }
133
+ function declared(p, kinds, schema, name) {
134
+ return p.objects.find((o) => kinds.has(o.type) && (o.name === name || multirangeOf(o) === name) && (schema === undefined || o.schema === schema));
135
+ }
136
+ /** The extension that provides `name` in `section`, if the overlay knows one; declared ones first. */
137
+ function extensionFor(p, test) {
138
+ let found;
139
+ for (const [name, e] of Object.entries(EXTENSION_OBJECTS)) {
140
+ if (!test(e))
141
+ continue;
142
+ if (p.extensions.has(name))
143
+ return { name, declared: true };
144
+ found ?? (found = { name, declared: false });
145
+ }
146
+ return found;
147
+ }
148
+ function miss(p, ext) {
149
+ if (ext?.declared)
150
+ return undefined;
151
+ if (ext)
152
+ return { severity: "error", note: `; it comes from the ${ext.name} extension, which the project does not declare` };
153
+ if (p.extensions.size > 0)
154
+ return { severity: "warning", note: `; if an extension (${[...p.extensions].sort().join(", ")}) provides it, ignore this` };
155
+ return { severity: "error", note: "" };
156
+ }
157
+ /** A schema-qualified name the project cannot answer for: one in a schema it does not declare. */
158
+ const outsideProject = (p, schema) => !p.schemas.has(schema);
159
+ /** A type as written in a column, domain or sequence: `numeric(12,2)`, `time(3) with time zone`, `app.mood[]`. Undefined when it is not a plain type name. */
160
+ export function parseType(text) {
161
+ if (!text)
162
+ return undefined;
163
+ const toks = significant(text);
164
+ if (!toks || toks.length === 0)
165
+ return undefined;
166
+ const words = [];
167
+ let schema;
168
+ let quoted = false;
169
+ let modifiers;
170
+ let array = false;
171
+ for (let i = 0; i < toks.length; i++) {
172
+ const t = toks[i];
173
+ if (isName(t)) {
174
+ if (t.kind === "ident" && t.text.toLowerCase() === "array" && words.length > 0) {
175
+ array = true;
176
+ continue;
177
+ }
178
+ if (array)
179
+ return undefined;
180
+ if (isPunct(toks[i + 1], ".") && words.length === 0 && schema === undefined && isName(toks[i + 2])) {
181
+ schema = nameOf(t);
182
+ i++;
183
+ continue;
184
+ }
185
+ if (t.kind === "qident")
186
+ quoted = true;
187
+ words.push(nameOf(t));
188
+ }
189
+ else if (isPunct(t, "(")) {
190
+ let depth = 1;
191
+ let j = i + 1;
192
+ for (; j < toks.length && depth > 0; j++) {
193
+ if (isPunct(toks[j], "("))
194
+ depth++;
195
+ else if (isPunct(toks[j], ")"))
196
+ depth--;
197
+ }
198
+ if (depth !== 0)
199
+ return undefined;
200
+ if (modifiers === undefined) {
201
+ const start = t.end;
202
+ const end = toks[j - 1].start;
203
+ modifiers = splitTopLevel(text.slice(start, end));
204
+ }
205
+ i = j - 1;
206
+ }
207
+ else if (isPunct(t, "[")) {
208
+ array = true;
209
+ let j = i + 1;
210
+ while (j < toks.length && !isPunct(toks[j], "]"))
211
+ j++;
212
+ i = j;
213
+ }
214
+ else {
215
+ return undefined;
216
+ }
217
+ }
218
+ if (words.length === 0)
219
+ return undefined;
220
+ if (!quoted && words[0] === "interval" && words.length > 1 && words.slice(1).every((w) => INTERVAL_FIELDS.has(w))) {
221
+ return { schema, name: "interval", quoted, modifiers, array, fields: words.slice(1).join(" ").toUpperCase() };
222
+ }
223
+ return { schema, name: words.join(" "), quoted, modifiers, array };
224
+ }
225
+ /** Resolve a parsed type at the target major. `serial` admits `serial` and friends (a column). */
226
+ export function resolveType(p, t, opts = {}) {
227
+ if (t.schema !== undefined) {
228
+ if (outsideProject(p, t.schema))
229
+ return { kind: "outside" };
230
+ const d = declared(p, TYPE_OBJECTS, t.schema, t.name);
231
+ if (d)
232
+ return { kind: "declared", object: d };
233
+ const m = miss(p, extensionFor(p, (e) => e.types?.includes(t.name) ?? false));
234
+ return m ? { kind: "missing", miss: m } : { kind: "outside" };
235
+ }
236
+ if (!t.quoted) {
237
+ if (opts.serial && SERIALS[t.name] && !t.modifiers)
238
+ return { kind: "builtin", canonical: SERIALS[t.name], serial: true };
239
+ if (t.name === "float" && t.modifiers?.length === 1 && /^\d+$/.test(t.modifiers[0])) {
240
+ return { kind: "builtin", canonical: Number(t.modifiers[0]) <= 24 ? "real" : "double precision" };
241
+ }
242
+ const canonical = BUILTIN_SPELLINGS.get(t.name) ?? (t.name.startsWith("_") ? BUILTIN_SPELLINGS.get(t.name.slice(1)) : undefined);
243
+ if (canonical)
244
+ return { kind: "builtin", canonical };
245
+ }
246
+ else {
247
+ if (t.name === "char")
248
+ return { kind: "builtin", canonical: '"char"' };
249
+ const byCatalog = Object.entries(columnTypes).find(([sql, s]) => (s.catalogName ?? sql) === t.name);
250
+ if (byCatalog)
251
+ return { kind: "builtin", canonical: byCatalog[0] };
252
+ }
253
+ const d = declared(p, TYPE_OBJECTS, undefined, t.name);
254
+ if (d)
255
+ return { kind: "declared", object: d };
256
+ const m = miss(p, extensionFor(p, (e) => e.types?.includes(t.name) ?? false));
257
+ return m ? { kind: "missing", miss: m } : { kind: "outside" };
258
+ }
259
+ /** A type as the user wrote it, for a message. */
260
+ export const typeLabel = (t) => `${t.schema ? `${t.schema}.` : ""}${t.name}`;
261
+ /**
262
+ * A comparable identity for a column's type, for a foreign key: a built-in
263
+ * type's catalog category (`U`, the catch-all, by name), a domain's base type,
264
+ * an enum or row type by name; `[]` for an array. Undefined when it cannot be
265
+ * told (a type outside the project).
266
+ */
267
+ export function typeIdentity(p, text, depth = 0) {
268
+ const t = parseType(text);
269
+ if (!t || depth > 5)
270
+ return undefined;
271
+ const r = resolveType(p, t, { serial: true });
272
+ const suffix = t.array ? "[]" : "";
273
+ if (r.kind === "builtin") {
274
+ const cat = categoryOf(r.canonical);
275
+ if (!cat)
276
+ return undefined;
277
+ return (cat === "U" ? `U:${r.canonical}` : cat) + suffix;
278
+ }
279
+ if (r.kind === "declared") {
280
+ if (r.object.type === "Postgres::Domain") {
281
+ const base = typeIdentity(p, r.object.dataType, depth + 1);
282
+ return base === undefined ? undefined : base + suffix;
283
+ }
284
+ return `${r.object.type}:${r.object.sqlName}${suffix}`;
285
+ }
286
+ return undefined;
287
+ }
288
+ // ── Calls ──────────────────────────────────────────────────────────────
289
+ /**
290
+ * Words that take a parenthesis in an expression without being a call to a
291
+ * function in `pg_proc`. Reserved and column-name key words (`COALESCE`,
292
+ * `CAST`, `EXTRACT`, `ROW`, `ARRAY`, `EXISTS`, `IN`, `ANY`, `SUBSTRING`...)
293
+ * are not function names to the grammar and are skipped by their category;
294
+ * these are the unreserved ones that read the same way.
295
+ */
296
+ const NOT_CALLS = new Set(["filter", "over", "within", "zone", "varying", "precision", "value", "and", "or", "not", "as", "then", "else", "when"]);
297
+ /** The function calls in an expression: a name directly followed by `(`, outside string literals, not a key word construct. */
298
+ export function callsIn(expr) {
299
+ if (!expr)
300
+ return [];
301
+ const toks = significant(expr);
302
+ if (!toks)
303
+ return [];
304
+ const out = [];
305
+ for (let i = 0; i < toks.length; i++) {
306
+ const t = toks[i];
307
+ if (!isName(t))
308
+ continue;
309
+ const prev = toks[i - 1];
310
+ if (isPunct(prev, "."))
311
+ continue;
312
+ // A type's modifiers after a cast: `x::numeric(10, 2)`, `CAST(x AS varchar(3))`.
313
+ if (prev?.kind === "op" && prev.text === "::")
314
+ continue;
315
+ if (prev?.kind === "ident" && prev.text.toLowerCase() === "as")
316
+ continue;
317
+ if (isPunct(toks[i + 1], ".") && isName(toks[i + 2]) && isPunct(toks[i + 3], "(")) {
318
+ out.push({ schema: nameOf(t), name: nameOf(toks[i + 2]) });
319
+ i += 2;
320
+ continue;
321
+ }
322
+ if (!isPunct(toks[i + 1], "("))
323
+ continue;
324
+ if (t.kind === "ident") {
325
+ const w = t.text.toLowerCase();
326
+ const cat = KEYWORDS[w];
327
+ if (cat === "R" || cat === "C" || cat === "T")
328
+ continue;
329
+ if (NOT_CALLS.has(w))
330
+ continue;
331
+ }
332
+ out.push({ name: nameOf(t) });
333
+ }
334
+ return out;
335
+ }
336
+ /** Resolve a call at the target major. */
337
+ export function resolveCall(p, c) {
338
+ if (c.schema !== undefined) {
339
+ if (outsideProject(p, c.schema))
340
+ return { ok: true };
341
+ if (declared(p, ROUTINES, c.schema, c.name))
342
+ return { ok: true };
343
+ const m = miss(p, extensionFor(p, (e) => providesFunction(e, c.name)));
344
+ return m ? { ok: false, miss: m } : { ok: true };
345
+ }
346
+ const presence = FUNCTIONS[c.name];
347
+ if (presentAt(presence, p.major))
348
+ return { ok: true };
349
+ if (declared(p, ROUTINES, undefined, c.name))
350
+ return { ok: true };
351
+ // A function-style cast: `int4(x)`, `text(x)`, or a declared type's name.
352
+ if (BUILTIN_SPELLINGS.has(c.name) || declared(p, TYPE_OBJECTS, undefined, c.name))
353
+ return { ok: true };
354
+ if (presence !== undefined) {
355
+ const ms = majorsOf(presence);
356
+ const since = ms[0] > p.major ? ms[0] : undefined;
357
+ const until = ms[ms.length - 1] < p.major ? ms[ms.length - 1] : undefined;
358
+ return { ok: false, miss: { severity: "error", note: "" }, since, until };
359
+ }
360
+ const m = miss(p, extensionFor(p, (e) => providesFunction(e, c.name)));
361
+ return m ? { ok: false, miss: m } : { ok: true };
362
+ }
363
+ function providesFunction(e, name) {
364
+ return (e.functions?.includes(name) ?? false) || (e.functionPrefixes?.some((pre) => name.startsWith(pre)) ?? false);
365
+ }
366
+ /** The expressions of a table, domain or index that may call functions, each with where it sits. */
367
+ export function expressionsOf(o) {
368
+ const out = [];
369
+ if (o.type === "Postgres::Table") {
370
+ const t = o;
371
+ for (const c of t.columns) {
372
+ if (c.default)
373
+ out.push({ where: `column ${c.name}'s DEFAULT`, expr: c.default });
374
+ if (c.generated?.expr)
375
+ out.push({ where: `generated column ${c.name}`, expr: c.generated.expr });
376
+ }
377
+ for (const k of t.checks)
378
+ out.push({ where: k.name ? `CHECK ${k.name}` : "a CHECK", expr: k.expr });
379
+ }
380
+ else if (o.type === "Postgres::Domain") {
381
+ if (o.default)
382
+ out.push({ where: "its DEFAULT", expr: o.default });
383
+ for (const k of o.checks ?? [])
384
+ out.push({ where: k.name ? `CHECK ${k.name}` : "a CHECK", expr: k.expr });
385
+ }
386
+ else if (o.type === "Postgres::Index") {
387
+ for (const e of o.elements ?? []) {
388
+ if (e.column)
389
+ continue;
390
+ const el = parseIndexElement(e.expr);
391
+ out.push({ where: "an index expression", expr: el?.expr ?? e.expr });
392
+ }
393
+ }
394
+ return out;
395
+ }
396
+ const ORDERING = new Set(["asc", "desc", "nulls", "first", "last"]);
397
+ /** An index element (`(lower(x)) text_pattern_ops DESC`, `x gin_trgm_ops`) split into its expression and operator class. */
398
+ export function parseIndexElement(text) {
399
+ const toks = significant(text);
400
+ if (!toks || toks.length === 0)
401
+ return undefined;
402
+ let i = 0;
403
+ const skipGroup = (at) => {
404
+ let depth = 0;
405
+ for (let j = at; j < toks.length; j++) {
406
+ if (isPunct(toks[j], "("))
407
+ depth++;
408
+ else if (isPunct(toks[j], ")") && --depth === 0)
409
+ return j + 1;
410
+ }
411
+ return toks.length;
412
+ };
413
+ if (isPunct(toks[0], "("))
414
+ i = skipGroup(0);
415
+ else if (isName(toks[0])) {
416
+ i = 1;
417
+ while (isPunct(toks[i], ".") && isName(toks[i + 1]))
418
+ i += 2;
419
+ if (isPunct(toks[i], "("))
420
+ i = skipGroup(i);
421
+ }
422
+ else
423
+ return undefined;
424
+ const expr = text.slice(toks[0].start, toks[i - 1].end);
425
+ if (toks[i]?.kind === "ident" && toks[i].text.toLowerCase() === "collate") {
426
+ i += 2;
427
+ while (isPunct(toks[i], ".") && isName(toks[i + 1]))
428
+ i += 2;
429
+ }
430
+ const t = toks[i];
431
+ if (!isName(t) || (t.kind === "ident" && ORDERING.has(t.text.toLowerCase())))
432
+ return { expr };
433
+ if (isPunct(toks[i + 1], ".") && isName(toks[i + 2]))
434
+ return { expr, opclass: { schema: nameOf(t), name: nameOf(toks[i + 2]) } };
435
+ return { expr, opclass: { name: nameOf(t) } };
436
+ }
437
+ /** An index access method at the target major. */
438
+ export function resolveMethod(p, method) {
439
+ const presence = INDEX_ACCESS_METHODS[method];
440
+ if (presentAt(presence, p.major))
441
+ return { ok: true };
442
+ if (presence !== undefined)
443
+ return { ok: false, miss: { severity: "error", note: "" } };
444
+ const m = miss(p, extensionFor(p, (e) => e.accessMethods?.includes(method) ?? false));
445
+ return m ? { ok: false, miss: m } : { ok: true };
446
+ }
447
+ /** An operator class for `method` at the target major. */
448
+ export function resolveOpClass(p, method, opclass) {
449
+ if (opclass.schema !== undefined && outsideProject(p, opclass.schema))
450
+ return { ok: true };
451
+ const classes = OPERATOR_CLASSES;
452
+ if (presentAt(classes[method]?.[opclass.name], p.major))
453
+ return { ok: true };
454
+ const ext = extensionFor(p, (e) => e.opclasses?.[method]?.includes(opclass.name) ?? false);
455
+ if (ext?.declared)
456
+ return { ok: true };
457
+ if (!ext) {
458
+ const others = Object.keys(classes).filter((am) => am !== method && presentAt(classes[am][opclass.name], p.major));
459
+ const extOthers = Object.entries(EXTENSION_OBJECTS).flatMap(([, e]) => Object.entries(e.opclasses ?? {}).filter(([am, names]) => am !== method && names.includes(opclass.name)).map(([am]) => am));
460
+ const all = [...new Set([...others, ...extOthers])].sort();
461
+ if (all.length > 0)
462
+ return { ok: false, miss: { severity: "error", note: "" }, otherMethods: all };
463
+ }
464
+ const m = miss(p, ext);
465
+ return m ? { ok: false, miss: m } : { ok: true };
466
+ }
467
+ // ── Tables ─────────────────────────────────────────────────────────────
468
+ /** The table an interpolated reference names (`references`, `table`, a grant's object): the entity when it is one, else undefined. */
469
+ export function interpolatedTable(p, ref, sqlName) {
470
+ if (typeof ref !== "string")
471
+ return undefined;
472
+ const o = p.byExport.get(ref);
473
+ if (!o || o.type !== "Postgres::Table")
474
+ return undefined;
475
+ if (sqlName !== undefined && o.sqlName !== sqlName)
476
+ return undefined;
477
+ return o;
478
+ }
479
+ /**
480
+ * Every column a table has, its own and those it inherits or copies from a
481
+ * declared parent; undefined when some come from somewhere the build cannot
482
+ * see (a plain-text parent, `LIKE`, `OF type`).
483
+ */
484
+ export function columnsOf(p, t, seen = new Set()) {
485
+ if (seen.has(t.export))
486
+ return undefined;
487
+ seen.add(t.export);
488
+ if (t.ofType !== undefined || t.like.length > 0)
489
+ return undefined;
490
+ const out = new Map();
491
+ const parents = [...(t.inherits ?? []), ...(t.partitionOf !== undefined ? [t.partitionOf] : [])];
492
+ for (const parent of parents) {
493
+ const pt = interpolatedTable(p, parent, undefined);
494
+ if (!pt)
495
+ return undefined;
496
+ const pc = columnsOf(p, pt, seen);
497
+ if (!pc)
498
+ return undefined;
499
+ for (const [k, v] of pc)
500
+ out.set(k, v);
501
+ }
502
+ // A partition's `col WITH OPTIONS ...` carries no type; the parent's column stands.
503
+ for (const c of t.columns)
504
+ if (c.type !== undefined || !out.has(c.name))
505
+ out.set(c.name, c);
506
+ return out;
507
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Reading the sql lexicon's build output in a post-synth check: the JSON
3
+ * document the serializer writes (`dialect`, `applyOrder`, `objects`). The
4
+ * documents are found by `../../read/objects.ts`, the same path the public
5
+ * read API (`@intentius/chant-lexicon-sql/read`) takes.
6
+ */
7
+ import { dialectObjects, recordedPostgresMajor } from "../../read/objects.js";
8
+ /** Every schema object in the build's sql output, ClickHouse dialect. */
9
+ export function clickhouseObjects(ctx) {
10
+ return dialectObjects(ctx, "clickhouse");
11
+ }
12
+ /** Every schema object in the build's sql output, Postgres dialect. */
13
+ export function postgresObjects(ctx) {
14
+ return dialectObjects(ctx, "postgres");
15
+ }
16
+ /**
17
+ * The Postgres major the build targets, as the serializer recorded it
18
+ * (`sql.postgresMajor`, else the newest pinned major). `fallback` answers for
19
+ * output that carries none.
20
+ */
21
+ export function postgresMajorOf(ctx, fallback) {
22
+ return recordedPostgresMajor(ctx, fallback);
23
+ }
@@ -0,0 +1,59 @@
1
+ import { clickhouseObjects, postgresObjects } from "./sql-helpers.js";
2
+ /**
3
+ * Kinds whose name is not the object's identity on the server, so two
4
+ * exports may share it: grants; Postgres functions and procedures, which
5
+ * overload; triggers and policies, named per table; ClickHouse row policies,
6
+ * also per table.
7
+ */
8
+ const NOT_UNIQUE = new Set([
9
+ "ClickHouse::Grant",
10
+ "ClickHouse::RowPolicy",
11
+ "Postgres::Grant",
12
+ "Postgres::DefaultPrivileges",
13
+ "Postgres::Function",
14
+ "Postgres::Procedure",
15
+ "Postgres::Aggregate",
16
+ "Postgres::Trigger",
17
+ "Postgres::Policy",
18
+ ]);
19
+ const kindOf = (type) => type
20
+ .replace(/^(ClickHouse|Postgres)::/, "")
21
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
22
+ .toLowerCase();
23
+ /**
24
+ * SQL101: two exports declare the same object, the same kind under the same
25
+ * qualified name (`shop.events` twice). The build writes both `CREATE`
26
+ * statements, and the second fails on the server, or replaces the first
27
+ * under `OR REPLACE`.
28
+ */
29
+ export const sql101 = {
30
+ id: "SQL101",
31
+ description: "Two exports declare the same object",
32
+ check(ctx) {
33
+ const out = [];
34
+ const scan = (dialect, objects) => {
35
+ const first = new Map();
36
+ for (const o of objects) {
37
+ const name = typeof o.sqlName === "string" && o.sqlName !== "" ? o.sqlName : undefined;
38
+ if (!name || NOT_UNIQUE.has(o.type))
39
+ continue;
40
+ const key = `${o.type}\u0000${name}`;
41
+ const prior = first.get(key);
42
+ if (!prior) {
43
+ first.set(key, o);
44
+ continue;
45
+ }
46
+ out.push({
47
+ checkId: "SQL101",
48
+ severity: "error",
49
+ message: `${prior.export} and ${o.export} both declare ${dialect} ${kindOf(o.type)} ${name}; the build would create it twice`,
50
+ entity: o.export,
51
+ lexicon: "sql",
52
+ });
53
+ }
54
+ };
55
+ scan("ClickHouse", clickhouseObjects(ctx));
56
+ scan("Postgres", postgresObjects(ctx));
57
+ return out;
58
+ },
59
+ };
@@ -0,0 +1,34 @@
1
+ import { sqlTypesPath, sqlTypesState } from "../../sql-types.js";
2
+ /**
3
+ * SQL102: `sql-types.d.ts` no longer matches what the build declares.
4
+ *
5
+ * tsc checks a typed tag's columns, and the foreign-key and sort-key slots,
6
+ * against the generated file, never against the template text, so a stale file
7
+ * keeps checking the old types. The check renders the declarations in memory
8
+ * (the render `chant sql types` writes) and compares them with the file in the
9
+ * project's source directory. Silent when the context carries no project
10
+ * directory (a hand-built one) or the file does not exist, so a project that
11
+ * has not adopted typed columns, or keeps the file elsewhere, sees nothing.
12
+ * `chant sql types --check` is the same comparison without a build, and a
13
+ * workspace `generated` entry runs it under `chant workspace check --generated`.
14
+ */
15
+ export const sql102 = {
16
+ id: "SQL102",
17
+ description: "sql-types.d.ts differs from what the build declares",
18
+ check(ctx) {
19
+ if (!ctx.projectDir)
20
+ return [];
21
+ const file = sqlTypesPath(ctx.projectDir, ctx.projectConfig);
22
+ const state = sqlTypesState(ctx.entities, file, { projectDir: ctx.projectDir, config: ctx.projectConfig });
23
+ if (state !== "stale")
24
+ return [];
25
+ return [
26
+ {
27
+ checkId: "SQL102",
28
+ severity: "error",
29
+ message: `${file} differs from what the build declares, so tsc checks columns against out-of-date types; run \`chant sql types\` to rewrite it`,
30
+ lexicon: "sql",
31
+ },
32
+ ];
33
+ },
34
+ };
@@ -0,0 +1,34 @@
1
+ import { DATABASE_ENGINES, TABLE_ENGINES, CLICKHOUSE_VERSION } from "../../generated/clickhouse.js";
2
+ import { clickhouseObjects } from "./sql-helpers.js";
3
+ /**
4
+ * SQLCH101: a table, materialized view or database names an engine the pinned
5
+ * ClickHouse server does not have.
6
+ *
7
+ * The engine list is the pinned server's own (`system.table_engines`,
8
+ * `system.database_engines`), so a misspelling (`MergTree`) or an engine a
9
+ * release removed fails here, at build, rather than at `CREATE` against a
10
+ * server.
11
+ */
12
+ export const sqlch101 = {
13
+ id: "SQLCH101",
14
+ description: "A ClickHouse object names an engine the pinned server does not have",
15
+ check(ctx) {
16
+ const out = [];
17
+ for (const obj of clickhouseObjects(ctx)) {
18
+ const engine = obj.engine?.name;
19
+ if (!engine)
20
+ continue;
21
+ const known = obj.type === "ClickHouse::Database" ? engine in DATABASE_ENGINES : engine in TABLE_ENGINES;
22
+ if (known)
23
+ continue;
24
+ out.push({
25
+ checkId: "SQLCH101",
26
+ severity: "error",
27
+ message: `${obj.export} (${obj.name}) uses engine "${engine}", which ClickHouse ${CLICKHOUSE_VERSION} does not have`,
28
+ entity: obj.export,
29
+ lexicon: "sql",
30
+ });
31
+ }
32
+ return out;
33
+ },
34
+ };
@@ -0,0 +1,23 @@
1
+ import { checkOf, keyElements, tablesOf } from "./clickhouse-helpers.js";
2
+ /**
3
+ * SQLCH102: a PRIMARY KEY that is not a prefix of ORDER BY.
4
+ *
5
+ * Doc: https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree
6
+ * ("The primary key must be a prefix of the sorting key"); the server refuses
7
+ * the table at CREATE.
8
+ */
9
+ export const sqlch102 = checkOf({ id: "SQLCH102", description: "A PRIMARY KEY is not a prefix of ORDER BY" }, (ctx, report) => {
10
+ for (const t of tablesOf(ctx)) {
11
+ if (!t.primaryKey || !t.orderBy)
12
+ continue;
13
+ const pk = keyElements(t.primaryKey).map((e) => e.replace(/\s+/g, ""));
14
+ const ob = keyElements(t.orderBy).map((e) => e.replace(/\s+/g, ""));
15
+ if (pk.every((e, i) => ob[i] === e))
16
+ continue;
17
+ report({
18
+ severity: "error",
19
+ message: `${t.export} (${t.name}) has PRIMARY KEY ${t.primaryKey}, which is not a prefix of ORDER BY ${t.orderBy}`,
20
+ entity: t.export,
21
+ });
22
+ }
23
+ });