@intentius/chant-lexicon-sql 0.124.46 → 0.124.49

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 (402) 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/statements.d.ts +29 -0
  37. package/dist/clickhouse/plan/statements.d.ts.map +1 -0
  38. package/dist/clickhouse/plan/statements.js +334 -0
  39. package/dist/clickhouse/plan/stream.d.ts +40 -0
  40. package/dist/clickhouse/plan/stream.d.ts.map +1 -0
  41. package/dist/clickhouse/plan/stream.js +107 -0
  42. package/dist/clickhouse/rebuild/observe.d.ts +3 -1
  43. package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
  44. package/dist/clickhouse/rebuild/observe.js +14 -1
  45. package/dist/clickhouse/review-catalog.d.ts +213 -0
  46. package/dist/clickhouse/review-catalog.d.ts.map +1 -0
  47. package/dist/clickhouse/review-catalog.js +330 -0
  48. package/dist/codegen/classifier-page.d.ts.map +1 -1
  49. package/dist/codegen/classifier-page.js +44 -4
  50. package/dist/codegen/clickhouse-module.d.ts.map +1 -1
  51. package/dist/codegen/clickhouse-module.js +3 -0
  52. package/dist/codegen/coverage-page.d.ts.map +1 -1
  53. package/dist/codegen/coverage-page.js +5 -3
  54. package/dist/codegen/docs.js +1 -1
  55. package/dist/codegen/postgres-classifier-page.js +2 -2
  56. package/dist/codegen/registry.d.ts.map +1 -1
  57. package/dist/codegen/registry.js +10 -0
  58. package/dist/composites/catalog.d.ts.map +1 -1
  59. package/dist/composites/catalog.js +100 -0
  60. package/dist/composites/clickhouse.d.ts +2 -0
  61. package/dist/composites/clickhouse.d.ts.map +1 -1
  62. package/dist/composites/clickhouse.js +2 -0
  63. package/dist/composites/otel-tables.d.ts +100 -0
  64. package/dist/composites/otel-tables.d.ts.map +1 -0
  65. package/dist/composites/otel-tables.js +282 -0
  66. package/dist/composites/stream-ingest.d.ts +63 -0
  67. package/dist/composites/stream-ingest.d.ts.map +1 -0
  68. package/dist/composites/stream-ingest.js +39 -0
  69. package/dist/core/statement-class.d.ts +55 -0
  70. package/dist/core/statement-class.d.ts.map +1 -0
  71. package/dist/core/statement-class.js +43 -0
  72. package/dist/coverage/clickhouse.json +1 -1
  73. package/dist/coverage/postgres.json +12 -12
  74. package/dist/coverage.d.ts +16 -6
  75. package/dist/coverage.d.ts.map +1 -1
  76. package/dist/coverage.js +10 -5
  77. package/dist/drizzle-command.d.ts +17 -0
  78. package/dist/drizzle-command.d.ts.map +1 -0
  79. package/dist/drizzle-command.js +91 -0
  80. package/dist/drizzle.d.ts +70 -0
  81. package/dist/drizzle.d.ts.map +1 -0
  82. package/dist/drizzle.js +689 -0
  83. package/dist/files/clickhouse.d.ts.map +1 -1
  84. package/dist/files/clickhouse.js +8 -1
  85. package/dist/files/common.d.ts +13 -0
  86. package/dist/files/common.d.ts.map +1 -1
  87. package/dist/files/common.js +5 -0
  88. package/dist/files/postgres.d.ts.map +1 -1
  89. package/dist/files/postgres.js +35 -3
  90. package/dist/generated/clickhouse.d.ts +2 -0
  91. package/dist/generated/clickhouse.d.ts.map +1 -1
  92. package/dist/generated/clickhouse.js +2 -0
  93. package/dist/import-parser.d.ts +21 -1
  94. package/dist/import-parser.d.ts.map +1 -1
  95. package/dist/import-parser.js +79 -5
  96. package/dist/index.d.ts +1 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +1 -1
  99. package/dist/integrity.json +7 -6
  100. package/dist/lint/post-synth/audit-entries.d.ts.map +1 -1
  101. package/dist/lint/post-synth/audit-entries.js +1 -0
  102. package/dist/lint/post-synth/index.d.ts.map +1 -1
  103. package/dist/lint/post-synth/index.js +2 -0
  104. package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
  105. package/dist/lint/post-synth/postgres-names.js +3 -0
  106. package/dist/lint/post-synth/sqlch128.d.ts +18 -0
  107. package/dist/lint/post-synth/sqlch128.d.ts.map +1 -0
  108. package/dist/lint/post-synth/sqlch128.js +222 -0
  109. package/dist/lint/rules/postgres-templates.js +2 -2
  110. package/dist/lsp/postgres-template.d.ts.map +1 -1
  111. package/dist/lsp/postgres-template.js +21 -0
  112. package/dist/manifest.json +1 -1
  113. package/dist/mcp/postgres.js +1 -1
  114. package/dist/meta.json +50 -0
  115. package/dist/migrate/from-postgres/detect.d.ts +4 -0
  116. package/dist/migrate/from-postgres/detect.d.ts.map +1 -0
  117. package/dist/migrate/from-postgres/detect.js +7 -0
  118. package/dist/migrate/from-postgres/emit.d.ts +26 -0
  119. package/dist/migrate/from-postgres/emit.d.ts.map +1 -0
  120. package/dist/migrate/from-postgres/emit.js +86 -0
  121. package/dist/migrate/from-postgres/index.d.ts +51 -0
  122. package/dist/migrate/from-postgres/index.d.ts.map +1 -0
  123. package/dist/migrate/from-postgres/index.js +115 -0
  124. package/dist/migrate/from-postgres/translate.d.ts +73 -0
  125. package/dist/migrate/from-postgres/translate.d.ts.map +1 -0
  126. package/dist/migrate/from-postgres/translate.js +261 -0
  127. package/dist/migrate/from-postgres/types.d.ts +51 -0
  128. package/dist/migrate/from-postgres/types.d.ts.map +1 -0
  129. package/dist/migrate/from-postgres/types.js +211 -0
  130. package/dist/migration-classify.d.ts +23 -0
  131. package/dist/migration-classify.d.ts.map +1 -0
  132. package/dist/migration-classify.js +44 -0
  133. package/dist/migration-replay.d.ts +73 -0
  134. package/dist/migration-replay.d.ts.map +1 -0
  135. package/dist/migration-replay.js +294 -0
  136. package/dist/okf/index.md +11 -0
  137. package/dist/okf/rules/SQLCH128.md +11 -0
  138. package/dist/okf/types/PostgresForeignDataWrapper.md +9 -0
  139. package/dist/okf/types/PostgresForeignServer.md +9 -0
  140. package/dist/okf/types/PostgresForeignTable.md +9 -0
  141. package/dist/okf/types/PostgresOperator.md +9 -0
  142. package/dist/okf/types/PostgresOperatorClass.md +9 -0
  143. package/dist/okf/types/PostgresOperatorFamily.md +9 -0
  144. package/dist/okf/types/PostgresPublication.md +9 -0
  145. package/dist/okf/types/PostgresStatistics.md +9 -0
  146. package/dist/okf/types/PostgresTextSearchConfiguration.md +9 -0
  147. package/dist/okf/types/PostgresTextSearchDictionary.md +9 -0
  148. package/dist/op/activities/postgres-migration.d.ts.map +1 -1
  149. package/dist/op/activities/postgres-migration.js +4 -0
  150. package/dist/op/activity-contracts.d.ts +20 -0
  151. package/dist/op/activity-contracts.d.ts.map +1 -1
  152. package/dist/op/activity-contracts.js +2 -0
  153. package/dist/plugin.d.ts.map +1 -1
  154. package/dist/plugin.js +49 -2
  155. package/dist/postgres/access/acl.js +1 -1
  156. package/dist/postgres/access/import.js +1 -1
  157. package/dist/postgres/apply/apply.js +13 -1
  158. package/dist/postgres/apply/statements.d.ts.map +1 -1
  159. package/dist/postgres/apply/statements.js +243 -4
  160. package/dist/postgres/constraint-names.d.ts +30 -0
  161. package/dist/postgres/constraint-names.d.ts.map +1 -0
  162. package/dist/postgres/constraint-names.js +72 -0
  163. package/dist/postgres/entities.d.ts +229 -2
  164. package/dist/postgres/entities.d.ts.map +1 -1
  165. package/dist/postgres/entities.js +326 -5
  166. package/dist/postgres/entity-types.d.ts +10 -0
  167. package/dist/postgres/entity-types.d.ts.map +1 -1
  168. package/dist/postgres/entity-types.js +10 -0
  169. package/dist/postgres/import/generator.d.ts.map +1 -1
  170. package/dist/postgres/import/generator.js +65 -2
  171. package/dist/postgres/import/live-export.d.ts.map +1 -1
  172. package/dist/postgres/import/live-export.js +2 -0
  173. package/dist/postgres/live/catalog-kinds.d.ts +20 -0
  174. package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
  175. package/dist/postgres/live/catalog-kinds.js +452 -0
  176. package/dist/postgres/live/catalog.d.ts +2 -0
  177. package/dist/postgres/live/catalog.d.ts.map +1 -1
  178. package/dist/postgres/live/catalog.js +3 -1
  179. package/dist/postgres/live/describe-resources.d.ts.map +1 -1
  180. package/dist/postgres/live/describe-resources.js +12 -2
  181. package/dist/postgres/migrate/backfill.d.ts +2 -0
  182. package/dist/postgres/migrate/backfill.d.ts.map +1 -1
  183. package/dist/postgres/migrate/backfill.js +7 -1
  184. package/dist/postgres/migrate/carry.d.ts +45 -1
  185. package/dist/postgres/migrate/carry.d.ts.map +1 -1
  186. package/dist/postgres/migrate/carry.js +72 -8
  187. package/dist/postgres/migrate/observe.d.ts +28 -4
  188. package/dist/postgres/migrate/observe.d.ts.map +1 -1
  189. package/dist/postgres/migrate/observe.js +45 -13
  190. package/dist/postgres/migrate/op.d.ts +26 -1
  191. package/dist/postgres/migrate/op.d.ts.map +1 -1
  192. package/dist/postgres/migrate/op.js +3 -1
  193. package/dist/postgres/migrate/replication.d.ts +42 -0
  194. package/dist/postgres/migrate/replication.d.ts.map +1 -1
  195. package/dist/postgres/migrate/replication.js +53 -0
  196. package/dist/postgres/migrate/steps.d.ts +35 -2
  197. package/dist/postgres/migrate/steps.d.ts.map +1 -1
  198. package/dist/postgres/migrate/steps.js +158 -12
  199. package/dist/postgres/parser.d.ts +144 -1
  200. package/dist/postgres/parser.d.ts.map +1 -1
  201. package/dist/postgres/parser.js +370 -4
  202. package/dist/postgres/plan/deep.d.ts.map +1 -1
  203. package/dist/postgres/plan/deep.js +43 -4
  204. package/dist/postgres/plan/diff.d.ts.map +1 -1
  205. package/dist/postgres/plan/diff.js +154 -1
  206. package/dist/postgres/plan/normalize.d.ts +28 -2
  207. package/dist/postgres/plan/normalize.d.ts.map +1 -1
  208. package/dist/postgres/plan/normalize.js +260 -1
  209. package/dist/postgres/plan/rules.d.ts +4 -0
  210. package/dist/postgres/plan/rules.d.ts.map +1 -1
  211. package/dist/postgres/plan/rules.js +6 -2
  212. package/dist/postgres/plan/schema.js +1 -1
  213. package/dist/postgres/plan/statements.d.ts +26 -0
  214. package/dist/postgres/plan/statements.d.ts.map +1 -0
  215. package/dist/postgres/plan/statements.js +364 -0
  216. package/dist/postgres/secret-options.d.ts +10 -0
  217. package/dist/postgres/secret-options.d.ts.map +1 -0
  218. package/dist/postgres/secret-options.js +9 -0
  219. package/dist/postgres/testing/roundtrip-schema.d.ts +4 -2
  220. package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
  221. package/dist/postgres/testing/roundtrip-schema.js +24 -1
  222. package/dist/postgres.d.ts +1 -1
  223. package/dist/postgres.d.ts.map +1 -1
  224. package/dist/postgres.js +1 -1
  225. package/dist/rules/audit-entries.ts +1 -0
  226. package/dist/rules/postgres-names.ts +2 -0
  227. package/dist/rules/postgres-templates.ts +2 -2
  228. package/dist/rules/sqlch128.ts +230 -0
  229. package/dist/serializer.d.ts +5 -0
  230. package/dist/serializer.d.ts.map +1 -1
  231. package/dist/serializer.js +13 -2
  232. package/dist/sql-types-command.d.ts +13 -0
  233. package/dist/sql-types-command.d.ts.map +1 -1
  234. package/dist/sql-types-command.js +31 -16
  235. package/dist/sql-types.d.ts +22 -0
  236. package/dist/sql-types.d.ts.map +1 -1
  237. package/dist/sql-types.js +10 -6
  238. package/dist/sqlserver/lexer.d.ts +37 -0
  239. package/dist/sqlserver/lexer.d.ts.map +1 -0
  240. package/dist/sqlserver/lexer.js +254 -0
  241. package/dist/sqlserver/parse.d.ts +185 -0
  242. package/dist/sqlserver/parse.d.ts.map +1 -0
  243. package/dist/sqlserver/parse.js +767 -0
  244. package/dist/sqlserver/translate.d.ts +34 -0
  245. package/dist/sqlserver/translate.d.ts.map +1 -0
  246. package/dist/sqlserver/translate.js +495 -0
  247. package/dist/sqlserver/types.d.ts +44 -0
  248. package/dist/sqlserver/types.d.ts.map +1 -0
  249. package/dist/sqlserver/types.js +133 -0
  250. package/dist/sqlserver/validate.d.ts +31 -0
  251. package/dist/sqlserver/validate.d.ts.map +1 -0
  252. package/dist/sqlserver/validate.js +67 -0
  253. package/dist/validate.d.ts.map +1 -1
  254. package/dist/validate.js +10 -0
  255. package/package.json +2 -2
  256. package/src/change-set.ts +2 -1
  257. package/src/clickhouse/apply/statements.ts +25 -0
  258. package/src/clickhouse/import/live-export.ts +20 -5
  259. package/src/clickhouse/live/named-collections.e2e.test.ts +88 -0
  260. package/src/clickhouse/live/named-collections.ts +61 -0
  261. package/src/clickhouse/live/undeclarable.e2e.test.ts +5 -3
  262. package/src/clickhouse/live/undeclarable.ts +20 -4
  263. package/src/clickhouse/named-collections.test.ts +187 -0
  264. package/src/clickhouse/named-collections.ts +224 -0
  265. package/src/clickhouse/parser.ts +2 -1
  266. package/src/clickhouse/plan/commands.ts +17 -2
  267. package/src/clickhouse/plan/diff.ts +57 -1
  268. package/src/clickhouse/plan/disruption.ts +5 -0
  269. package/src/clickhouse/plan/rebuild-handoff.ts +3 -0
  270. package/src/clickhouse/plan/report.ts +6 -1
  271. package/src/clickhouse/plan/rules.ts +14 -4
  272. package/src/clickhouse/plan/statements.test.ts +61 -0
  273. package/src/clickhouse/plan/statements.ts +306 -0
  274. package/src/clickhouse/plan/stream-s3queue.e2e.test.ts +148 -0
  275. package/src/clickhouse/plan/stream.test.ts +237 -0
  276. package/src/clickhouse/plan/stream.ts +111 -0
  277. package/src/clickhouse/rebuild/observe.ts +15 -1
  278. package/src/clickhouse/review-catalog.test.ts +181 -0
  279. package/src/clickhouse/review-catalog.ts +489 -0
  280. package/src/codegen/classifier-page.ts +44 -4
  281. package/src/codegen/clickhouse-module.ts +3 -0
  282. package/src/codegen/coverage-page.ts +5 -3
  283. package/src/codegen/docs.ts +1 -1
  284. package/src/codegen/postgres-classifier-page.ts +2 -2
  285. package/src/codegen/registry.ts +10 -0
  286. package/src/composites/catalog.test.ts +6 -1
  287. package/src/composites/catalog.ts +100 -0
  288. package/src/composites/clickhouse.ts +2 -0
  289. package/src/composites/composites.test.ts +1 -1
  290. package/src/composites/otel-tables.test.ts +118 -0
  291. package/src/composites/otel-tables.ts +339 -0
  292. package/src/composites/stream-ingest.test.ts +114 -0
  293. package/src/composites/stream-ingest.ts +82 -0
  294. package/src/core/statement-class.ts +88 -0
  295. package/src/core/undeclarable.test.ts +6 -5
  296. package/src/coverage/clickhouse.json +1 -1
  297. package/src/coverage/postgres.json +12 -12
  298. package/src/coverage.test.ts +27 -2
  299. package/src/coverage.ts +18 -7
  300. package/src/drizzle-command.ts +94 -0
  301. package/src/drizzle-kit.e2e.test.ts +89 -0
  302. package/src/drizzle-types.e2e.test.ts +67 -0
  303. package/src/drizzle.test.ts +358 -0
  304. package/src/drizzle.ts +742 -0
  305. package/src/files/clickhouse.ts +8 -1
  306. package/src/files/common.ts +14 -1
  307. package/src/files/postgres.ts +34 -3
  308. package/src/generated/clickhouse.ts +3 -0
  309. package/src/generated/lexicon-sql.json +50 -0
  310. package/src/import-parser.sqlserver.test.ts +92 -0
  311. package/src/import-parser.ts +79 -5
  312. package/src/index.ts +1 -1
  313. package/src/lint/post-synth/audit-entries.ts +1 -0
  314. package/src/lint/post-synth/index.ts +2 -0
  315. package/src/lint/post-synth/postgres-checks.test.ts +8 -1
  316. package/src/lint/post-synth/postgres-names.ts +2 -0
  317. package/src/lint/post-synth/sqlch128.test.ts +107 -0
  318. package/src/lint/post-synth/sqlch128.ts +230 -0
  319. package/src/lint/rules/postgres-templates.ts +2 -2
  320. package/src/lsp/postgres-template.ts +20 -0
  321. package/src/mcp/postgres.ts +1 -1
  322. package/src/migrate/from-postgres/cli.test.ts +165 -0
  323. package/src/migrate/from-postgres/detect.ts +7 -0
  324. package/src/migrate/from-postgres/emit.ts +96 -0
  325. package/src/migrate/from-postgres/example.test.ts +205 -0
  326. package/src/migrate/from-postgres/index.ts +122 -0
  327. package/src/migrate/from-postgres/translate.test.ts +242 -0
  328. package/src/migrate/from-postgres/translate.ts +359 -0
  329. package/src/migrate/from-postgres/types.test.ts +116 -0
  330. package/src/migrate/from-postgres/types.ts +236 -0
  331. package/src/migrate/from-postgres/validate.e2e.test.ts +44 -0
  332. package/src/migrate/from-postgres/workspace.e2e.test.ts +114 -0
  333. package/src/migration-classify.test.ts +150 -0
  334. package/src/migration-classify.ts +45 -0
  335. package/src/migration-replay.e2e.test.ts +155 -0
  336. package/src/migration-replay.test.ts +173 -0
  337. package/src/migration-replay.ts +289 -0
  338. package/src/op/__snapshots__/watch-pipeline.test.ts.snap +2 -2
  339. package/src/op/activities/postgres-migration.ts +4 -0
  340. package/src/op/activity-contracts.ts +2 -0
  341. package/src/op/watch-pipeline.test.ts +8 -1
  342. package/src/plugin.ts +51 -2
  343. package/src/postgres/access/acl.ts +1 -1
  344. package/src/postgres/access/import.ts +1 -1
  345. package/src/postgres/apply/apply.ts +14 -1
  346. package/src/postgres/apply/statements.ts +225 -4
  347. package/src/postgres/constraint-names.ts +71 -0
  348. package/src/postgres/database-kinds.test.ts +865 -0
  349. package/src/postgres/entities.ts +541 -5
  350. package/src/postgres/entity-types.ts +10 -0
  351. package/src/postgres/import/generator.ts +54 -2
  352. package/src/postgres/import/live-export.ts +1 -0
  353. package/src/postgres/live/catalog-kinds.ts +494 -0
  354. package/src/postgres/live/catalog.ts +6 -1
  355. package/src/postgres/live/database-kinds.e2e.test.ts +164 -0
  356. package/src/postgres/live/describe-resources.ts +13 -2
  357. package/src/postgres/live/undeclarable.e2e.test.ts +13 -6
  358. package/src/postgres/migrate/backfill.ts +13 -1
  359. package/src/postgres/migrate/carry.ts +103 -8
  360. package/src/postgres/migrate/followups.e2e.test.ts +218 -0
  361. package/src/postgres/migrate/followups.test.ts +199 -0
  362. package/src/postgres/migrate/kinds.e2e.test.ts +5 -5
  363. package/src/postgres/migrate/observe.ts +56 -15
  364. package/src/postgres/migrate/op.ts +28 -1
  365. package/src/postgres/migrate/partition.e2e.test.ts +129 -0
  366. package/src/postgres/migrate/partition.test.ts +206 -0
  367. package/src/postgres/migrate/replication.ts +66 -0
  368. package/src/postgres/migrate/steps.ts +157 -12
  369. package/src/postgres/migrate/subscriber.e2e.test.ts +141 -0
  370. package/src/postgres/migrate/subscriber.test.ts +59 -0
  371. package/src/postgres/parser.ts +455 -4
  372. package/src/postgres/plan/deep.ts +42 -4
  373. package/src/postgres/plan/diff.ts +137 -1
  374. package/src/postgres/plan/normalize.ts +277 -1
  375. package/src/postgres/plan/rules.ts +32 -4
  376. package/src/postgres/plan/schema.ts +1 -1
  377. package/src/postgres/plan/statements.test.ts +78 -0
  378. package/src/postgres/plan/statements.ts +299 -0
  379. package/src/postgres/secret-options.ts +10 -0
  380. package/src/postgres/testing/roundtrip-schema.ts +24 -1
  381. package/src/postgres.ts +33 -0
  382. package/src/serializer.ts +13 -2
  383. package/src/skills/chant-sql-postgres-migration.md +4 -3
  384. package/src/skills/chant-sql-postgres.md +11 -1
  385. package/src/skills/chant-sql.md +2 -1
  386. package/src/sql-files.test.ts +33 -0
  387. package/src/sql-types-command.ts +38 -16
  388. package/src/sql-types.ts +11 -6
  389. package/src/sqlserver/fixture.test.ts +186 -0
  390. package/src/sqlserver/import-cli.test.ts +61 -0
  391. package/src/sqlserver/lexer.ts +260 -0
  392. package/src/sqlserver/parse.ts +865 -0
  393. package/src/sqlserver/translate.test.ts +396 -0
  394. package/src/sqlserver/translate.ts +558 -0
  395. package/src/sqlserver/types.test.ts +102 -0
  396. package/src/sqlserver/types.ts +154 -0
  397. package/src/sqlserver/validate.e2e.test.ts +32 -0
  398. package/src/sqlserver/validate.test.ts +49 -0
  399. package/src/sqlserver/validate.ts +76 -0
  400. package/src/validate.ts +10 -0
  401. package/src/workspace-kinds.test.ts +11 -0
  402. package/workspace-kinds.json +100 -2
