@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,45 @@
1
+ import { baseEngineName, checkOf, columnByName, engineArguments, isNullableType, tablesOf, typeFamily } from "./clickhouse-helpers.js";
2
+ const VERSION_TYPES = new Set(["UInt8", "UInt16", "UInt32", "UInt64", "UInt128", "UInt256", "Date", "DateTime", "DateTime64"]);
3
+ /** engine -> the argument positions that name a column, and the types the engine accepts there. */
4
+ const SPECIAL = {
5
+ ReplacingMergeTree: [
6
+ { role: "version", index: 0, accepts: (f) => VERSION_TYPES.has(f), wants: "UInt*, Date, DateTime or DateTime64" },
7
+ { role: "is_deleted", index: 1, accepts: (f) => f === "UInt8", wants: "UInt8" },
8
+ ],
9
+ CollapsingMergeTree: [{ role: "sign", index: 0, accepts: (f) => f === "Int8", wants: "Int8" }],
10
+ VersionedCollapsingMergeTree: [
11
+ { role: "sign", index: 0, accepts: (f) => f === "Int8", wants: "Int8" },
12
+ { role: "version", index: 1, accepts: (f) => VERSION_TYPES.has(f), wants: "UInt*, Date, DateTime or DateTime64" },
13
+ ],
14
+ };
15
+ /**
16
+ * SQLCH103: the version, sign or is_deleted column of a Replacing, Collapsing
17
+ * or VersionedCollapsing table has a type the engine does not accept.
18
+ *
19
+ * Docs: https://clickhouse.com/docs/engines/table-engines/mergetree-family/replacingmergetree
20
+ * (version: UInt*, Date, DateTime or DateTime64; is_deleted: UInt8) and
21
+ * .../collapsingmergetree, .../versionedcollapsingmergetree (sign: Int8).
22
+ */
23
+ export const sqlch103 = checkOf({ id: "SQLCH103", description: "An engine's version, sign or is_deleted column has an unsupported type" }, (ctx, report) => {
24
+ for (const t of tablesOf(ctx)) {
25
+ if (!t.engine)
26
+ continue;
27
+ const roles = SPECIAL[baseEngineName(t.engine)];
28
+ if (!roles)
29
+ continue;
30
+ const args = engineArguments(t.engine);
31
+ for (const r of roles) {
32
+ const arg = args[r.index];
33
+ const col = arg ? columnByName(t, arg.replace(/^`|`$/g, "")) : undefined;
34
+ if (!col?.type)
35
+ continue;
36
+ if (!isNullableType(col.type) && r.accepts(typeFamily(col.type)))
37
+ continue;
38
+ report({
39
+ severity: "error",
40
+ message: `${t.export} (${t.name}): ${r.role} column ${col.name} is ${col.type}; ${t.engine.name} takes ${r.wants}`,
41
+ entity: t.export,
42
+ });
43
+ }
44
+ }
45
+ });
@@ -0,0 +1,41 @@
1
+ import { baseEngineName, checkOf, engineArguments, splitTop, tablesOf } from "./clickhouse-helpers.js";
2
+ /** The engine arguments that name columns, by base engine. */
3
+ function columnArguments(engine, args) {
4
+ switch (engine) {
5
+ case "ReplacingMergeTree":
6
+ return args.slice(0, 2);
7
+ case "CollapsingMergeTree":
8
+ return args.slice(0, 1);
9
+ case "VersionedCollapsingMergeTree":
10
+ return args.slice(0, 2);
11
+ case "SummingMergeTree":
12
+ case "CoalescingMergeTree":
13
+ return args.flatMap((a) => splitTop(a.trim().replace(/^\(([\s\S]*)\)$/, "$1")));
14
+ default:
15
+ return [];
16
+ }
17
+ }
18
+ /**
19
+ * SQLCH104: an engine argument names a column the table does not declare.
20
+ *
21
+ * Docs: the engine pages under
22
+ * https://clickhouse.com/docs/engines/table-engines/mergetree-family/ (the
23
+ * sign, version, is_deleted and summed columns are columns of the table).
24
+ */
25
+ export const sqlch104 = checkOf({ id: "SQLCH104", description: "A MergeTree engine argument names a column the table does not declare" }, (ctx, report) => {
26
+ for (const t of tablesOf(ctx)) {
27
+ if (!t.engine || t.columns.length === 0)
28
+ continue;
29
+ const declared = new Set(t.columns.map((c) => c.name));
30
+ for (const arg of columnArguments(baseEngineName(t.engine), engineArguments(t.engine))) {
31
+ const name = arg.trim().replace(/^`(.*)`$/, "$1");
32
+ if (!/^[A-Za-z_]\w*$/.test(name) || declared.has(name))
33
+ continue;
34
+ report({
35
+ severity: "error",
36
+ message: `${t.export} (${t.name}): ${t.engine.name} names column ${name}, which the table does not declare`,
37
+ entity: t.export,
38
+ });
39
+ }
40
+ }
41
+ });
@@ -0,0 +1,36 @@
1
+ import { bareName, checkOf, keyElements, tablesOf } from "./clickhouse-helpers.js";
2
+ /**
3
+ * SQLCH105: ORDER BY, PRIMARY KEY, PARTITION BY or SAMPLE BY lists a bare
4
+ * column name the table does not declare.
5
+ *
6
+ * Doc: https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree
7
+ * (the keys are expressions over the table's columns). Only a bare name is
8
+ * judged; an expression is left to the server.
9
+ */
10
+ export const sqlch105 = checkOf({ id: "SQLCH105", description: "A table key clause names a column the table does not declare" }, (ctx, report) => {
11
+ for (const t of tablesOf(ctx)) {
12
+ if (t.columns.length === 0)
13
+ continue;
14
+ const declared = new Set(t.columns.map((c) => c.name));
15
+ const clauses = [
16
+ ["ORDER BY", t.orderBy],
17
+ ["PRIMARY KEY", t.primaryKey],
18
+ ["PARTITION BY", t.partitionBy],
19
+ ["SAMPLE BY", t.sampleBy],
20
+ ];
21
+ for (const [clause, expr] of clauses) {
22
+ if (!expr)
23
+ continue;
24
+ for (const element of keyElements(expr)) {
25
+ const name = bareName(element);
26
+ if (name === undefined || declared.has(name))
27
+ continue;
28
+ report({
29
+ severity: "error",
30
+ message: `${t.export} (${t.name}): ${clause} names column ${name}, which the table does not declare`,
31
+ entity: t.export,
32
+ });
33
+ }
34
+ }
35
+ }
36
+ });
@@ -0,0 +1,27 @@
1
+ import { checkOf, mentionedNames, tablesOf } from "./clickhouse-helpers.js";
2
+ /**
3
+ * SQLCH106: a data skipping index expression mentions a column the table does
4
+ * not declare.
5
+ *
6
+ * Doc: https://clickhouse.com/docs/optimize/skipping-indexes (an index is
7
+ * defined over an expression of the table's columns). Projections are not
8
+ * checked: their SELECT is a full query, and reading its column names without a
9
+ * SQL parser would guess.
10
+ */
11
+ export const sqlch106 = checkOf({ id: "SQLCH106", description: "A skip index expression names a column the table does not declare" }, (ctx, report) => {
12
+ for (const t of tablesOf(ctx)) {
13
+ if (t.columns.length === 0)
14
+ continue;
15
+ const declared = new Set(t.columns.map((c) => c.name));
16
+ for (const index of t.indexes ?? []) {
17
+ const missing = [...new Set(mentionedNames(index.expr))].filter((n) => !declared.has(n));
18
+ if (missing.length === 0)
19
+ continue;
20
+ report({
21
+ severity: "error",
22
+ message: `${t.export} (${t.name}): index ${index.name} (${index.expr}) names ${missing.join(", ")}, which the table does not declare`,
23
+ entity: t.export,
24
+ });
25
+ }
26
+ }
27
+ });
@@ -0,0 +1,39 @@
1
+ import { bareName, checkOf, splitTop, tablesOf, typeFamily } from "./clickhouse-helpers.js";
2
+ const DATE_TYPES = new Set(["Date", "Date32", "DateTime", "DateTime64"]);
3
+ const TTL_ACTION = /\s+(DELETE|TO\s+DISK|TO\s+VOLUME|RECOMPRESS|GROUP\s+BY|WHERE)\b[\s\S]*$/i;
4
+ /**
5
+ * SQLCH107: a TTL expression is a bare column, or a column plus an INTERVAL,
6
+ * and that column is not a Date or DateTime.
7
+ *
8
+ * Doc: https://clickhouse.com/docs/guides/developer/ttl ("The TTL expression
9
+ * must evaluate to Date or DateTime"). Only the two plain forms are judged;
10
+ * a function call is left to the server.
11
+ */
12
+ export const sqlch107 = checkOf({ id: "SQLCH107", description: "A TTL expression is built on a column that is not a Date or DateTime" }, (ctx, report) => {
13
+ for (const t of tablesOf(ctx)) {
14
+ const ttls = [];
15
+ if (t.ttl)
16
+ for (const clause of splitTop(t.ttl))
17
+ ttls.push({ where: "table TTL", expr: clause.replace(TTL_ACTION, "") });
18
+ for (const c of t.columns)
19
+ if (c.ttl)
20
+ ttls.push({ where: `column ${c.name} TTL`, expr: c.ttl.replace(TTL_ACTION, "") });
21
+ for (const { where, expr } of ttls) {
22
+ const plain = /^\s*(`[^`]+`|[A-Za-z_]\w*)\s*(?:[+-]\s*INTERVAL\b[\s\S]*)?$/i.exec(expr);
23
+ if (!plain)
24
+ continue;
25
+ const name = bareName(plain[1]);
26
+ const col = name === undefined ? undefined : t.columns.find((c) => c.name === name);
27
+ if (!col?.type)
28
+ continue;
29
+ const family = typeFamily(col.type);
30
+ if (DATE_TYPES.has(family))
31
+ continue;
32
+ report({
33
+ severity: "error",
34
+ message: `${t.export} (${t.name}): ${where} is built on ${col.name}, which is ${col.type}; a TTL expression must be a Date or DateTime`,
35
+ entity: t.export,
36
+ });
37
+ }
38
+ }
39
+ });
@@ -0,0 +1,28 @@
1
+ import { checkOf, isTable } from "./clickhouse-helpers.js";
2
+ import { clickhouseObjects } from "./sql-helpers.js";
3
+ /**
4
+ * SQLCH108: CREATE OR REPLACE TABLE in a database whose engine is not Atomic
5
+ * (or Replicated, which is Atomic underneath).
6
+ *
7
+ * Doc: https://clickhouse.com/docs/sql-reference/statements/create/table
8
+ * ("The REPLACE query is supported only for Atomic and Replicated databases"),
9
+ * and https://clickhouse.com/docs/engines/database-engines/atomic for
10
+ * EXCHANGE TABLES. Judged against a database the same build declares.
11
+ */
12
+ export const sqlch108 = checkOf({ id: "SQLCH108", description: "CREATE OR REPLACE TABLE in a database that is not Atomic" }, (ctx, report) => {
13
+ const objects = clickhouseObjects(ctx);
14
+ const databases = new Map(objects.filter((o) => o.type === "ClickHouse::Database").map((d) => [d.name, d]));
15
+ for (const t of objects.filter(isTable)) {
16
+ if (!t.orReplace || !t.database)
17
+ continue;
18
+ const db = databases.get(t.database);
19
+ const engine = db?.engine?.name;
20
+ if (!db || !engine || engine === "Atomic" || engine === "Replicated")
21
+ continue;
22
+ report({
23
+ severity: "error",
24
+ message: `${t.export} (${t.name}) is CREATE OR REPLACE in database ${t.database}, whose engine is ${engine}; REPLACE needs Atomic or Replicated`,
25
+ entity: t.export,
26
+ });
27
+ }
28
+ });
@@ -0,0 +1,21 @@
1
+ import { checkOf, isMaterializedView } from "./clickhouse-helpers.js";
2
+ import { clickhouseObjects } from "./sql-helpers.js";
3
+ /**
4
+ * SQLCH109: a materialized view selects `*`.
5
+ *
6
+ * Doc: https://clickhouse.com/docs/materialized-view/incremental-materialized-view
7
+ * (the view's columns come from the SELECT, and a materialized view is a
8
+ * trigger on inserts). A star ties the target's columns to whatever the source
9
+ * has today, so adding a source column changes what the view writes.
10
+ */
11
+ export const sqlch109 = checkOf({ id: "SQLCH109", description: "A materialized view selects *" }, (ctx, report) => {
12
+ for (const v of clickhouseObjects(ctx).filter(isMaterializedView)) {
13
+ if (!(v.lineage ?? []).some((e) => e.expr === "*" || /\.\*$/.test(e.expr)))
14
+ continue;
15
+ report({
16
+ severity: "warning",
17
+ message: `${v.export} (${v.name}) selects *; list the columns so a new source column does not change what the view writes`,
18
+ entity: v.export,
19
+ });
20
+ }
21
+ });
@@ -0,0 +1,31 @@
1
+ import { checkOf, isMaterializedView, isTable, resolveObject } from "./clickhouse-helpers.js";
2
+ import { clickhouseObjects } from "./sql-helpers.js";
3
+ /**
4
+ * SQLCH110: a materialized view writes an output column its TO target table
5
+ * does not declare.
6
+ *
7
+ * Doc: https://clickhouse.com/docs/sql-reference/statements/create/view
8
+ * (a materialized view with TO inserts the SELECT's result into the target;
9
+ * columns are matched by name, so a column the target lacks is not stored).
10
+ * Judged only against a target the same build declares, and only when the
11
+ * SELECT lists its columns.
12
+ */
13
+ export const sqlch110 = checkOf({ id: "SQLCH110", description: "A materialized view writes a column its TO target does not declare" }, (ctx, report) => {
14
+ for (const v of clickhouseObjects(ctx).filter(isMaterializedView)) {
15
+ if (!v.to || (v.lineage ?? []).some((e) => e.expr === "*" || /\.\*$/.test(e.expr)))
16
+ continue;
17
+ const target = resolveObject(ctx, v.to);
18
+ if (!target || !isTable(target) || target.columns.length === 0)
19
+ continue;
20
+ const declared = new Set(target.columns.map((c) => c.name));
21
+ const outputs = v.columns.length > 0 ? v.columns.map((c) => c.name) : v.lineage.map((e) => e.output);
22
+ const dropped = outputs.filter((n) => !declared.has(n));
23
+ if (dropped.length === 0)
24
+ continue;
25
+ report({
26
+ severity: "warning",
27
+ message: `${v.export} (${v.name}) selects ${dropped.join(", ")}, which ${target.name} does not declare; ClickHouse matches by name, so the value is not stored`,
28
+ entity: v.export,
29
+ });
30
+ }
31
+ });
@@ -0,0 +1,41 @@
1
+ import { checkOf, splitTop, tablesOf } from "./clickhouse-helpers.js";
2
+ const MAX_VALUES = 100;
3
+ /** The literals a CHECK constrains `column` to, for `col IN (...)` and `col = 'a' OR col = 'b'`. */
4
+ function allowedValues(expr) {
5
+ const inList = /^\(?\s*(`[^`]+`|[A-Za-z_]\w*)\s+IN\s*\(([\s\S]*)\)\s*\)?$/i.exec(expr.trim());
6
+ if (inList)
7
+ return { column: inList[1].replace(/`/g, ""), count: splitTop(inList[2]).length };
8
+ const ors = expr.split(/\s+OR\s+/i);
9
+ const eq = ors.map((p) => /^\(?\s*(`[^`]+`|[A-Za-z_]\w*)\s*=\s*'(?:[^'\\]|\\.)*'\s*\)?$/.exec(p.trim()));
10
+ if (ors.length > 1 && eq.every((m) => m && m[1] === eq[0][1]))
11
+ return { column: eq[0][1].replace(/`/g, ""), count: ors.length };
12
+ return undefined;
13
+ }
14
+ /**
15
+ * SQLCH111: a String column a CHECK constrains to a small set of values is not
16
+ * LowCardinality.
17
+ *
18
+ * Doc: https://clickhouse.com/docs/sql-reference/data-types/lowcardinality
19
+ * (LowCardinality dictionary-encodes a column and speeds up SELECT for fewer
20
+ * than about 10,000 distinct values) and
21
+ * https://clickhouse.com/docs/best-practices/select-data-types.
22
+ */
23
+ export const sqlch111 = checkOf({ id: "SQLCH111", description: "A String column limited to a few values is not LowCardinality" }, (ctx, report) => {
24
+ for (const t of tablesOf(ctx)) {
25
+ for (const c of t.constraints ?? []) {
26
+ if (c.kind !== "CHECK")
27
+ continue;
28
+ const found = allowedValues(c.expr);
29
+ if (!found || found.count > MAX_VALUES)
30
+ continue;
31
+ const col = t.columns.find((x) => x.name === found.column);
32
+ if (!col?.type || !/^String$/i.test(col.type.trim()))
33
+ continue;
34
+ report({
35
+ severity: "warning",
36
+ message: `${t.export} (${t.name}): column ${col.name} is String but constraint ${c.name} limits it to ${found.count} values; use LowCardinality(String)`,
37
+ entity: t.export,
38
+ });
39
+ }
40
+ }
41
+ });
@@ -0,0 +1,28 @@
1
+ import { checkOf, tablesOf, typeFamily } from "./clickhouse-helpers.js";
2
+ const FINE_FUNCTIONS = /\b(toStartOfSecond|toStartOfMinute|toStartOfFiveMinutes?|toStartOfTenMinutes|toStartOfFifteenMinutes|toStartOfHour|toRelativeSecondNum|toRelativeMinuteNum|toRelativeHourNum|toUnixTimestamp|toYYYYMMDDhhmmss|toStartOfInterval)\s*\(/;
3
+ /**
4
+ * SQLCH112: a PARTITION BY finer than a day.
5
+ *
6
+ * Doc: https://clickhouse.com/docs/best-practices/choosing-a-partitioning-key
7
+ * and the MergeTree page's note that partitioning is not meant to speed up
8
+ * queries and that "you should not partition by too granular": every
9
+ * partition is a separate set of parts, and the server stops inserts when a
10
+ * table has too many (parts_to_throw_insert). Flags a bare DateTime column and
11
+ * the sub-day truncation functions.
12
+ */
13
+ export const sqlch112 = checkOf({ id: "SQLCH112", description: "A PARTITION BY expression is finer than a day" }, (ctx, report) => {
14
+ for (const t of tablesOf(ctx)) {
15
+ const expr = t.partitionBy?.trim();
16
+ if (!expr)
17
+ continue;
18
+ const col = t.columns.find((c) => c.name === expr.replace(/^\(|\)$/g, "").trim());
19
+ const bareDateTime = col?.type && ["DateTime", "DateTime64"].includes(typeFamily(col.type));
20
+ if (!bareDateTime && !FINE_FUNCTIONS.test(expr))
21
+ continue;
22
+ report({
23
+ severity: "warning",
24
+ message: `${t.export} (${t.name}) partitions by ${expr}, which makes a partition per second, minute or hour; partition by month or day`,
25
+ entity: t.export,
26
+ });
27
+ }
28
+ });
@@ -0,0 +1,19 @@
1
+ import { checkOf, isMergeTree, tablesOf } from "./clickhouse-helpers.js";
2
+ /**
3
+ * SQLCH113: a MergeTree table with neither ORDER BY nor PRIMARY KEY.
4
+ *
5
+ * Doc: https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree
6
+ * ("ORDER BY: required unless PRIMARY KEY is specified"; write
7
+ * `ORDER BY tuple()` to have none). The server refuses the CREATE.
8
+ */
9
+ export const sqlch113 = checkOf({ id: "SQLCH113", description: "A MergeTree table declares no sort key" }, (ctx, report) => {
10
+ for (const t of tablesOf(ctx)) {
11
+ if (!isMergeTree(t.engine) || t.orderBy || t.primaryKey)
12
+ continue;
13
+ report({
14
+ severity: "error",
15
+ message: `${t.export} (${t.name}) uses ${t.engine.name} with no ORDER BY or PRIMARY KEY; add one, or ORDER BY tuple() for none`,
16
+ entity: t.export,
17
+ });
18
+ }
19
+ });
@@ -0,0 +1,26 @@
1
+ import { CODECS } from "../../generated/clickhouse.js";
2
+ import { checkOf, codecNames, tablesOf } from "./clickhouse-helpers.js";
3
+ /**
4
+ * SQLCH114: a column CODEC names a codec the pinned server does not have.
5
+ *
6
+ * Doc: https://clickhouse.com/docs/sql-reference/statements/create/table#column-compression-codecs.
7
+ * The list is the pinned server's own (`system.codecs`).
8
+ */
9
+ export const sqlch114 = checkOf({ id: "SQLCH114", description: "A column codec is not one the pinned server has" }, (ctx, report) => {
10
+ const known = new Set(Object.keys(CODECS).map((n) => n.toLowerCase()));
11
+ for (const t of tablesOf(ctx)) {
12
+ for (const c of t.columns) {
13
+ if (!c.codec)
14
+ continue;
15
+ for (const name of codecNames(c.codec)) {
16
+ if (known.has(name.toLowerCase()))
17
+ continue;
18
+ report({
19
+ severity: "error",
20
+ message: `${t.export} (${t.name}): column ${c.name} uses codec ${name}, which the pinned server does not have`,
21
+ entity: t.export,
22
+ });
23
+ }
24
+ }
25
+ }
26
+ });
@@ -0,0 +1,30 @@
1
+ import { CODECS } from "../../generated/clickhouse.js";
2
+ import { checkOf, codecNames, tablesOf } from "./clickhouse-helpers.js";
3
+ const SENSITIVE = /(^|_)(password|passwd|pwd|secret|api_?key|access_?key|private_?key|token|ssn|social_?security|credit_?card|card_?number|iban)(_|$)/i;
4
+ /**
5
+ * SQLCH115: a column named like a secret or personal data has no COMMENT, no
6
+ * TTL and no encryption codec.
7
+ *
8
+ * Docs: https://clickhouse.com/docs/sql-reference/statements/create/table
9
+ * (column COMMENT, column TTL, and the AES_128_GCM_SIV / AES_256_GCM_SIV
10
+ * encryption codecs). The marker is the author saying the column was
11
+ * considered: a COMMENT stating what it holds, a TTL that bounds how long it
12
+ * is kept, or a codec that encrypts it. The table's own TTL counts for the
13
+ * whole row.
14
+ */
15
+ export const sqlch115 = checkOf({ id: "SQLCH115", description: "A secret- or PII-named column carries no comment, TTL or encryption" }, (ctx, report) => {
16
+ const encryption = new Set(Object.entries(CODECS).filter(([, s]) => s.encryption).map(([n]) => n.toLowerCase()));
17
+ for (const t of tablesOf(ctx)) {
18
+ for (const c of t.columns) {
19
+ if (!SENSITIVE.test(c.name) || c.comment || c.ttl || t.ttl)
20
+ continue;
21
+ if (c.codec && codecNames(c.codec).some((n) => encryption.has(n.toLowerCase())))
22
+ continue;
23
+ report({
24
+ severity: "warning",
25
+ message: `${t.export} (${t.name}): column ${c.name} looks like a secret or personal data; add a COMMENT, a TTL or an encryption codec`,
26
+ entity: t.export,
27
+ });
28
+ }
29
+ }
30
+ });
@@ -0,0 +1,22 @@
1
+ import { checkOf, isAnyView } from "./clickhouse-helpers.js";
2
+ import { clickhouseObjects } from "./sql-helpers.js";
3
+ /**
4
+ * SQLCH116: a view declared SQL SECURITY DEFINER without a DEFINER.
5
+ *
6
+ * Doc: https://clickhouse.com/docs/sql-reference/statements/create/view#sql-security
7
+ * (the DEFINER clause names the user whose privileges the view's SELECT runs
8
+ * with). Without it the view's access is decided by whoever happened to run
9
+ * the CREATE, so the same declaration grants different access per environment.
10
+ */
11
+ export const sqlch116 = checkOf({ id: "SQLCH116", description: "A view is SQL SECURITY DEFINER with no DEFINER" }, (ctx, report) => {
12
+ for (const v of clickhouseObjects(ctx).filter(isAnyView)) {
13
+ const security = v.security ?? "";
14
+ if (!/SQL\s+SECURITY\s+DEFINER/i.test(security) || /\bDEFINER\s*=/i.test(security))
15
+ continue;
16
+ report({
17
+ severity: "warning",
18
+ message: `${v.export} (${v.name}) is SQL SECURITY DEFINER with no DEFINER = <user>; name the user whose privileges it runs with`,
19
+ entity: v.export,
20
+ });
21
+ }
22
+ });
@@ -0,0 +1,26 @@
1
+ import { MERGE_TREE_SETTINGS } from "../../generated/clickhouse.js";
2
+ import { checkOf, isMergeTree } from "./clickhouse-helpers.js";
3
+ import { clickhouseObjects } from "./sql-helpers.js";
4
+ /**
5
+ * SQLCH117: a MergeTree SETTINGS entry the pinned server marks obsolete.
6
+ *
7
+ * Doc: https://clickhouse.com/docs/operations/settings/merge-tree-settings.
8
+ * `system.merge_tree_settings` flags it with tier Obsolete; the server accepts
9
+ * it and ignores it, so the table does not behave as the setting suggests.
10
+ */
11
+ export const sqlch117 = checkOf({ id: "SQLCH117", description: "A MergeTree setting is obsolete at the pinned server" }, (ctx, report) => {
12
+ const specs = MERGE_TREE_SETTINGS;
13
+ for (const o of clickhouseObjects(ctx)) {
14
+ if (!isMergeTree(o.engine) && o.type !== "ClickHouse::MaterializedView")
15
+ continue;
16
+ for (const name of Object.keys(o.settings ?? {})) {
17
+ if (!specs[name]?.obsolete)
18
+ continue;
19
+ report({
20
+ severity: "warning",
21
+ message: `${o.export} (${o.name}) sets ${name}, which is obsolete and does nothing at ClickHouse 26.8; remove it`,
22
+ entity: o.export,
23
+ });
24
+ }
25
+ }
26
+ });
@@ -0,0 +1,27 @@
1
+ import { MERGE_TREE_SETTINGS } from "../../generated/clickhouse.js";
2
+ import { checkOf, isMergeTree } from "./clickhouse-helpers.js";
3
+ import { clickhouseObjects } from "./sql-helpers.js";
4
+ /**
5
+ * SQLCH118: a MergeTree SETTINGS entry the pinned server does not have.
6
+ *
7
+ * Doc: https://clickhouse.com/docs/operations/settings/merge-tree-settings.
8
+ * The list is the pinned server's `system.merge_tree_settings`; an unknown
9
+ * name (usually a typo, or a setting a later release added) fails the CREATE
10
+ * with "Unknown setting".
11
+ */
12
+ export const sqlch118 = checkOf({ id: "SQLCH118", description: "A MergeTree setting is not one the pinned server has" }, (ctx, report) => {
13
+ const known = MERGE_TREE_SETTINGS;
14
+ for (const o of clickhouseObjects(ctx)) {
15
+ if (o.type !== "ClickHouse::Table" || !isMergeTree(o.engine))
16
+ continue;
17
+ for (const name of Object.keys(o.settings ?? {})) {
18
+ if (name in known)
19
+ continue;
20
+ report({
21
+ severity: "error",
22
+ message: `${o.export} (${o.name}) sets ${name}, which ClickHouse 26.8 does not have as a MergeTree setting`,
23
+ entity: o.export,
24
+ });
25
+ }
26
+ }
27
+ });
@@ -0,0 +1,32 @@
1
+ import { DATABASE_ENGINES, TABLE_ENGINES } from "../../generated/clickhouse.js";
2
+ import { checkOf } from "./clickhouse-helpers.js";
3
+ import { clickhouseObjects } from "./sql-helpers.js";
4
+ /**
5
+ * SQLCH119: an engine the pinned catalog describes as deprecated or experimental.
6
+ *
7
+ * Docs: https://clickhouse.com/docs/engines/database-engines/ordinary (the
8
+ * legacy, deprecated default replaced by Atomic) and the engine pages the
9
+ * snapshot's summaries come from (`This engine is experimental`,
10
+ * ExperimentalBadge). The words are read from the committed catalog snapshot,
11
+ * so a pin move changes the verdict.
12
+ */
13
+ export const sqlch119 = checkOf({ id: "SQLCH119", description: "An object uses a deprecated or experimental engine" }, (ctx, report) => {
14
+ const tables = TABLE_ENGINES;
15
+ const databases = DATABASE_ENGINES;
16
+ for (const o of clickhouseObjects(ctx)) {
17
+ const name = o.engine?.name;
18
+ if (!name)
19
+ continue;
20
+ const isDatabase = o.type === "ClickHouse::Database";
21
+ const summary = (isDatabase ? databases : tables)[name]?.summary ?? "";
22
+ const deprecated = /\bdeprecated\b/i.test(summary);
23
+ const experimental = isDatabase ? /ExperimentalBadge/.test(summary) : /\b(is|are) (an )?experimental\b/i.test(summary);
24
+ if (!deprecated && !experimental)
25
+ continue;
26
+ report({
27
+ severity: "warning",
28
+ message: `${o.export} (${o.name}) uses the ${deprecated ? "deprecated" : "experimental"} engine ${name}`,
29
+ entity: o.export,
30
+ });
31
+ }
32
+ });
@@ -0,0 +1,20 @@
1
+ import { checkOf, tablesOf } from "./clickhouse-helpers.js";
2
+ /**
3
+ * SQLCH120: a MergeTree engine written with the legacy positional arguments.
4
+ *
5
+ * Doc: https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree
6
+ * ("Deprecated Method for Creating a Table": `MergeTree(date-column
7
+ * [, sampling_expression], (primary, key), index_granularity)`). Current
8
+ * syntax is a bare `MergeTree` with ORDER BY, PARTITION BY and SETTINGS.
9
+ */
10
+ export const sqlch120 = checkOf({ id: "SQLCH120", description: "A MergeTree engine uses the deprecated positional arguments" }, (ctx, report) => {
11
+ for (const t of tablesOf(ctx)) {
12
+ if (t.engine?.name !== "MergeTree" || (t.engine.args ?? []).length === 0)
13
+ continue;
14
+ report({
15
+ severity: "warning",
16
+ message: `${t.export} (${t.name}) is MergeTree(${t.engine.args.join(", ")}), the deprecated positional form; use ORDER BY, PARTITION BY and SETTINGS`,
17
+ entity: t.export,
18
+ });
19
+ }
20
+ });
@@ -0,0 +1,11 @@
1
+ import { checkOf } from "./clickhouse-helpers.js";
2
+ import { reportTypeFindings } from "./clickhouse-type-check.js";
3
+ /**
4
+ * SQLCH121: a column type names a family the pinned server does not have
5
+ * (`UInt46`, `uint64`, `Nullabel(String)`), at any depth of the type.
6
+ *
7
+ * The family list is the pinned server's own (`system.data_type_families`),
8
+ * aliases included; a name is case-sensitive unless the catalog marks it
9
+ * case-insensitive.
10
+ */
11
+ export const sqlch121 = checkOf({ id: "SQLCH121", description: "A column type names a family the pinned server does not have" }, reportTypeFindings("SQLCH121"));
@@ -0,0 +1,11 @@
1
+ import { checkOf } from "./clickhouse-helpers.js";
2
+ import { reportTypeFindings } from "./clickhouse-type-check.js";
3
+ /**
4
+ * SQLCH122: a column type's parameters do not fit the family: parameters on a
5
+ * family that takes none, a required one missing (`FixedString`), too many,
6
+ * or a number outside its range (`Decimal(100, 2)`).
7
+ *
8
+ * The parameter grammar is the `TYPE_PARAMETERS` overlay; no system table
9
+ * holds it.
10
+ */
11
+ export const sqlch122 = checkOf({ id: "SQLCH122", description: "A column type's parameters do not fit its family" }, reportTypeFindings("SQLCH122"));
@@ -0,0 +1,10 @@
1
+ import { checkOf } from "./clickhouse-helpers.js";
2
+ import { reportTypeFindings } from "./clickhouse-type-check.js";
3
+ /**
4
+ * SQLCH123: `Nullable` around a type ClickHouse keeps out of it: `Array`,
5
+ * `Map`, `Nested`, `LowCardinality`, `Variant` or another `Nullable`. The
6
+ * server refuses the column ("Nested type ... cannot be inside Nullable
7
+ * type"). `Nullable(Tuple(...))` is left alone: the server takes it when the
8
+ * profile enables `enable_nullable_tuple_type`.
9
+ */
10
+ export const sqlch123 = checkOf({ id: "SQLCH123", description: "A Nullable wraps a type ClickHouse does not allow inside Nullable" }, reportTypeFindings("SQLCH123"));
@@ -0,0 +1,21 @@
1
+ import { checkOf } from "./clickhouse-helpers.js";
2
+ import { clickhouseObjects } from "./sql-helpers.js";
3
+ /**
4
+ * SQLCH124: a table, view or dictionary declares a column twice. The server
5
+ * refuses the `CREATE` ("Column ... already exists").
6
+ */
7
+ export const sqlch124 = checkOf({ id: "SQLCH124", description: "A table declares a column twice" }, (ctx, report) => {
8
+ for (const o of clickhouseObjects(ctx)) {
9
+ if (!/^ClickHouse::(Table|View|MaterializedView|Dictionary)$/.test(o.type))
10
+ continue;
11
+ const seen = new Set();
12
+ const reported = new Set();
13
+ for (const c of (o.columns ?? [])) {
14
+ if (seen.has(c.name) && !reported.has(c.name)) {
15
+ reported.add(c.name);
16
+ report({ severity: "error", message: `${o.export} (${o.name}) declares column ${c.name} twice`, entity: o.export });
17
+ }
18
+ seen.add(c.name);
19
+ }
20
+ }
21
+ });