@intentius/chant-lexicon-sql 0.124.45 → 0.124.48

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 (360) hide show
  1. package/dist/change-set.d.ts +1 -1
  2. package/dist/change-set.d.ts.map +1 -1
  3. package/dist/change-set.js +2 -1
  4. package/dist/clickhouse/apply/statements.d.ts +9 -0
  5. package/dist/clickhouse/apply/statements.d.ts.map +1 -1
  6. package/dist/clickhouse/apply/statements.js +24 -0
  7. package/dist/clickhouse/import/live-export.d.ts +7 -3
  8. package/dist/clickhouse/import/live-export.d.ts.map +1 -1
  9. package/dist/clickhouse/import/live-export.js +21 -5
  10. package/dist/clickhouse/live/named-collections.d.ts +27 -0
  11. package/dist/clickhouse/live/named-collections.d.ts.map +1 -0
  12. package/dist/clickhouse/live/named-collections.js +53 -0
  13. package/dist/clickhouse/live/undeclarable.d.ts +15 -3
  14. package/dist/clickhouse/live/undeclarable.d.ts.map +1 -1
  15. package/dist/clickhouse/live/undeclarable.js +18 -4
  16. package/dist/clickhouse/named-collections.d.ts +83 -0
  17. package/dist/clickhouse/named-collections.d.ts.map +1 -0
  18. package/dist/clickhouse/named-collections.js +203 -0
  19. package/dist/clickhouse/parser.d.ts.map +1 -1
  20. package/dist/clickhouse/parser.js +1 -1
  21. package/dist/clickhouse/plan/commands.d.ts +2 -1
  22. package/dist/clickhouse/plan/commands.d.ts.map +1 -1
  23. package/dist/clickhouse/plan/commands.js +17 -2
  24. package/dist/clickhouse/plan/diff.d.ts +15 -0
  25. package/dist/clickhouse/plan/diff.d.ts.map +1 -1
  26. package/dist/clickhouse/plan/diff.js +43 -1
  27. package/dist/clickhouse/plan/disruption.d.ts.map +1 -1
  28. package/dist/clickhouse/plan/disruption.js +5 -0
  29. package/dist/clickhouse/plan/rebuild-handoff.d.ts.map +1 -1
  30. package/dist/clickhouse/plan/rebuild-handoff.js +4 -0
  31. package/dist/clickhouse/plan/report.d.ts.map +1 -1
  32. package/dist/clickhouse/plan/report.js +6 -1
  33. package/dist/clickhouse/plan/rules.d.ts +5 -1
  34. package/dist/clickhouse/plan/rules.d.ts.map +1 -1
  35. package/dist/clickhouse/plan/rules.js +7 -3
  36. package/dist/clickhouse/plan/stream.d.ts +40 -0
  37. package/dist/clickhouse/plan/stream.d.ts.map +1 -0
  38. package/dist/clickhouse/plan/stream.js +107 -0
  39. package/dist/clickhouse/rebuild/observe.d.ts +3 -1
  40. package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
  41. package/dist/clickhouse/rebuild/observe.js +14 -1
  42. package/dist/clickhouse/review-catalog.d.ts +213 -0
  43. package/dist/clickhouse/review-catalog.d.ts.map +1 -0
  44. package/dist/clickhouse/review-catalog.js +330 -0
  45. package/dist/codegen/classifier-page.d.ts.map +1 -1
  46. package/dist/codegen/classifier-page.js +21 -4
  47. package/dist/codegen/clickhouse-module.d.ts.map +1 -1
  48. package/dist/codegen/clickhouse-module.js +3 -0
  49. package/dist/codegen/coverage-page.d.ts.map +1 -1
  50. package/dist/codegen/coverage-page.js +5 -3
  51. package/dist/codegen/docs.js +1 -1
  52. package/dist/codegen/postgres-classifier-page.js +1 -1
  53. package/dist/codegen/registry.d.ts.map +1 -1
  54. package/dist/codegen/registry.js +5 -0
  55. package/dist/composites/catalog.d.ts.map +1 -1
  56. package/dist/composites/catalog.js +100 -0
  57. package/dist/composites/clickhouse.d.ts +2 -0
  58. package/dist/composites/clickhouse.d.ts.map +1 -1
  59. package/dist/composites/clickhouse.js +2 -0
  60. package/dist/composites/otel-tables.d.ts +100 -0
  61. package/dist/composites/otel-tables.d.ts.map +1 -0
  62. package/dist/composites/otel-tables.js +282 -0
  63. package/dist/composites/stream-ingest.d.ts +63 -0
  64. package/dist/composites/stream-ingest.d.ts.map +1 -0
  65. package/dist/composites/stream-ingest.js +39 -0
  66. package/dist/coverage/clickhouse.json +1 -1
  67. package/dist/coverage/postgres.json +7 -7
  68. package/dist/coverage.d.ts +16 -6
  69. package/dist/coverage.d.ts.map +1 -1
  70. package/dist/coverage.js +10 -5
  71. package/dist/drizzle-command.d.ts +17 -0
  72. package/dist/drizzle-command.d.ts.map +1 -0
  73. package/dist/drizzle-command.js +91 -0
  74. package/dist/drizzle.d.ts +70 -0
  75. package/dist/drizzle.d.ts.map +1 -0
  76. package/dist/drizzle.js +684 -0
  77. package/dist/files/clickhouse.d.ts.map +1 -1
  78. package/dist/files/clickhouse.js +8 -1
  79. package/dist/files/common.d.ts +13 -0
  80. package/dist/files/common.d.ts.map +1 -1
  81. package/dist/files/common.js +5 -0
  82. package/dist/files/postgres.js +12 -2
  83. package/dist/generated/clickhouse.d.ts +2 -0
  84. package/dist/generated/clickhouse.d.ts.map +1 -1
  85. package/dist/generated/clickhouse.js +2 -0
  86. package/dist/import-parser.d.ts +21 -1
  87. package/dist/import-parser.d.ts.map +1 -1
  88. package/dist/import-parser.js +79 -5
  89. package/dist/index.d.ts +1 -1
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +1 -1
  92. package/dist/integrity.json +6 -5
  93. package/dist/lint/post-synth/audit-entries.d.ts.map +1 -1
  94. package/dist/lint/post-synth/audit-entries.js +1 -0
  95. package/dist/lint/post-synth/index.d.ts.map +1 -1
  96. package/dist/lint/post-synth/index.js +2 -0
  97. package/dist/lint/post-synth/sqlch128.d.ts +18 -0
  98. package/dist/lint/post-synth/sqlch128.d.ts.map +1 -0
  99. package/dist/lint/post-synth/sqlch128.js +222 -0
  100. package/dist/lint/rules/postgres-templates.js +2 -2
  101. package/dist/lsp/postgres-template.d.ts.map +1 -1
  102. package/dist/lsp/postgres-template.js +11 -0
  103. package/dist/manifest.json +1 -1
  104. package/dist/mcp/postgres.js +1 -1
  105. package/dist/meta.json +25 -0
  106. package/dist/migrate/from-postgres/detect.d.ts +4 -0
  107. package/dist/migrate/from-postgres/detect.d.ts.map +1 -0
  108. package/dist/migrate/from-postgres/detect.js +7 -0
  109. package/dist/migrate/from-postgres/emit.d.ts +26 -0
  110. package/dist/migrate/from-postgres/emit.d.ts.map +1 -0
  111. package/dist/migrate/from-postgres/emit.js +86 -0
  112. package/dist/migrate/from-postgres/index.d.ts +51 -0
  113. package/dist/migrate/from-postgres/index.d.ts.map +1 -0
  114. package/dist/migrate/from-postgres/index.js +115 -0
  115. package/dist/migrate/from-postgres/translate.d.ts +73 -0
  116. package/dist/migrate/from-postgres/translate.d.ts.map +1 -0
  117. package/dist/migrate/from-postgres/translate.js +261 -0
  118. package/dist/migrate/from-postgres/types.d.ts +51 -0
  119. package/dist/migrate/from-postgres/types.d.ts.map +1 -0
  120. package/dist/migrate/from-postgres/types.js +211 -0
  121. package/dist/migration-replay.d.ts +73 -0
  122. package/dist/migration-replay.d.ts.map +1 -0
  123. package/dist/migration-replay.js +294 -0
  124. package/dist/okf/index.md +6 -0
  125. package/dist/okf/rules/SQLCH128.md +11 -0
  126. package/dist/okf/types/PostgresForeignDataWrapper.md +9 -0
  127. package/dist/okf/types/PostgresForeignServer.md +9 -0
  128. package/dist/okf/types/PostgresOperator.md +9 -0
  129. package/dist/okf/types/PostgresPublication.md +9 -0
  130. package/dist/okf/types/PostgresStatistics.md +9 -0
  131. package/dist/op/activities/postgres-migration.d.ts.map +1 -1
  132. package/dist/op/activities/postgres-migration.js +4 -0
  133. package/dist/op/activity-contracts.d.ts +20 -0
  134. package/dist/op/activity-contracts.d.ts.map +1 -1
  135. package/dist/op/activity-contracts.js +2 -0
  136. package/dist/plugin.d.ts.map +1 -1
  137. package/dist/plugin.js +36 -2
  138. package/dist/postgres/apply/apply.js +8 -1
  139. package/dist/postgres/apply/statements.d.ts.map +1 -1
  140. package/dist/postgres/apply/statements.js +87 -2
  141. package/dist/postgres/constraint-names.d.ts +30 -0
  142. package/dist/postgres/constraint-names.d.ts.map +1 -0
  143. package/dist/postgres/constraint-names.js +72 -0
  144. package/dist/postgres/entities.d.ts +110 -1
  145. package/dist/postgres/entities.d.ts.map +1 -1
  146. package/dist/postgres/entities.js +146 -0
  147. package/dist/postgres/entity-types.d.ts +5 -0
  148. package/dist/postgres/entity-types.d.ts.map +1 -1
  149. package/dist/postgres/entity-types.js +5 -0
  150. package/dist/postgres/import/generator.d.ts.map +1 -1
  151. package/dist/postgres/import/generator.js +50 -2
  152. package/dist/postgres/import/live-export.d.ts.map +1 -1
  153. package/dist/postgres/import/live-export.js +2 -0
  154. package/dist/postgres/live/catalog-kinds.d.ts +14 -0
  155. package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
  156. package/dist/postgres/live/catalog-kinds.js +206 -0
  157. package/dist/postgres/live/catalog.d.ts +2 -0
  158. package/dist/postgres/live/catalog.d.ts.map +1 -1
  159. package/dist/postgres/live/catalog.js +3 -1
  160. package/dist/postgres/live/describe-resources.d.ts.map +1 -1
  161. package/dist/postgres/live/describe-resources.js +10 -2
  162. package/dist/postgres/migrate/backfill.d.ts +2 -0
  163. package/dist/postgres/migrate/backfill.d.ts.map +1 -1
  164. package/dist/postgres/migrate/backfill.js +7 -1
  165. package/dist/postgres/migrate/carry.d.ts +45 -1
  166. package/dist/postgres/migrate/carry.d.ts.map +1 -1
  167. package/dist/postgres/migrate/carry.js +72 -8
  168. package/dist/postgres/migrate/observe.d.ts +28 -4
  169. package/dist/postgres/migrate/observe.d.ts.map +1 -1
  170. package/dist/postgres/migrate/observe.js +41 -12
  171. package/dist/postgres/migrate/op.d.ts +26 -1
  172. package/dist/postgres/migrate/op.d.ts.map +1 -1
  173. package/dist/postgres/migrate/op.js +3 -1
  174. package/dist/postgres/migrate/steps.d.ts +35 -2
  175. package/dist/postgres/migrate/steps.d.ts.map +1 -1
  176. package/dist/postgres/migrate/steps.js +158 -12
  177. package/dist/postgres/parser.d.ts +72 -1
  178. package/dist/postgres/parser.d.ts.map +1 -1
  179. package/dist/postgres/parser.js +194 -2
  180. package/dist/postgres/plan/deep.d.ts.map +1 -1
  181. package/dist/postgres/plan/deep.js +29 -1
  182. package/dist/postgres/plan/diff.d.ts.map +1 -1
  183. package/dist/postgres/plan/diff.js +39 -1
  184. package/dist/postgres/plan/normalize.d.ts +17 -2
  185. package/dist/postgres/plan/normalize.d.ts.map +1 -1
  186. package/dist/postgres/plan/normalize.js +109 -1
  187. package/dist/postgres/plan/rules.d.ts +1 -0
  188. package/dist/postgres/plan/rules.d.ts.map +1 -1
  189. package/dist/postgres/plan/rules.js +3 -2
  190. package/dist/postgres/plan/schema.js +1 -1
  191. package/dist/postgres/secret-options.d.ts +10 -0
  192. package/dist/postgres/secret-options.d.ts.map +1 -0
  193. package/dist/postgres/secret-options.js +9 -0
  194. package/dist/postgres/testing/roundtrip-schema.d.ts +3 -2
  195. package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
  196. package/dist/postgres/testing/roundtrip-schema.js +13 -1
  197. package/dist/postgres.d.ts +1 -1
  198. package/dist/postgres.d.ts.map +1 -1
  199. package/dist/postgres.js +1 -1
  200. package/dist/rules/audit-entries.ts +1 -0
  201. package/dist/rules/postgres-templates.ts +2 -2
  202. package/dist/rules/sqlch128.ts +230 -0
  203. package/dist/serializer.d.ts +5 -0
  204. package/dist/serializer.d.ts.map +1 -1
  205. package/dist/serializer.js +13 -2
  206. package/dist/sql-types-command.d.ts +13 -0
  207. package/dist/sql-types-command.d.ts.map +1 -1
  208. package/dist/sql-types-command.js +31 -16
  209. package/dist/sql-types.d.ts +22 -0
  210. package/dist/sql-types.d.ts.map +1 -1
  211. package/dist/sql-types.js +10 -6
  212. package/dist/sqlserver/lexer.d.ts +37 -0
  213. package/dist/sqlserver/lexer.d.ts.map +1 -0
  214. package/dist/sqlserver/lexer.js +254 -0
  215. package/dist/sqlserver/parse.d.ts +185 -0
  216. package/dist/sqlserver/parse.d.ts.map +1 -0
  217. package/dist/sqlserver/parse.js +767 -0
  218. package/dist/sqlserver/translate.d.ts +34 -0
  219. package/dist/sqlserver/translate.d.ts.map +1 -0
  220. package/dist/sqlserver/translate.js +495 -0
  221. package/dist/sqlserver/types.d.ts +44 -0
  222. package/dist/sqlserver/types.d.ts.map +1 -0
  223. package/dist/sqlserver/types.js +133 -0
  224. package/dist/sqlserver/validate.d.ts +31 -0
  225. package/dist/sqlserver/validate.d.ts.map +1 -0
  226. package/dist/sqlserver/validate.js +67 -0
  227. package/dist/validate.d.ts.map +1 -1
  228. package/dist/validate.js +5 -0
  229. package/package.json +2 -2
  230. package/src/change-set.ts +2 -1
  231. package/src/clickhouse/apply/statements.ts +25 -0
  232. package/src/clickhouse/import/live-export.ts +20 -5
  233. package/src/clickhouse/live/named-collections.e2e.test.ts +88 -0
  234. package/src/clickhouse/live/named-collections.ts +61 -0
  235. package/src/clickhouse/live/undeclarable.e2e.test.ts +5 -3
  236. package/src/clickhouse/live/undeclarable.ts +20 -4
  237. package/src/clickhouse/named-collections.test.ts +187 -0
  238. package/src/clickhouse/named-collections.ts +224 -0
  239. package/src/clickhouse/parser.ts +2 -1
  240. package/src/clickhouse/plan/commands.ts +17 -2
  241. package/src/clickhouse/plan/diff.ts +57 -1
  242. package/src/clickhouse/plan/disruption.ts +5 -0
  243. package/src/clickhouse/plan/rebuild-handoff.ts +3 -0
  244. package/src/clickhouse/plan/report.ts +6 -1
  245. package/src/clickhouse/plan/rules.ts +14 -4
  246. package/src/clickhouse/plan/stream-s3queue.e2e.test.ts +148 -0
  247. package/src/clickhouse/plan/stream.test.ts +237 -0
  248. package/src/clickhouse/plan/stream.ts +111 -0
  249. package/src/clickhouse/rebuild/observe.ts +15 -1
  250. package/src/clickhouse/review-catalog.test.ts +181 -0
  251. package/src/clickhouse/review-catalog.ts +489 -0
  252. package/src/codegen/classifier-page.ts +21 -4
  253. package/src/codegen/clickhouse-module.ts +3 -0
  254. package/src/codegen/coverage-page.ts +5 -3
  255. package/src/codegen/docs.ts +1 -1
  256. package/src/codegen/postgres-classifier-page.ts +1 -1
  257. package/src/codegen/registry.ts +5 -0
  258. package/src/composites/catalog.test.ts +6 -1
  259. package/src/composites/catalog.ts +100 -0
  260. package/src/composites/clickhouse.ts +2 -0
  261. package/src/composites/composites.test.ts +1 -1
  262. package/src/composites/otel-tables.test.ts +118 -0
  263. package/src/composites/otel-tables.ts +339 -0
  264. package/src/composites/stream-ingest.test.ts +114 -0
  265. package/src/composites/stream-ingest.ts +82 -0
  266. package/src/core/undeclarable.test.ts +6 -5
  267. package/src/coverage/clickhouse.json +1 -1
  268. package/src/coverage/postgres.json +7 -7
  269. package/src/coverage.test.ts +27 -2
  270. package/src/coverage.ts +18 -7
  271. package/src/drizzle-command.ts +94 -0
  272. package/src/drizzle-kit.e2e.test.ts +89 -0
  273. package/src/drizzle-types.e2e.test.ts +67 -0
  274. package/src/drizzle.test.ts +358 -0
  275. package/src/drizzle.ts +737 -0
  276. package/src/files/clickhouse.ts +8 -1
  277. package/src/files/common.ts +14 -1
  278. package/src/files/postgres.ts +12 -2
  279. package/src/generated/clickhouse.ts +3 -0
  280. package/src/generated/lexicon-sql.json +25 -0
  281. package/src/import-parser.sqlserver.test.ts +92 -0
  282. package/src/import-parser.ts +79 -5
  283. package/src/index.ts +1 -1
  284. package/src/lint/post-synth/audit-entries.ts +1 -0
  285. package/src/lint/post-synth/index.ts +2 -0
  286. package/src/lint/post-synth/sqlch128.test.ts +107 -0
  287. package/src/lint/post-synth/sqlch128.ts +230 -0
  288. package/src/lint/rules/postgres-templates.ts +2 -2
  289. package/src/lsp/postgres-template.ts +11 -0
  290. package/src/mcp/postgres.ts +1 -1
  291. package/src/migrate/from-postgres/cli.test.ts +165 -0
  292. package/src/migrate/from-postgres/detect.ts +7 -0
  293. package/src/migrate/from-postgres/emit.ts +96 -0
  294. package/src/migrate/from-postgres/example.test.ts +205 -0
  295. package/src/migrate/from-postgres/index.ts +122 -0
  296. package/src/migrate/from-postgres/translate.test.ts +242 -0
  297. package/src/migrate/from-postgres/translate.ts +359 -0
  298. package/src/migrate/from-postgres/types.test.ts +116 -0
  299. package/src/migrate/from-postgres/types.ts +236 -0
  300. package/src/migrate/from-postgres/validate.e2e.test.ts +44 -0
  301. package/src/migrate/from-postgres/workspace.e2e.test.ts +114 -0
  302. package/src/migration-replay.e2e.test.ts +155 -0
  303. package/src/migration-replay.test.ts +173 -0
  304. package/src/migration-replay.ts +289 -0
  305. package/src/op/__snapshots__/watch-pipeline.test.ts.snap +2 -2
  306. package/src/op/activities/postgres-migration.ts +4 -0
  307. package/src/op/activity-contracts.ts +2 -0
  308. package/src/plugin.ts +37 -2
  309. package/src/postgres/apply/apply.ts +9 -1
  310. package/src/postgres/apply/statements.ts +74 -1
  311. package/src/postgres/constraint-names.ts +71 -0
  312. package/src/postgres/database-kinds.test.ts +384 -0
  313. package/src/postgres/entities.ts +266 -1
  314. package/src/postgres/entity-types.ts +5 -0
  315. package/src/postgres/import/generator.ts +42 -2
  316. package/src/postgres/import/live-export.ts +1 -0
  317. package/src/postgres/live/catalog-kinds.ts +223 -0
  318. package/src/postgres/live/catalog.ts +6 -1
  319. package/src/postgres/live/database-kinds.e2e.test.ts +164 -0
  320. package/src/postgres/live/describe-resources.ts +11 -2
  321. package/src/postgres/live/undeclarable.e2e.test.ts +13 -6
  322. package/src/postgres/migrate/backfill.ts +13 -1
  323. package/src/postgres/migrate/carry.ts +103 -8
  324. package/src/postgres/migrate/followups.e2e.test.ts +218 -0
  325. package/src/postgres/migrate/followups.test.ts +199 -0
  326. package/src/postgres/migrate/kinds.e2e.test.ts +5 -5
  327. package/src/postgres/migrate/observe.ts +53 -14
  328. package/src/postgres/migrate/op.ts +28 -1
  329. package/src/postgres/migrate/partition.e2e.test.ts +129 -0
  330. package/src/postgres/migrate/partition.test.ts +206 -0
  331. package/src/postgres/migrate/steps.ts +157 -12
  332. package/src/postgres/parser.ts +234 -2
  333. package/src/postgres/plan/deep.ts +28 -1
  334. package/src/postgres/plan/diff.ts +36 -1
  335. package/src/postgres/plan/normalize.ts +122 -1
  336. package/src/postgres/plan/rules.ts +10 -3
  337. package/src/postgres/plan/schema.ts +1 -1
  338. package/src/postgres/secret-options.ts +10 -0
  339. package/src/postgres/testing/roundtrip-schema.ts +13 -1
  340. package/src/postgres.ts +16 -0
  341. package/src/serializer.ts +13 -2
  342. package/src/skills/chant-sql-postgres-migration.md +3 -3
  343. package/src/skills/chant-sql-postgres.md +6 -1
  344. package/src/skills/chant-sql.md +2 -1
  345. package/src/sql-types-command.ts +38 -16
  346. package/src/sql-types.ts +11 -6
  347. package/src/sqlserver/fixture.test.ts +186 -0
  348. package/src/sqlserver/import-cli.test.ts +61 -0
  349. package/src/sqlserver/lexer.ts +260 -0
  350. package/src/sqlserver/parse.ts +865 -0
  351. package/src/sqlserver/translate.test.ts +396 -0
  352. package/src/sqlserver/translate.ts +558 -0
  353. package/src/sqlserver/types.test.ts +102 -0
  354. package/src/sqlserver/types.ts +154 -0
  355. package/src/sqlserver/validate.e2e.test.ts +32 -0
  356. package/src/sqlserver/validate.test.ts +49 -0
  357. package/src/sqlserver/validate.ts +76 -0
  358. package/src/validate.ts +5 -0
  359. package/src/workspace-kinds.test.ts +11 -0
  360. package/workspace-kinds.json +100 -2