package/src/sql-types.ts CHANGED
@@ -133,7 +133,7 @@ function originOf(entity: Declarable, exportName: string, projectDir: string): s
133
133
  }
134
134
 
135
135
  /** The names a relation's `.columns` record holds at run time, in order. */
136
- const runtimeColumns = (entity: Declarable | undefined): string[] => Object.keys((entity as { columns?: object } | undefined)?.columns ?? {});
136
+ export const runtimeColumns = (entity: Declarable | undefined): string[] => Object.keys((entity as { columns?: object } | undefined)?.columns ?? {});
137
137
 
138
138
  /** Whether `expr` is the column `column` alone, bare or qualified (`o.user_id`). */
139
139
  function isBareColumn(expr: string, quoted: string): boolean {
@@ -148,7 +148,7 @@ interface LineageJson {
148
148
  }
149
149
 
150
150
  /** A view's column that passes one column through: that column's export and name. */
151
- function passthrough(o: OutputObject, name: string, quote: (c: string) => string): { export: string; column: string } | undefined {
151
+ export function passthrough(o: OutputObject, name: string, quote: (c: string) => string): { export: string; column: string } | undefined {
152
152
  const edge = ((o.lineage as LineageJson[] | undefined) ?? []).find((e) => e.output === name);
153
153
  const ref = edge?.from.length === 1 ? edge.from[0] : undefined;
154
154
  if (!edge || !ref) return undefined;
@@ -177,7 +177,7 @@ function spell(canonical: string, t: ParsedType): string {
177
177
  const arrayOf = (ts: string): string => `${/[|&]/.test(ts) && !/^\{.*\}$/s.test(ts) ? `(${ts})` : ts}[]`;
178
178
 
179
179
  /** A Postgres type's spelling, row type, and whether the type itself forbids NULL (a serial, a NOT NULL domain). */
180
- function pgType(p: PgProject, text: string | undefined, depth = 0): { sqlType: string; ts: string; notNull: boolean } {
180
+ export function pgType(p: PgProject, text: string | undefined, depth = 0): { sqlType: string; ts: string; notNull: boolean } {
181
181
  const t = parseType(text);
182
182
  if (!t || depth > 5) return { sqlType: text?.trim() || "unknown", ts: "unknown", notNull: false };
183
183
  const r = resolveType(p, t, { serial: true });
@@ -320,7 +320,7 @@ function renderEntry(e: RelationEntry, lines: string[]): void {
320
320
  }
321
321
 
322
322
  /** The dialect a build's entities declare, else the config's, else ClickHouse. */
323
- function dialectOf(entities: ReadonlyMap<string, Declarable>, config: Record<string, unknown>): SqlDialect {
323
+ export function dialectOf(entities: ReadonlyMap<string, Declarable>, config: Record<string, unknown>): SqlDialect {
324
324
  try {
325
325
  return filesDialect(entities, config);
326
326
  } catch {
@@ -328,6 +328,12 @@ function dialectOf(entities: ReadonlyMap<string, Declarable>, config: Record<str
328
328
  }
329
329
  }
330
330
 
331
+ /** The build's sql output as `chant build` writes it: the objects, and the Postgres major they were built for. */
332
+ export function serializedObjects(entities: ReadonlyMap<string, Declarable>, config: Record<string, unknown>): { objects: OutputObject[]; postgresMajor?: number } {
333
+ const out = sqlSerializer.serialize(new Map(entities), undefined, { config }) as SerializerResult | string;
334
+ return JSON.parse(typeof out === "string" ? out : out.primary) as { objects: OutputObject[]; postgresMajor?: number };
335
+ }
336
+
331
337
  /**
332
338
  * The `sql-types.d.ts` for a build's entities: one module that augments the
333
339
  * dialect's `SqlSchema` with each table, view and dictionary, in key order.
@@ -340,8 +346,7 @@ export function renderSqlTypes(entities: ReadonlyMap<string, Declarable>, opts:
340
346
  const sqlEntities = new Map([...entities].filter(([, e]) => isPostgresObject(e) || isClickHouseObject(e)));
341
347
  let entries: RelationEntry[] = [];
342
348
  if (sqlEntities.size > 0) {
343
- const out = sqlSerializer.serialize(new Map(entities), undefined, { config }) as SerializerResult | string;
344
- const doc = JSON.parse(typeof out === "string" ? out : out.primary) as { objects: OutputObject[]; postgresMajor?: number };
349
+ const doc = serializedObjects(entities, config);
345
350
  entries = dialect === "postgres" ? postgresEntries(doc.objects, doc.postgresMajor ?? PINNED_MAJOR, entities, projectDir) : clickhouseEntries(doc.objects, entities, projectDir);
346
351
  }
347
352
  const byKey = new Map<string, RelationEntry>();
@@ -0,0 +1,186 @@
1
+ /**
2
+ * The first two Done-when boxes of #4398, on a public sample schema: an
3
+ * excerpt of Microsoft's WideWorldImporters (MIT, vendored in
4
+ * `test-fixtures/sqlserver/` with its licence). The schema imports, every
5
+ * non-mechanical decision is a SARIF diagnostic, and the output builds and
6
+ * passes the sql lexicon's lint rules and post-synth checks.
7
+ */
8
+
9
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { afterAll, describe, expect, test } from "vitest";
12
+ import { build } from "@intentius/chant/build";
13
+ import { runLint } from "@intentius/chant/lint/engine";
14
+ import { runPostSynthChecks } from "@intentius/chant/lint/post-synth";
15
+ import { importExitCode, importSarif } from "@intentius/chant/cli/commands/import-report";
16
+ import type { SerializerResult } from "@intentius/chant/serializer";
17
+ import { sqlPlugin } from "../plugin";
18
+ import { sqlTemplateGenerator } from "../import-generator";
19
+ import { postSynthChecks } from "../lint/post-synth";
20
+ import { CLICKHOUSE_DDL_FILE, sqlSerializer } from "../serializer";
21
+ import { splitStatements } from "../clickhouse/import/ir";
22
+ import { REVIEW_RULES } from "../clickhouse/review-catalog";
23
+
24
+ const fixtures = join(import.meta.dirname, "..", "..", "test-fixtures", "sqlserver");
25
+ const source = readFileSync(join(fixtures, "wwi-excerpt.sql"), "utf8");
26
+ const parse = (text: string, options: Record<string, string | boolean> = { from: "sqlserver" }) => sqlPlugin.templateParser!(options).parse(text);
27
+ const ir = parse(source);
28
+ const diagnostics = ir.diagnostics ?? [];
29
+ const ddlOf = (name: string) => (ir.resources.find((r) => (r.properties as { name: string }).name === name)!.properties as { ddl: string }).ddl;
30
+
31
+ describe("the vendored sample", () => {
32
+ test("carries its licence, source path and commit", () => {
33
+ const licence = readFileSync(join(fixtures, "LICENSE-sql-server-samples.txt"), "utf8");
34
+ expect(licence).toContain("MIT License");
35
+ expect(licence).toContain("Copyright (c) Microsoft Corporation");
36
+ expect(source).toContain("Licence: MIT. Copyright (c) Microsoft Corporation");
37
+ expect(source).toContain("samples/databases/wide-world-importers/wwi-ssdt/wwi-ssdt/");
38
+ expect(source).toMatch(/Commit:\s+[0-9a-f]{40}/);
39
+ });
40
+ });
41
+
42
+ describe("importing it", () => {
43
+ test("every table, view and schema becomes one object, databases first", () => {
44
+ expect(ir.resources.map((r) => `${r.type.replace("ClickHouse::", "")} ${(r.properties as { name: string }).name}`)).toEqual([
45
+ "Database Warehouse",
46
+ "Database Sales",
47
+ "Database WebApi",
48
+ "Table StockItemTransactions",
49
+ "Table CustomerTransactions",
50
+ "Table Orders",
51
+ "Table Colors",
52
+ "Table ColdRoomTemperatures",
53
+ "View Colors",
54
+ ]);
55
+ expect(ir.warnings ?? []).toEqual([]);
56
+ });
57
+
58
+ test("the columnstore table, the clustered-index table and the primary-key tables get the sort key the catalog picks", () => {
59
+ expect(ddlOf("StockItemTransactions")).toContain("ORDER BY StockItemTransactionID");
60
+ expect(ddlOf("CustomerTransactions")).toContain("ORDER BY TransactionDate");
61
+ expect(ddlOf("Orders")).toContain("ORDER BY OrderID");
62
+ });
63
+
64
+ test("the generated declarations name their dependencies", () => {
65
+ const [file] = sqlTemplateGenerator.generate(ir);
66
+ expect(file!.content).toContain("CREATE TABLE ${warehouseDb}.StockItemTransactions");
67
+ expect(file!.content).toContain("FROM ${warehouseColors}");
68
+ });
69
+ });
70
+
71
+ describe("every non-mechanical decision is a diagnostic", () => {
72
+ const at = (rule: string) => diagnostics.filter((d) => d.ruleId === rule);
73
+ const columnsOf = (ddl: string) => [...ddl.matchAll(/^ {4}(\w+) (.+?),?$/gm)].map((m) => ({ name: m[1]!, rest: m[2]! }));
74
+
75
+ test("each Nullable column in the DDL has a SQLRV003 diagnostic naming the table and column", () => {
76
+ const wanted: string[] = [];
77
+ for (const r of ir.resources) {
78
+ const p = r.properties as { name: string; database?: string; ddl: string };
79
+ if (r.type !== "ClickHouse::Table") continue;
80
+ for (const c of columnsOf(p.ddl)) if (c.rest.startsWith("Nullable(")) wanted.push(`${p.database ?? ""}.${p.name}.${c.name}`.replace(/^\./, ""));
81
+ }
82
+ expect(wanted.length).toBeGreaterThan(10);
83
+ expect(at("SQLRV003").map((d) => d.message.split(":")[0])).toEqual(expect.arrayContaining(wanted));
84
+ expect(at("SQLRV003")).toHaveLength(wanted.length);
85
+ });
86
+
87
+ test("each table has exactly one sort key diagnostic and one engine diagnostic", () => {
88
+ expect(at("SQLRV001").map((d) => d.entity).sort()).toEqual(["Sales.CustomerTransactions", "Sales.Orders", "Warehouse.ColdRoomTemperatures", "Warehouse.Colors", "Warehouse.StockItemTransactions"]);
89
+ expect(at("SQLRV002")).toHaveLength(5);
90
+ });
91
+
92
+ test("each foreign key and unique constraint in the source is a comment in the DDL and a SQLRV005 diagnostic", () => {
93
+ const sourceCount = [...source.matchAll(/FOREIGN KEY|\bUNIQUE NONCLUSTERED/g)].length;
94
+ const commentCount = ir.resources.reduce((n, r) => n + [...((r.properties as { ddl: string }).ddl.matchAll(/^-- (?:FOREIGN KEY|UNIQUE)/gm))].length, 0);
95
+ expect(sourceCount).toBeGreaterThan(15);
96
+ expect(commentCount).toBe(sourceCount);
97
+ expect(at("SQLRV005")).toHaveLength(sourceCount);
98
+ });
99
+
100
+ test("the identity column, the sequence defaults, the computed column and the period columns are SQLRV006", () => {
101
+ const messages = at("SQLRV006").map((d) => d.message);
102
+ expect(messages.filter((m) => m.includes("draws from a sequence"))).toHaveLength([...source.matchAll(/NEXT VALUE FOR/g)].length);
103
+ expect(messages.some((m) => m.includes("Identity column ColdRoomTemperatureID"))).toBe(true);
104
+ expect(messages.some((m) => m.includes("Computed column IsFinalized"))).toBe(true);
105
+ expect(messages.filter((m) => m.includes("filled by the system"))).toHaveLength(4);
106
+ });
107
+
108
+ test("the sysdatetime() defaults, the view and the indexes that are not carried are diagnostics", () => {
109
+ expect(at("SQLRV008")).toHaveLength([...source.matchAll(/DEFAULT \(sysdatetime\(\)\)/g)].length);
110
+ expect(at("SQLRV010")).toHaveLength(1);
111
+ const notCarried = at("SQLRV009").filter((d) => d.message.includes("is not carried") && d.message.includes("index"));
112
+ expect(notCarried).toHaveLength([...source.matchAll(/CREATE NONCLUSTERED INDEX/g)].length + 1);
113
+ });
114
+
115
+ test("the diagnostics use only rules in the catalog, each with a line in the source", () => {
116
+ for (const d of diagnostics) {
117
+ expect(Object.keys(REVIEW_RULES)).toContain(d.ruleId);
118
+ expect(d.line, d.message).toBeGreaterThan(0);
119
+ }
120
+ });
121
+
122
+ test("the SARIF report has one result per diagnostic with its rule, level and region", () => {
123
+ const sarif = JSON.parse(importSarif("wwi-excerpt.sql", diagnostics)) as {
124
+ version: string;
125
+ runs: Array<{ results: Array<{ ruleId: string; level: string; message: { text: string }; locations: Array<{ physicalLocation: { artifactLocation: { uri: string }; region: { startLine: number } } }> }> }>;
126
+ };
127
+ expect(sarif.version).toBe("2.1.0");
128
+ const results = sarif.runs[0]!.results;
129
+ expect(results).toHaveLength(diagnostics.length);
130
+ expect(new Set(results.map((r) => r.level))).toEqual(new Set(["warning", "note"]));
131
+ expect(results[0]!.locations[0]!.physicalLocation.artifactLocation.uri).toContain("wwi-excerpt.sql");
132
+ expect(results.find((r) => r.ruleId === "SQLRV001")!.message.text).toContain("Evidence:");
133
+ expect(results.every((r) => r.locations[0]!.physicalLocation.region.startLine > 0)).toBe(true);
134
+ });
135
+ });
136
+
137
+ describe("--strict", () => {
138
+ test("exits non-zero while a diagnostic to review is open, and zero without --strict", () => {
139
+ expect(importExitCode(diagnostics, true)).toBe(1);
140
+ expect(importExitCode(diagnostics, false)).toBe(0);
141
+ });
142
+
143
+ test("a schema whose only diagnostics are notes passes --strict", () => {
144
+ const notes = diagnostics.filter((d) => d.severity === "info");
145
+ expect(notes.length).toBeGreaterThan(0);
146
+ expect(importExitCode(notes, true)).toBe(0);
147
+ });
148
+ });
149
+
150
+ describe("refusing the sample's function", () => {
151
+ const refused = readFileSync(join(fixtures, "wwi-refused.sql"), "utf8");
152
+
153
+ test("the import fails and names the function and its line", () => {
154
+ expect(() => parse(refused)).toThrow(/function Application\.DetermineCustomerAccess \(line \d+\)/);
155
+ });
156
+
157
+ test("with skipUndeclarable it imports the rest and warns by name", () => {
158
+ const withTable = `${source}\nGO\n${refused}`;
159
+ const r = parse(withTable, { from: "sqlserver", skipUndeclarable: true });
160
+ expect(r.resources).toHaveLength(ir.resources.length);
161
+ expect(r.diagnostics!.some((d) => d.severity === "warning" && d.message.includes("function Application.DetermineCustomerAccess"))).toBe(true);
162
+ });
163
+ });
164
+
165
+ // The output builds and lints.
166
+ const dir = join(import.meta.dirname, "..", "..", `.sqlserver-import-tmp-${process.pid}`);
167
+ afterAll(() => rmSync(dir, { recursive: true, force: true }));
168
+
169
+ describe("the output builds and lints clean", () => {
170
+ test("the declarations build to one statement per object and pass the lexicon's lint and checks", async () => {
171
+ const [file] = sqlTemplateGenerator.generate(ir);
172
+ mkdirSync(join(dir, "src"), { recursive: true });
173
+ writeFileSync(join(dir, "chant.config.ts"), 'export default { lexicons: ["sql"] };\n');
174
+ writeFileSync(join(dir, "src", "schema.ts"), file!.content);
175
+
176
+ const result = await build(join(dir, "src"), [sqlSerializer], undefined, { fold: true, intrinsics: sqlPlugin.intrinsics!(), lexicons: ["sql"] });
177
+ expect(result.errors).toEqual([]);
178
+ const statements = splitStatements((result.outputs.get("sql") as SerializerResult).files![CLICKHOUSE_DDL_FILE]!);
179
+ expect(statements).toHaveLength(ir.resources.length);
180
+ expect(statements.find((s) => s.includes("Warehouse.StockItemTransactions"))).toContain("ENGINE = MergeTree");
181
+
182
+ expect(runPostSynthChecks(postSynthChecks, result)).toEqual([]);
183
+ const lint = await runLint([join(dir, "src", "schema.ts")], sqlPlugin.lintRules!(), undefined, sqlPlugin.intrinsics!());
184
+ expect(lint.diagnostics.filter((d) => d.severity === "error" || d.severity === "warning")).toEqual([]);
185
+ });
186
+ });
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `chant import` end to end in-process, for the third Done-when box of #4398:
3
+ * `--strict` exits non-zero while a diagnostic is open, `--report` writes the
4
+ * SARIF, and without `--strict` the same import exits 0.
5
+ */
6
+
7
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
11
+ import { parseArgs } from "@intentius/chant/cli/parse-args";
12
+ import { runImport } from "@intentius/chant/cli/handlers/misc";
13
+
14
+ const dir = mkdtempSync(join(tmpdir(), "sqlserver-import-cli-"));
15
+ const input = join(dir, "schema.sql");
16
+ afterAll(() => rmSync(dir, { recursive: true, force: true }));
17
+ beforeAll(() => writeFileSync(input, "CREATE TABLE [dbo].[T] ([Id] INT NOT NULL, [Note] NVARCHAR(40) NULL, CONSTRAINT [PK_T] PRIMARY KEY CLUSTERED ([Id]));\nGO\n"));
18
+
19
+ async function run(extra: string[], name: string) {
20
+ const out = join(dir, name);
21
+ const args = parseArgs(["import", input, "--lexicon", "sql", "--parser-option", "from=sqlserver", "--output", out, ...extra]);
22
+ const spy = vi.spyOn(console, "error").mockImplementation(() => {});
23
+ const log = vi.spyOn(console, "log").mockImplementation(() => {});
24
+ try {
25
+ const code = await runImport({ args, plugins: [], serializers: [] });
26
+ return { code, out, stderr: spy.mock.calls.map((c) => String(c[0])).join("\n") };
27
+ } finally {
28
+ spy.mockRestore();
29
+ log.mockRestore();
30
+ }
31
+ }
32
+
33
+ describe("chant import --parser-option from=sqlserver", () => {
34
+ test("exits 0 without --strict, writes the declarations and prints the review summary", async () => {
35
+ const r = await run([], "plain");
36
+ expect(r.code).toBe(0);
37
+ expect(existsSync(join(r.out, "schema.ts"))).toBe(true);
38
+ expect(r.stderr).toContain("Review: 3 to review");
39
+ expect(r.stderr).toContain("SQLRV001 x1");
40
+ });
41
+
42
+ test("--strict exits 1 while a diagnostic is open", async () => {
43
+ expect((await run(["--strict"], "strict")).code).toBe(1);
44
+ });
45
+
46
+ test("--report writes SARIF with the three diagnostics, with or without --strict", async () => {
47
+ const report = join(dir, "review.sarif");
48
+ const r = await run(["--report", report], "report");
49
+ expect(r.code).toBe(0);
50
+ const sarif = JSON.parse(readFileSync(report, "utf8")) as { runs: Array<{ results: Array<{ ruleId: string; locations: Array<{ physicalLocation: { artifactLocation: { uri: string } } }> }> }> };
51
+ expect(sarif.runs[0]!.results.map((x) => x.ruleId).sort()).toEqual(["SQLRV001", "SQLRV002", "SQLRV003"]);
52
+ expect(sarif.runs[0]!.results[0]!.locations[0]!.physicalLocation.artifactLocation.uri).toContain("schema.sql");
53
+ });
54
+
55
+ test("a refused statement fails the import and names it", async () => {
56
+ writeFileSync(input, "CREATE TABLE T (Id int NOT NULL);\nGO\nCREATE TRIGGER trg_t ON T AFTER INSERT AS SELECT 1\n");
57
+ const r = await run([], "refused");
58
+ expect(r.code).toBe(1);
59
+ expect(r.stderr).toContain("trigger trg_t (line 3)");
60
+ });
61
+ });
@@ -0,0 +1,260 @@
1
+ /**
2
+ * A T-SQL tokenizer for the statements `chant import` reads from SQL Server.
3
+ *
4
+ * T-SQL differs from the lexers the other dialects use in four places: an
5
+ * identifier can be `[bracketed]` (a `]` inside is written `]]`) or
6
+ * "double quoted", a string can carry an `N` prefix, a line holding only `GO`
7
+ * ends a batch (it is a client command, not a statement), and statements need
8
+ * no terminating `;`. Comments (`--` and nestable `/* *\/`) are dropped; each
9
+ * token keeps its 1-based line and column in the source.
10
+ */
11
+
12
+ export type TokenKind = "word" | "qident" | "string" | "number" | "variable" | "punct" | "go";
13
+
14
+ export interface Token {
15
+ kind: TokenKind;
16
+ /** As written: a `qident` keeps its brackets; a string its quotes. */
17
+ text: string;
18
+ /** A `qident`'s name, a string's value, a word's text. */
19
+ value: string;
20
+ line: number;
21
+ column: number;
22
+ /** Offset of the first character. */
23
+ start: number;
24
+ /** Offset after the last. */
25
+ end: number;
26
+ }
27
+
28
+ export class TsqlLexError extends Error {
29
+ constructor(
30
+ message: string,
31
+ readonly line: number,
32
+ readonly column: number,
33
+ ) {
34
+ super(`${message} (line ${String(line)}, column ${String(column)})`);
35
+ this.name = "TsqlLexError";
36
+ }
37
+ }
38
+
39
+ const WORD_START = /[A-Za-z_#@\u0080-￿]/;
40
+ const WORD_PART = /[A-Za-z0-9_#$@\u0080-￿]/;
41
+
42
+ /** Tokenize a file. A `GO` on a line of its own (optionally with a count) becomes one `go` token. */
43
+ export function tokenize(text: string): Token[] {
44
+ const tokens: Token[] = [];
45
+ let i = 0;
46
+ let line = 1;
47
+ let lineStart = 0;
48
+ const n = text.length;
49
+ // A byte order mark starts files saved by SSMS and Visual Studio.
50
+ if (text.charCodeAt(0) === 0xfeff) {
51
+ i = 1;
52
+ lineStart = 1;
53
+ }
54
+ const push = (kind: TokenKind, start: number, end: number, value: string, startLine: number, startCol: number) =>
55
+ tokens.push({ kind, text: text.slice(start, end), value, line: startLine, column: startCol, start, end });
56
+
57
+ while (i < n) {
58
+ const c = text[i]!;
59
+ if (c === "\n") {
60
+ line++;
61
+ i++;
62
+ lineStart = i;
63
+ continue;
64
+ }
65
+ if (/\s/.test(c)) {
66
+ i++;
67
+ continue;
68
+ }
69
+ const col = i - lineStart + 1;
70
+ if (c === "-" && text[i + 1] === "-") {
71
+ while (i < n && text[i] !== "\n") i++;
72
+ continue;
73
+ }
74
+ if (c === "/" && text[i + 1] === "*") {
75
+ let depth = 1;
76
+ const startLine = line;
77
+ const startCol = col;
78
+ i += 2;
79
+ while (i < n && depth > 0) {
80
+ if (text[i] === "/" && text[i + 1] === "*") {
81
+ depth++;
82
+ i += 2;
83
+ } else if (text[i] === "*" && text[i + 1] === "/") {
84
+ depth--;
85
+ i += 2;
86
+ } else {
87
+ if (text[i] === "\n") {
88
+ line++;
89
+ lineStart = i + 1;
90
+ }
91
+ i++;
92
+ }
93
+ }
94
+ if (depth > 0) throw new TsqlLexError("unterminated /* comment", startLine, startCol);
95
+ continue;
96
+ }
97
+ if (c === "[") {
98
+ const start = i;
99
+ let value = "";
100
+ i++;
101
+ for (;;) {
102
+ if (i >= n) throw new TsqlLexError("unterminated [identifier]", line, col);
103
+ if (text[i] === "]") {
104
+ if (text[i + 1] === "]") {
105
+ value += "]";
106
+ i += 2;
107
+ continue;
108
+ }
109
+ i++;
110
+ break;
111
+ }
112
+ if (text[i] === "\n") {
113
+ line++;
114
+ lineStart = i + 1;
115
+ }
116
+ value += text[i];
117
+ i++;
118
+ }
119
+ push("qident", start, i, value, line, col);
120
+ continue;
121
+ }
122
+ if (c === '"') {
123
+ const start = i;
124
+ let value = "";
125
+ i++;
126
+ for (;;) {
127
+ if (i >= n) throw new TsqlLexError('unterminated "identifier"', line, col);
128
+ if (text[i] === '"') {
129
+ if (text[i + 1] === '"') {
130
+ value += '"';
131
+ i += 2;
132
+ continue;
133
+ }
134
+ i++;
135
+ break;
136
+ }
137
+ value += text[i];
138
+ i++;
139
+ }
140
+ push("qident", start, i, value, line, col);
141
+ continue;
142
+ }
143
+ if (c === "'" || ((c === "N" || c === "n") && text[i + 1] === "'")) {
144
+ const start = i;
145
+ const startLine = line;
146
+ if (c !== "'") i++;
147
+ i++;
148
+ let value = "";
149
+ for (;;) {
150
+ if (i >= n) throw new TsqlLexError("unterminated string", startLine, col);
151
+ if (text[i] === "'") {
152
+ if (text[i + 1] === "'") {
153
+ value += "'";
154
+ i += 2;
155
+ continue;
156
+ }
157
+ i++;
158
+ break;
159
+ }
160
+ if (text[i] === "\n") {
161
+ line++;
162
+ lineStart = i + 1;
163
+ }
164
+ value += text[i];
165
+ i++;
166
+ }
167
+ push("string", start, i, value, startLine, col);
168
+ continue;
169
+ }
170
+ if (/[0-9]/.test(c) || (c === "." && /[0-9]/.test(text[i + 1] ?? ""))) {
171
+ const start = i;
172
+ if (c === "0" && (text[i + 1] === "x" || text[i + 1] === "X")) {
173
+ i += 2;
174
+ while (i < n && /[0-9A-Fa-f]/.test(text[i]!)) i++;
175
+ } else {
176
+ while (i < n && /[0-9]/.test(text[i]!)) i++;
177
+ if (text[i] === "." ) {
178
+ i++;
179
+ while (i < n && /[0-9]/.test(text[i]!)) i++;
180
+ }
181
+ if ((text[i] === "e" || text[i] === "E") && /[0-9+-]/.test(text[i + 1] ?? "")) {
182
+ i += 2;
183
+ while (i < n && /[0-9]/.test(text[i]!)) i++;
184
+ }
185
+ }
186
+ push("number", start, i, text.slice(start, i), line, col);
187
+ continue;
188
+ }
189
+ if (c === "@" && text[i + 1] !== undefined && WORD_PART.test(text[i + 1]!)) {
190
+ const start = i;
191
+ i++;
192
+ while (i < n && WORD_PART.test(text[i]!)) i++;
193
+ push("variable", start, i, text.slice(start, i), line, col);
194
+ continue;
195
+ }
196
+ if (WORD_START.test(c)) {
197
+ const start = i;
198
+ i++;
199
+ while (i < n && WORD_PART.test(text[i]!)) i++;
200
+ const word = text.slice(start, i);
201
+ push("word", start, i, word, line, col);
202
+ continue;
203
+ }
204
+ // Two-character operators are one token so a `<>` is not a `<` and a `>`.
205
+ const two = text.slice(i, i + 2);
206
+ if (["<>", ">=", "<=", "!=", "+=", "-=", "*=", "/=", "||", "::"].includes(two)) {
207
+ push("punct", i, i + 2, two, line, col);
208
+ i += 2;
209
+ continue;
210
+ }
211
+ push("punct", i, i + 1, c, line, col);
212
+ i++;
213
+ }
214
+ return markBatchSeparators(tokens, text);
215
+ }
216
+
217
+ /** `GO` alone on its line (or `GO 5`) is a separator, not a word. */
218
+ function markBatchSeparators(tokens: Token[], text: string): Token[] {
219
+ const out: Token[] = [];
220
+ for (let k = 0; k < tokens.length; k++) {
221
+ const t = tokens[k]!;
222
+ if (t.kind === "word" && t.value.toUpperCase() === "GO") {
223
+ const prev = tokens[k - 1];
224
+ const next = tokens[k + 1];
225
+ const alone = (a: Token | undefined) => a === undefined || a.line !== t.line;
226
+ const count = next && next.kind === "number" && next.line === t.line ? next : undefined;
227
+ const after = count ? tokens[k + 2] : next;
228
+ // The line before `GO` ends the earlier statement, and a `GO` mid-line is a name.
229
+ if (alone(prev) && alone(after) && !/[A-Za-z0-9_]/.test(text.slice(t.end, t.end + 1))) {
230
+ out.push({ ...t, kind: "go" });
231
+ if (count) k++;
232
+ continue;
233
+ }
234
+ }
235
+ out.push(t);
236
+ }
237
+ return out;
238
+ }
239
+
240
+ /** Tokens split into batches at `go` tokens; empty batches are dropped. */
241
+ export function batches(tokens: readonly Token[]): Token[][] {
242
+ const out: Token[][] = [];
243
+ let cur: Token[] = [];
244
+ for (const t of tokens) {
245
+ if (t.kind === "go") {
246
+ if (cur.length > 0) out.push(cur);
247
+ cur = [];
248
+ } else cur.push(t);
249
+ }
250
+ if (cur.length > 0) out.push(cur);
251
+ return out;
252
+ }
253
+
254
+ export const upper = (t: Token | undefined): string => (t && t.kind === "word" ? t.value.toUpperCase() : "");
255
+
256
+ /** The source text of a run of tokens, from the first token's start to the last token's end. */
257
+ export function sourceOf(text: string, tokens: readonly Token[]): string {
258
+ if (tokens.length === 0) return "";
259
+ return text.slice(tokens[0]!.start, tokens[tokens.length - 1]!.end);
260
+ }