@@ -0,0 +1,358 @@
1
+ /**
2
+ * The Drizzle export (#4401): `renderDrizzle`, `chant sql drizzle`, and the
3
+ * workspace `generated` entry that keeps the file current.
4
+ *
5
+ * Two golden files under `test-fixtures/drizzle/` hold the output for
6
+ * `examples/postgres-getting-started` and for a schema that uses every
7
+ * mapping. `drizzle-types.e2e.test.ts` type-checks them against drizzle-orm.
8
+ * Set `CHANT_UPDATE_GOLDEN=1` to rewrite them after a deliberate change.
9
+ */
10
+
11
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, symlinkSync, writeFileSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
16
+ import { build } from "@intentius/chant/build";
17
+ import { resolveProjectBuildOptions } from "@intentius/chant/cli/build-options";
18
+ import type { Declarable } from "@intentius/chant/declarable";
19
+ import { readDeclaration } from "@intentius/chant/workspace/declaration";
20
+ import { checkGenerated, gatherGeneratedFacts, WSP_GENERATED_DRIFT, type RenderSkills } from "@intentius/chant/workspace/checks/generated";
21
+ import { workingTree } from "@intentius/chant/workspace/tree";
22
+ import * as ch from "./clickhouse/entities";
23
+ import * as pg from "./postgres/entities";
24
+ import { checkName, chooseName, makeObjectName, referencedColumns } from "./postgres/constraint-names";
25
+ import { DRIZZLE_ORM_CHECKED, DrizzleDialectError, renderDrizzle } from "./drizzle";
26
+ import { runDrizzle } from "./drizzle-command";
27
+ import { sqlPlugin } from "./plugin";
28
+ import { sqlSerializer } from "./serializer";
29
+
30
+ const here = dirname(fileURLToPath(import.meta.url));
31
+ const lexiconDir = join(here, "..");
32
+ const repoNodeModules = resolve(here, "../../../node_modules");
33
+ const fixtures = join(lexiconDir, "test-fixtures", "drizzle");
34
+ const POSTGRES = { sql: { dialect: "postgres" } };
35
+
36
+ const entityMap = (o: Record<string, unknown>) => new Map(Object.entries(o)) as Map<string, Declarable>;
37
+
38
+ /** Compare with a golden file, or rewrite it under CHANT_UPDATE_GOLDEN=1. */
39
+ function golden(file: string, text: string): void {
40
+ if (process.env.CHANT_UPDATE_GOLDEN === "1") {
41
+ mkdirSync(dirname(file), { recursive: true });
42
+ writeFileSync(file, text);
43
+ }
44
+ expect(text).toBe(readFileSync(file, "utf8"));
45
+ }
46
+
47
+ async function exampleEntities(example: string): Promise<Map<string, Declarable>> {
48
+ const dir = join(lexiconDir, "examples", example);
49
+ const options = resolveProjectBuildOptions({ config: {}, configDir: dir, plugins: [sqlPlugin], modes: { fold: true, sandbox: false } });
50
+ const result = await build(join(dir, "src"), [sqlSerializer], undefined, options);
51
+ expect(result.errors.map(String)).toEqual([]);
52
+ return result.entities;
53
+ }
54
+
55
+ describe("the names Postgres gives", () => {
56
+ test("a name is the table, the columns and a label, cut to 63 bytes by shortening the longer part", () => {
57
+ expect(makeObjectName("orders", "user_id", "fkey")).toBe("orders_user_id_fkey");
58
+ expect(makeObjectName("orders", undefined, "pkey")).toBe("orders_pkey");
59
+ const long = makeObjectName("a_very_long_table_name_for_a_test_of_the_limit", "a_very_long_column_name_that_goes_on", "fkey");
60
+ expect(long).toHaveLength(63);
61
+ expect(long.endsWith("_fkey")).toBe(true);
62
+ });
63
+
64
+ test("a taken name gets a number after its label", () => {
65
+ expect(chooseName("t", "a", "key", new Set(["t_a_key"]))).toBe("t_a_key1");
66
+ expect(chooseName("t", "a", "key", new Set(["t_a_key", "t_a_key1"]))).toBe("t_a_key2");
67
+ });
68
+
69
+ test("a check is named for the one column it reads, else for the table", () => {
70
+ const cols = ["n", "ratio", "label"];
71
+ expect(referencedColumns("n > 0 AND n < 100", cols)).toEqual(["n"]);
72
+ expect(referencedColumns("length(label) > 2 AND label <> 'n'", cols)).toEqual(["label"]);
73
+ expect(checkName("t", "n > 0", cols, new Set())).toBe("t_n_check");
74
+ expect(checkName("t", "n > ratio", cols, new Set())).toBe("t_check");
75
+ expect(checkName("t", "n > ratio", cols, new Set(["t_check"]))).toBe("t_check1");
76
+ });
77
+ });
78
+
79
+ describe("renderDrizzle", () => {
80
+ test("examples/postgres-getting-started matches its golden file", async () => {
81
+ const text = renderDrizzle(await exampleEntities("postgres-getting-started"), { config: POSTGRES }).text;
82
+ expect(text.split("\n")[0]).toContain("Generated by chant");
83
+ golden(join(fixtures, "postgres-getting-started", "schema.ts"), text);
84
+ });
85
+
86
+ test("the getting-started tables, with the names Postgres gives their constraints", async () => {
87
+ const r = renderDrizzle(await exampleEntities("postgres-getting-started"), { config: POSTGRES });
88
+ expect(r.counts).toEqual({ tables: 2, views: 1, enums: 1, sequences: 1 });
89
+ expect(r.text).toContain('export const app = pgSchema("app");');
90
+ expect(r.text).toContain('export const orderStatus = app.enum("order_status", ["placed", "paid", "shipped", "cancelled"]);');
91
+ expect(r.text).toContain('export const invoiceSeq = app.sequence("invoice_seq", { startWith: 1000 });');
92
+ expect(r.text).toContain('id: bigint({ mode: "number" }).generatedAlwaysAsIdentity().primaryKey(),');
93
+ expect(r.text).toContain('status: orderStatus().notNull().default("placed"),');
94
+ expect(r.text).toContain('amount: numeric({ precision: 12, scale: 2 }).notNull(),');
95
+ expect(r.text).toContain("invoice_no: bigint({ mode: \"number\" }).notNull().default(sql`nextval('app.invoice_seq'::regclass)`),");
96
+ expect(r.text).toContain('foreignKey({ name: "orders_user_id_fkey", columns: [t.user_id], foreignColumns: [users.id] }).onDelete("cascade"),');
97
+ expect(r.text).toContain('check("orders_amount_check", sql`amount >= 0`),');
98
+ expect(r.text).toContain('index("orders_user_id_idx").on(t.user_id, t.placed_at.desc().nullsFirst()),');
99
+ expect(r.text).toContain('unique("users_email_key").on(t.email),');
100
+ expect(r.text).toContain('}).existing();');
101
+ expect(r.skipped).toEqual([]);
102
+ expect(r.approximated).toEqual(["app.order_totals: order_count, total are expressions, so their types are unknown"]);
103
+ });
104
+
105
+ test("--bigint bigint reads a bigint column as bigint", async () => {
106
+ const text = renderDrizzle(await exampleEntities("postgres-getting-started"), { config: POSTGRES, bigintMode: "bigint" }).text;
107
+ expect(text).toContain('id: bigint({ mode: "bigint" }).generatedAlwaysAsIdentity().primaryKey(),');
108
+ });
109
+
110
+ test("every mapping: types, defaults, keys, indexes, and what is left out", () => {
111
+ const shop = pg.schema`CREATE SCHEMA shop`;
112
+ const mood = pg.type`CREATE TYPE ${shop}.mood AS ENUM ('sad', 'ok', 'happy')`;
113
+ const kind = pg.type`CREATE TYPE public.kind AS ENUM ('a', 'b')`;
114
+ const email = pg.domain`CREATE DOMAIN ${shop}.email AS text CHECK (VALUE LIKE '%@%')`;
115
+ const tick = pg.sequence`CREATE SEQUENCE ${shop}.tick AS bigint INCREMENT BY 5 MINVALUE 10 MAXVALUE 1000000 START WITH 10 CACHE 2 CYCLE`;
116
+ const small = pg.sequence`CREATE SEQUENCE ${shop}.small AS integer`;
117
+ const accounts = pg.table`
118
+ CREATE TABLE ${shop}.accounts (
119
+ id bigserial PRIMARY KEY,
120
+ owner_id bigint,
121
+ mail ${email} NOT NULL,
122
+ mood ${mood} DEFAULT 'ok',
123
+ moods ${mood}[],
124
+ tags text[] NOT NULL DEFAULT '{}',
125
+ meta jsonb,
126
+ raw bytea,
127
+ search tsvector,
128
+ token uuid NOT NULL,
129
+ code varchar(12) NOT NULL DEFAULT 'x',
130
+ flag char(2),
131
+ ok boolean NOT NULL DEFAULT false,
132
+ n integer NOT NULL DEFAULT 0,
133
+ price numeric NOT NULL DEFAULT 0,
134
+ ratio real,
135
+ big double precision,
136
+ day date,
137
+ at timestamp(3) without time zone,
138
+ atz timestamptz DEFAULT now(),
139
+ t time(2) with time zone,
140
+ span interval day to second(3),
141
+ spot point,
142
+ ip inet,
143
+ low smallint GENERATED BY DEFAULT AS IDENTITY (START WITH 100 INCREMENT BY 2),
144
+ area integer GENERATED ALWAYS AS (n * 2) STORED,
145
+ CONSTRAINT accounts_owner_fk FOREIGN KEY (owner_id) REFERENCES ${shop}.accounts (id) ON UPDATE SET NULL,
146
+ CHECK (n > 0 AND n < 100),
147
+ CHECK (n > ratio),
148
+ UNIQUE (token, code)
149
+ )`;
150
+ const links = pg.table`
151
+ CREATE TABLE ${shop}.links (
152
+ a bigint NOT NULL REFERENCES ${accounts} (${accounts.columns.id}) ON DELETE RESTRICT,
153
+ b bigint NOT NULL REFERENCES ${accounts},
154
+ note text,
155
+ PRIMARY KEY (a, b)
156
+ )`;
157
+ const ca = pg.table`CREATE TABLE ${shop}.ca (id bigint PRIMARY KEY, b_id bigint REFERENCES shop.cb (id))`;
158
+ const cb = pg.table`CREATE TABLE ${shop}.cb (id bigint PRIMARY KEY, a_id bigint REFERENCES ${ca} (${ca.columns.id}))`;
159
+ const plain = pg.table`CREATE TABLE public.plain (id integer PRIMARY KEY, kind ${kind} NOT NULL, label text UNIQUE)`;
160
+ const lowerMail = pg.index`CREATE UNIQUE INDEX accounts_lower_mail ON ${accounts} (lower(mail)) WHERE ok`;
161
+ const search = pg.index`CREATE INDEX accounts_search ON ${accounts} USING gin (search)`;
162
+ const atIdx = pg.index`CREATE INDEX accounts_at ON ${accounts} (at DESC NULLS LAST, atz)`;
163
+ const incl = pg.index`CREATE INDEX accounts_incl ON ${accounts} (mail) INCLUDE (code)`;
164
+ const opclass = pg.index`CREATE INDEX plain_label ON public.plain (label text_pattern_ops)`;
165
+ const v = pg.view`CREATE VIEW ${shop}.v AS SELECT a.${accounts.columns.mail}, a.${accounts.columns.mood}, a.${accounts.columns.n} + 1 AS n1 FROM ${accounts} a`;
166
+ const mv = pg.view`CREATE MATERIALIZED VIEW ${shop}.mv AS SELECT l.${links.columns.a} FROM ${links} l`;
167
+ const fn = pg.func`CREATE FUNCTION ${shop}.f() RETURNS integer LANGUAGE sql AS 'select 1'`;
168
+ const r = renderDrizzle(entityMap({ shop, mood, kind, email, tick, small, accounts, links, ca, cb, plain, lowerMail, search, atIdx, incl, opclass, v, mv, fn }), { config: POSTGRES });
169
+
170
+ const t = r.text;
171
+ // Types.
172
+ expect(t).toContain('id: bigserial({ mode: "number" }).primaryKey(),');
173
+ expect(t).toContain("mail: shop_emailType().notNull(),");
174
+ expect(t).toContain('const shop_emailType = customType<{ data: string }>({ dataType: () => "shop.email" });');
175
+ expect(t).toContain('const byteaType = customType<{ data: Uint8Array }>({ dataType: () => "bytea" });');
176
+ expect(t).toContain('const tsvectorType = customType<{ data: string }>({ dataType: () => "tsvector" });');
177
+ expect(t).toContain('mood: mood().default("ok"),');
178
+ expect(t).toContain("moods: mood().array(),");
179
+ expect(t).toContain('kind: kind().notNull(),');
180
+ expect(t).toContain("tags: text().array().notNull().default(sql`'{}'`),");
181
+ expect(t).toContain('code: varchar({ length: 12 }).notNull().default("x"),');
182
+ expect(t).toContain("flag: char({ length: 2 }),");
183
+ expect(t).toContain("ok: boolean().notNull().default(false),");
184
+ expect(t).toContain("n: integer().notNull().default(0),");
185
+ expect(t).toContain("price: numeric().notNull().default(sql`0`),");
186
+ expect(t).toContain("at: timestamp({ precision: 3 }),");
187
+ expect(t).toContain("atz: timestamp({ withTimezone: true }).default(sql`now()`),");
188
+ expect(t).toContain("t: time({ precision: 2, withTimezone: true }),");
189
+ expect(t).toContain('span: interval({ fields: "day to second", precision: 3 }),');
190
+ expect(t).toContain('spot: point({ mode: "xy" }),');
191
+ expect(t).toContain("low: smallint().generatedByDefaultAsIdentity({ startWith: 100, increment: 2 }).notNull(),");
192
+ expect(t).toContain("area: integer().generatedAlwaysAs(sql`n * 2`),");
193
+ // Keys, in the order Drizzle's extra config takes them.
194
+ expect(t).toContain('unique("accounts_token_code_key").on(t.token, t.code),');
195
+ expect(t).toContain('check("accounts_n_check", sql`n > 0 AND n < 100`),');
196
+ expect(t).toContain('check("accounts_check", sql`n > ratio`),');
197
+ expect(t).toContain('foreignKey({ name: "accounts_owner_fk", columns: [t.owner_id], foreignColumns: [t.id] }).onUpdate("set null"),');
198
+ expect(t).toContain('primaryKey({ name: "links_pkey", columns: [t.a, t.b] }),');
199
+ expect(t).toContain('foreignKey({ name: "links_a_fkey", columns: [t.a], foreignColumns: [accounts.id] }).onDelete("restrict"),');
200
+ expect(t).toContain('foreignKey({ name: "links_b_fkey", columns: [t.b], foreignColumns: [accounts.id] }),');
201
+ expect(t).toContain('unique("plain_label_key").on(t.label),');
202
+ // Two tables that reference each other need the callback's return type, so tsc can infer both.
203
+ expect(t).toContain('export const ca = shop.table("ca", {');
204
+ expect(t).toContain("}, (t): PgTableExtraConfigValue[] => [");
205
+ expect(t).toContain('import type { PgTableExtraConfigValue } from "drizzle-orm/pg-core";');
206
+ // Indexes.
207
+ expect(t).toContain('index("accounts_at").on(t.at.desc().nullsLast(), t.atz),');
208
+ expect(t).toContain("uniqueIndex(\"accounts_lower_mail\").on(sql`lower(mail)`).where(sql`ok`),");
209
+ expect(t).toContain('index("accounts_search").using("gin", t.search),');
210
+ // Schemas, sequences, views.
211
+ expect(t).toContain('export const kind = pgEnum("kind", ["a", "b"]);');
212
+ expect(t).toContain('export const plain = pgTable("plain", {');
213
+ expect(t).toContain('export const tick = shop.sequence("tick", { increment: 5, minValue: 10, maxValue: 1000000, startWith: 10, cache: 2, cycle: true });');
214
+ expect(t).toContain('export const mv = shop.materializedView("mv", {');
215
+ expect(t).toContain(" n1: unknownColumn(),");
216
+ expect(t).not.toContain("pgSchema(\"public\")");
217
+
218
+ // What it leaves out, by kind, with the reason; and the header says so.
219
+ const skipped = Object.fromEntries(r.skipped.map((s) => [`${s.kind}: ${s.names.join(", ")}`, s.reason]));
220
+ expect(Object.keys(skipped).sort()).toEqual([
221
+ "Postgres::Domain: shop.email",
222
+ "Postgres::Function: shop.f",
223
+ "Postgres::Index: public.plain_label",
224
+ "Postgres::Index: shop.accounts_incl",
225
+ "Postgres::Sequence: shop.small",
226
+ ]);
227
+ expect(t).toContain("// Postgres::Function: shop.f (Drizzle has no routines)");
228
+ golden(join(fixtures, "mappings", "schema.ts"), t);
229
+ });
230
+
231
+ test("a ClickHouse build is refused, since Drizzle has no ClickHouse dialect", () => {
232
+ const db = ch.database`CREATE DATABASE shop`;
233
+ const e = ch.table`CREATE TABLE ${db}.e (k String) ENGINE = MergeTree ORDER BY k`;
234
+ expect(() => renderDrizzle(entityMap({ db, e }))).toThrow(DrizzleDialectError);
235
+ expect(() => renderDrizzle(entityMap({ db, e }))).toThrow(/Drizzle has no clickhouse dialect/);
236
+ });
237
+
238
+ test("with nothing declared, a module that exports nothing", () => {
239
+ expect(renderDrizzle(new Map(), { config: POSTGRES }).text).toMatch(/\nexport \{\};\n$/);
240
+ });
241
+
242
+ test("it names the drizzle-orm version it was checked against", () => {
243
+ expect(DRIZZLE_ORM_CHECKED).toMatch(/^\d+\.\d+\.\d+$/);
244
+ expect(renderDrizzle(new Map(), { config: POSTGRES }).text).toContain(`drizzle-orm ${DRIZZLE_ORM_CHECKED}`);
245
+ });
246
+ });
247
+
248
+ describe("chant sql drizzle, and the workspace check that keeps its file current", () => {
249
+ let root: string;
250
+ let cwd: string;
251
+ let errors: string[];
252
+ let logs: string[];
253
+
254
+ const schema = (amount: string) =>
255
+ 'import { schema, table } from "@intentius/chant-lexicon-sql/postgres";\n' +
256
+ "export const shop = schema`CREATE SCHEMA shop`;\n" +
257
+ "export const orders = table`CREATE TABLE ${shop}.orders (id bigint PRIMARY KEY, " +
258
+ `amount ${amount})\`;\n`;
259
+ const write = (amount: string) => writeFileSync(join(root, "member", "src", "schema.ts"), schema(amount));
260
+ const drizzle = async (...args: string[]) => {
261
+ process.chdir(join(root, "member"));
262
+ return runDrizzle({ verb: "drizzle", rawArgs: args });
263
+ };
264
+
265
+ beforeEach(() => {
266
+ cwd = process.cwd();
267
+ root = realpathSync(mkdtempSync(join(tmpdir(), "chant-4401-")));
268
+ mkdirSync(join(root, "member", "src"), { recursive: true });
269
+ symlinkSync(repoNodeModules, join(root, "member", "node_modules"), "dir");
270
+ writeFileSync(join(root, "member", "chant.config.json"), JSON.stringify({ lexicons: ["sql"], sourceDir: "src", sql: { dialect: "postgres" } }));
271
+ write("numeric(12,2)");
272
+ errors = [];
273
+ logs = [];
274
+ vi.spyOn(console, "error").mockImplementation((m: unknown) => void errors.push(String(m)));
275
+ vi.spyOn(console, "log").mockImplementation((m: unknown) => void logs.push(String(m)));
276
+ process.env.NO_COLOR = "1";
277
+ });
278
+
279
+ afterEach(() => {
280
+ process.chdir(cwd);
281
+ vi.restoreAllMocks();
282
+ delete process.env.NO_COLOR;
283
+ });
284
+
285
+ test("writes drizzle-schema.ts in the project, outside the source directory, and --check follows the declaration", async () => {
286
+ expect(await drizzle()).toBe(0);
287
+ const file = join(root, "member", "drizzle-schema.ts");
288
+ const written = readFileSync(file, "utf8");
289
+ expect(written).toContain('export const orders = shop.table("orders", {');
290
+ expect(written).toContain("amount: numeric({ precision: 12, scale: 2 }),");
291
+ expect(logs.join("\n")).toContain("wrote drizzle-schema.ts: 1 table, 0 views, 0 enums, 0 sequences");
292
+ expect(existsSync(join(root, "member", "src", "drizzle-schema.ts"))).toBe(false);
293
+
294
+ expect(await drizzle("--check")).toBe(0);
295
+ write("bigint");
296
+ expect(await drizzle("--check")).toBe(1);
297
+ expect(errors.join("\n")).toContain("drizzle-schema.ts differs");
298
+ expect(readFileSync(file, "utf8")).toBe(written);
299
+ }, 30_000);
300
+
301
+ test("--check on a missing file exits 1 and writes nothing; -o and --output write elsewhere", async () => {
302
+ expect(await drizzle("--check")).toBe(1);
303
+ expect(errors.join("\n")).toContain("drizzle-schema.ts does not exist");
304
+ expect(existsSync(join(root, "member", "drizzle-schema.ts"))).toBe(false);
305
+ expect(await drizzle("-o", "db/schema.ts")).toBe(0);
306
+ expect(await drizzle("--check", "--output=db/schema.ts")).toBe(0);
307
+ expect(existsSync(join(root, "member", "db", "schema.ts"))).toBe(true);
308
+ }, 30_000);
309
+
310
+ test("a bad argument and a ClickHouse project exit 1", async () => {
311
+ expect(await drizzle("--bogus")).toBe(1);
312
+ expect(await drizzle("--bigint", "string")).toBe(1);
313
+ writeFileSync(join(root, "member", "chant.config.json"), JSON.stringify({ lexicons: ["sql"], sourceDir: "src", sql: { dialect: "clickhouse" } }));
314
+ writeFileSync(join(root, "member", "src", "schema.ts"), 'import { database, table } from "@intentius/chant-lexicon-sql/clickhouse";\nexport const shop = database`CREATE DATABASE shop`;\nexport const e = table`CREATE TABLE ${shop}.e (k String) ENGINE = MergeTree ORDER BY k`;\n');
315
+ expect(await drizzle()).toBe(1);
316
+ expect(errors.join("\n")).toContain("Drizzle has no clickhouse dialect");
317
+ }, 30_000);
318
+
319
+ test("changing a column without regenerating fails the workspace's generated-file check", async () => {
320
+ writeFileSync(
321
+ join(root, "chant.workspace.json"),
322
+ JSON.stringify({
323
+ name: "shop",
324
+ schema: 1,
325
+ members: [{ name: "member", dir: "member", kind: "chant", generated: [{ path: "drizzle-schema.ts", generator: "chant sql drizzle --output drizzle-schema.ts", sources: ["member/src"] }] }],
326
+ }),
327
+ );
328
+ expect(await drizzle()).toBe(0);
329
+
330
+ // The generator the check runs is `chant sql drizzle --output <temp>`; it is a process, so the
331
+ // text it would write for the declaration as it now stands is rendered first and handed to a stand-in.
332
+ const standIn = async (): Promise<GeneratorRun> => {
333
+ expect(await drizzle("--output", "next.ts")).toBe(0);
334
+ const text = readFileSync(join(root, "member", "next.ts"), "utf8");
335
+ return ({ argv }) => {
336
+ expect(argv.slice(0, 4)).toEqual(["chant", "sql", "drizzle", "--output"]);
337
+ writeFileSync(argv[4]!, text);
338
+ return { ok: true };
339
+ };
340
+ };
341
+ type GeneratorRun = NonNullable<Parameters<typeof gatherGeneratedFacts>[2]>["runGenerator"];
342
+ const noSkills: RenderSkills = async () => ({ status: "rendered", files: new Map() });
343
+ const findings = async (runGenerator: GeneratorRun) => {
344
+ const facts = await gatherGeneratedFacts(root, readDeclaration(workingTree(root)), { runGenerators: true, renderSkills: noSkills, runGenerator });
345
+ return checkGenerated(facts);
346
+ };
347
+
348
+ expect(await findings(await standIn())).toEqual([]);
349
+
350
+ write("bigint");
351
+ const stale = await findings(await standIn());
352
+ expect(stale.map((f) => [f.id, f.severity, f.path])).toEqual([[WSP_GENERATED_DRIFT, "error", "member/drizzle-schema.ts"]]);
353
+ expect(stale[0]!.message).toContain("differs from what `chant sql drizzle --output drizzle-schema.ts` writes");
354
+
355
+ expect(await drizzle()).toBe(0);
356
+ expect(await findings(await standIn())).toEqual([]);
357
+ }, 60_000);
358
+ });