@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
@@ -0,0 +1,396 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { translateSqlServer } from "./translate";
3
+ import { batches, tokenize } from "./lexer";
4
+ import { parseTsql } from "./parse";
5
+
6
+ const t = (sql: string, options = {}) => translateSqlServer(sql, options);
7
+ const table = (sql: string) => {
8
+ const r = t(sql);
9
+ expect(r.problems).toEqual([]);
10
+ return r.objects.find((o) => o.type === "ClickHouse::Table")!.ddl;
11
+ };
12
+ const rules = (sql: string) => [...new Set(t(sql).diagnostics.map((d) => d.ruleId))].sort();
13
+ const of = (sql: string, ruleId: string) => t(sql).diagnostics.filter((d) => d.ruleId === ruleId);
14
+
15
+ describe("the lexer", () => {
16
+ test("a [bracketed] name keeps a closing bracket written ]] and an N'string' is its value", () => {
17
+ const toks = tokenize("CREATE TABLE [a]]b] (c nvarchar(5) DEFAULT N'it''s')");
18
+ expect(toks.find((x) => x.kind === "qident")!.value).toBe("a]b");
19
+ expect(toks.find((x) => x.kind === "string")!.value).toBe("it's");
20
+ });
21
+
22
+ test("GO alone on a line ends a batch, and a name that is GO inside a statement does not", () => {
23
+ const toks = tokenize("SELECT 1\nGO\nSELECT 2 AS go\nGO 3\n");
24
+ expect(batches(toks).map((b) => b.map((x) => x.value).join(" "))).toEqual(["SELECT 1", "SELECT 2 AS go"]);
25
+ });
26
+
27
+ test("comments are dropped and positions are 1-based", () => {
28
+ const toks = tokenize("-- a\n/* b /* nested */ c */ CREATE");
29
+ expect(toks).toHaveLength(1);
30
+ expect(toks[0]).toMatchObject({ value: "CREATE", line: 2, column: 24 });
31
+ });
32
+
33
+ test("an unterminated bracket is an error with a position", () => {
34
+ expect(() => tokenize("CREATE TABLE [x")).toThrow(/line 1/);
35
+ });
36
+ });
37
+
38
+ describe("the parser", () => {
39
+ test("statements need no semicolon: a CREATE TABLE and a CREATE INDEX in one batch are two", () => {
40
+ const r = parseTsql("CREATE TABLE a (x int) CREATE INDEX ix ON a (x)");
41
+ expect(r.statements.map((s) => s.kind)).toEqual(["table", "index"]);
42
+ });
43
+
44
+ test("ON DELETE SET NULL does not start a SET statement", () => {
45
+ const r = parseTsql("CREATE TABLE a (x int, y int REFERENCES b (id) ON DELETE SET NULL ON UPDATE CASCADE);\nALTER TABLE a ADD CONSTRAINT fk FOREIGN KEY (x) REFERENCES b (id) ON DELETE SET NULL");
46
+ expect(r.unread).toEqual([]);
47
+ expect(r.statements.map((s) => s.kind)).toEqual(["table", "alter-constraint"]);
48
+ });
49
+
50
+ test("a statement it cannot read is returned with its words and the reason, not dropped", () => {
51
+ const r = parseTsql("CREATE TABLE a (x int FROBNICATE)");
52
+ expect(r.statements).toEqual([]);
53
+ expect(r.unread[0]).toMatchObject({ text: expect.stringContaining("CREATE TABLE a"), reason: expect.stringContaining("FROBNICATE") });
54
+ });
55
+ });
56
+
57
+ describe("a mechanical table", () => {
58
+ const sql = `CREATE TABLE dbo.m (
59
+ a int NOT NULL, b bigint NOT NULL, c nvarchar(50) NOT NULL, d datetime2(3) NOT NULL, e bit NOT NULL,
60
+ f uniqueidentifier NOT NULL, g decimal(12,4) NOT NULL, h money NOT NULL,
61
+ CONSTRAINT pk PRIMARY KEY CLUSTERED (a));`;
62
+
63
+ test("maps every type with no finding about a type", () => {
64
+ const ddl = table(sql);
65
+ for (const line of ["a Int32", "b Int64", "c String", "d DateTime64(3)", "e Bool", "f UUID", "g Decimal(12, 4)", "h Decimal(19, 4)"]) expect(ddl).toContain(` ${line}`);
66
+ expect(ddl).not.toContain("Nullable");
67
+ });
68
+
69
+ test("the only findings are the two every table gets: the sort key and the engine", () => {
70
+ expect(rules(sql)).toEqual(["SQLRV001", "SQLRV002"]);
71
+ });
72
+
73
+ test("the dbo schema is dropped from the name, and the table has no database", () => {
74
+ const r = t(sql);
75
+ expect(r.objects.map((o) => [o.type, o.database, o.name])).toEqual([["ClickHouse::Table", undefined, "m"]]);
76
+ });
77
+ });
78
+
79
+ describe("the sort key", () => {
80
+ const cols = "a int NOT NULL, b int NOT NULL, c datetime2(3) NOT NULL";
81
+
82
+ test("a columnstore ORDER is the key, in either clause order, and the evidence names the index", () => {
83
+ for (const tail of ["ORDER (c, a)", "WITH (MAXDOP = 1) ORDER (c, a)", "ORDER (c, a) WITH (DROP_EXISTING = ON)"]) {
84
+ const sql = `CREATE TABLE f (${cols}, PRIMARY KEY NONCLUSTERED (a));\nGO\nCREATE CLUSTERED COLUMNSTORE INDEX cci ON f ${tail}`;
85
+ expect(table(sql)).toContain("ORDER BY (c, a)");
86
+ const d = of(sql, "SQLRV001")[0]!;
87
+ expect(d.message).toContain("CREATE CLUSTERED COLUMNSTORE INDEX cci ORDER (c, a)");
88
+ expect(d.message).toContain("passed over: PRIMARY KEY NONCLUSTERED");
89
+ }
90
+ });
91
+
92
+ test("a columnstore with no ORDER names no key, so the primary key is used and the evidence says so", () => {
93
+ const sql = `CREATE TABLE f (${cols}, PRIMARY KEY NONCLUSTERED (b));\nGO\nCREATE CLUSTERED COLUMNSTORE INDEX cci ON f`;
94
+ expect(table(sql)).toContain("ORDER BY b");
95
+ expect(of(sql, "SQLRV001")[0]!.message).toContain("cci has no ORDER clause");
96
+ });
97
+
98
+ test("a clustered index outranks a nonclustered primary key", () => {
99
+ const sql = `CREATE TABLE f (${cols}, PRIMARY KEY NONCLUSTERED (a));\nGO\nCREATE CLUSTERED INDEX cx ON f (c DESC, b)`;
100
+ expect(table(sql)).toContain("ORDER BY (c, b)");
101
+ expect(of(sql, "SQLRV001")[0]!.message).toContain("descending in the source, ascending here: c");
102
+ });
103
+
104
+ test("a primary key with no keyword is the clustering key unless another structure is clustered", () => {
105
+ expect(of(`CREATE TABLE f (${cols}, PRIMARY KEY (a))`, "SQLRV001")[0]!.message).toContain("clustering key");
106
+ expect(of(`CREATE TABLE f (${cols}, PRIMARY KEY (a));\nGO\nCREATE CLUSTERED COLUMNSTORE INDEX c ON f`, "SQLRV001")[0]!.message).toContain("from the primary key");
107
+ });
108
+
109
+ test("a table with no key gets tuple() and a finding", () => {
110
+ const sql = `CREATE TABLE f (${cols})`;
111
+ expect(table(sql)).toContain("ORDER BY tuple()");
112
+ expect(of(sql, "SQLRV001")[0]!.message).toContain("tuple()");
113
+ });
114
+
115
+ test("a nullable key column adds allow_nullable_key", () => {
116
+ const sql = "CREATE TABLE f (a int NULL, b int NOT NULL);\nGO\nCREATE CLUSTERED INDEX cx ON f (a)";
117
+ expect(table(sql)).toContain("SETTINGS allow_nullable_key = 1");
118
+ });
119
+
120
+ test("a key that names a computed column is passed over, and the next one is used", () => {
121
+ const sql = "CREATE TABLE f (a int NOT NULL, b AS (a + 1) PERSISTED, PRIMARY KEY NONCLUSTERED (a));\nGO\nCREATE CLUSTERED INDEX cx ON f (b)";
122
+ expect(table(sql)).toContain("ORDER BY a");
123
+ expect(of(sql, "SQLRV001")[0]!.message).toContain("names a column that is not carried");
124
+ });
125
+ });
126
+
127
+ describe("Nullable and defaults", () => {
128
+ test("a column is nullable unless it is NOT NULL, a key column or an identity", () => {
129
+ const ddl = table("CREATE TABLE f (a int, b int NOT NULL, c int PRIMARY KEY, d int IDENTITY(1,1))");
130
+ expect(ddl).toContain(" a Nullable(Int32)");
131
+ expect(ddl).toContain(" b Int32,");
132
+ expect(ddl).toContain(" c Int32,");
133
+ expect(ddl).toContain(" d Int32\n");
134
+ });
135
+
136
+ test("a nullable column with a default is a finding that names the default", () => {
137
+ const sql = "CREATE TABLE f (n int NULL CONSTRAINT df DEFAULT ((7)))";
138
+ expect(table(sql)).toContain("n Nullable(Int32) DEFAULT 7");
139
+ const d = of(sql, "SQLRV003")[0]!;
140
+ expect(d.message).toContain("it has the default 7");
141
+ expect(d).toMatchObject({ line: 1, entity: "f" });
142
+ });
143
+
144
+ test("literal defaults are carried, strings quoted for ClickHouse", () => {
145
+ const ddl = table("CREATE TABLE f (a int NOT NULL DEFAULT (-1), s nvarchar(5) NOT NULL DEFAULT N'it''s', n int NULL DEFAULT (NULL))");
146
+ expect(ddl).toContain("a Int32 DEFAULT -1");
147
+ expect(ddl).toContain("s String DEFAULT 'it\\'s'");
148
+ expect(ddl).toContain("n Nullable(Int32)\n");
149
+ });
150
+
151
+ test.each([
152
+ ["sysdatetime()", "datetime2(7)", "now64(7)"],
153
+ ["getdate()", "datetime", "now64(3)"],
154
+ ["getdate()", "date", "today()"],
155
+ ["newid()", "uniqueidentifier", "generateUUIDv4()"],
156
+ ])("the function default %s on %s is written as %s and flagged", (fn, type, expected) => {
157
+ const sql = `CREATE TABLE f (c ${type} NOT NULL DEFAULT (${fn}))`;
158
+ expect(table(sql)).toContain(`DEFAULT ${expected}`);
159
+ expect(of(sql, "SQLRV008")).toHaveLength(1);
160
+ });
161
+
162
+ test("a default it cannot translate is dropped and said so", () => {
163
+ const sql = "CREATE TABLE f (c nvarchar(20) NOT NULL DEFAULT (suser_sname()))";
164
+ expect(table(sql)).not.toContain("DEFAULT");
165
+ expect(of(sql, "SQLRV008")[0]!.message).toContain("suser_sname()");
166
+ });
167
+
168
+ test("a default added by ALTER TABLE ... ADD DEFAULT ... FOR is read", () => {
169
+ const sql = "CREATE TABLE f (c int NOT NULL);\nGO\nALTER TABLE f ADD CONSTRAINT df DEFAULT (3) FOR c";
170
+ expect(table(sql)).toContain("c Int32 DEFAULT 3");
171
+ });
172
+ });
173
+
174
+ describe("identity, sequence, computed and generated columns", () => {
175
+ test("identity is a plain column and a finding with the seed and increment", () => {
176
+ const sql = "CREATE TABLE f (id bigint IDENTITY(10, 5) NOT NULL)";
177
+ expect(table(sql)).toContain("id Int64");
178
+ expect(of(sql, "SQLRV006")[0]!.message).toContain("IDENTITY(10, 5)");
179
+ });
180
+
181
+ test("a sequence default is dropped and a finding", () => {
182
+ const sql = "CREATE TABLE f (id int NOT NULL DEFAULT (NEXT VALUE FOR dbo.s))";
183
+ expect(table(sql)).not.toContain("DEFAULT");
184
+ expect(of(sql, "SQLRV006")[0]!.message).toContain("NEXT VALUE FOR dbo.s");
185
+ });
186
+
187
+ test("a computed column is left out of the DDL with a comment and a finding naming its expression", () => {
188
+ const sql = "CREATE TABLE f (a int NOT NULL, b AS (a * 2) PERSISTED)";
189
+ const ddl = table(sql);
190
+ expect(ddl).not.toMatch(/^\s+b /m);
191
+ expect(ddl).toContain("-- computed column b AS (a * 2) is not carried");
192
+ expect(of(sql, "SQLRV006")[0]!.message).toContain("AS (a * 2) PERSISTED");
193
+ });
194
+
195
+ test("a temporal table's period columns, SYSTEM_VERSIONING and PERIOD are findings", () => {
196
+ const sql = `CREATE TABLE f (id int NOT NULL, vf datetime2 GENERATED ALWAYS AS ROW START NOT NULL, vt datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
197
+ PRIMARY KEY CLUSTERED (id), PERIOD FOR SYSTEM_TIME (vf, vt)) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.h))`;
198
+ expect(of(sql, "SQLRV006").map((d) => d.message)).toEqual([expect.stringContaining("vf is filled by the system"), expect.stringContaining("vt is filled by the system")]);
199
+ const options = of(sql, "SQLRV009")[0]!.message;
200
+ expect(options).toContain("SYSTEM_VERSIONING = ON");
201
+ expect(options).toContain("PERIOD FOR SYSTEM_TIME (vf, vt)");
202
+ });
203
+ });
204
+
205
+ describe("constraints ClickHouse does not enforce", () => {
206
+ const sql = `CREATE TABLE o (id int NOT NULL, c int NOT NULL, e nvarchar(9) NULL,
207
+ CONSTRAINT pk PRIMARY KEY (id),
208
+ CONSTRAINT fk FOREIGN KEY (c) REFERENCES dbo.cust (id),
209
+ CONSTRAINT uq UNIQUE (e),
210
+ CONSTRAINT ck CHECK (c > 0));
211
+ GO
212
+ ALTER TABLE o WITH CHECK ADD CONSTRAINT fk2 FOREIGN KEY (c) REFERENCES cust2 (id)`;
213
+
214
+ test("each is a comment before the CREATE and one finding", () => {
215
+ const ddl = table(sql);
216
+ for (const c of ["-- FOREIGN KEY fk (c) REFERENCES dbo.cust (id)", "-- UNIQUE uq (e)", "-- CHECK ck (c > 0)", "-- FOREIGN KEY fk2 (c) REFERENCES cust2 (id)"]) expect(ddl).toContain(c);
217
+ expect(ddl.indexOf("-- UNIQUE")).toBeLessThan(ddl.indexOf("CREATE TABLE"));
218
+ expect(of(sql, "SQLRV005")).toHaveLength(4);
219
+ });
220
+
221
+ test("an ALTER TABLE ADD CONSTRAINT on a table the file does not create is a problem naming it", () => {
222
+ const r = t("ALTER TABLE ghost ADD CONSTRAINT x CHECK (a > 1)");
223
+ expect(r.problems).toEqual([expect.stringContaining("ghost")]);
224
+ });
225
+ });
226
+
227
+ describe("indexes", () => {
228
+ const base = "CREATE TABLE f (a int NOT NULL, b int NOT NULL, c nvarchar(5) NOT NULL);\nGO\n";
229
+
230
+ test("a nonclustered index is not carried and says what a skip index could do", () => {
231
+ const d = of(`${base}CREATE NONCLUSTERED INDEX ix ON f (b) INCLUDE (c) WHERE b > 1`, "SQLRV009")[0]!;
232
+ expect(d.message).toContain("ix, a nonclustered index on (b), is not carried");
233
+ expect(d.message).toContain("INCLUDE (c)");
234
+ expect(d.message).toContain("skip index");
235
+ });
236
+
237
+ test("a unique index is a unique constraint comment and a finding", () => {
238
+ const sql = `${base}CREATE UNIQUE NONCLUSTERED INDEX ux ON f (b)`;
239
+ expect(table(sql)).toContain("-- UNIQUE ux (b)");
240
+ expect(of(sql, "SQLRV005")).toHaveLength(1);
241
+ });
242
+
243
+ test("an index declared inside CREATE TABLE is read like a separate one", () => {
244
+ const sql = "CREATE TABLE f (a int NOT NULL, INDEX ix NONCLUSTERED (a) WITH (DATA_COMPRESSION = ROW))";
245
+ expect(of(sql, "SQLRV009").map((d) => d.message).join("\n")).toContain("ix, a nonclustered index");
246
+ });
247
+
248
+ test("an index on a table or view the file does not create is a problem", () => {
249
+ expect(t("CREATE INDEX ix ON nowhere (a)").problems[0]).toContain("nowhere");
250
+ expect(t("CREATE VIEW v AS SELECT 1 AS a;\nGO\nCREATE UNIQUE CLUSTERED INDEX ix ON v (a)").problems[0]).toContain("indexed view");
251
+ });
252
+ });
253
+
254
+ describe("what it refuses, by name", () => {
255
+ test.each([
256
+ ["CREATE TRIGGER dbo.trg_audit ON dbo.t AFTER INSERT AS BEGIN SELECT 1 END", "trigger dbo.trg_audit"],
257
+ ["CREATE PROCEDURE [dbo].[usp_load] @x int AS SELECT @x", "procedure dbo.usp_load"],
258
+ ["CREATE OR ALTER PROC usp_two AS SELECT 1", "procedure usp_two"],
259
+ ["CREATE FUNCTION [Application].DetermineCustomerAccess(@CityID int) RETURNS TABLE AS RETURN (SELECT 1 AS a)", "function Application.DetermineCustomerAccess"],
260
+ ["CREATE SEQUENCE dbo.s START WITH 1", "sequence dbo.s"],
261
+ ["CREATE TYPE dbo.flag FROM bit", "type dbo.flag"],
262
+ ["EXEC sp_rename 'a', 'b'", "statement"],
263
+ ["INSERT INTO t VALUES (1)", "statement"],
264
+ ])("%s is refused as %s", (sql, name) => {
265
+ const r = t(`CREATE TABLE ok (a int NOT NULL);\nGO\n${sql}`);
266
+ expect(r.problems).toHaveLength(1);
267
+ expect(r.problems[0]).toContain(name);
268
+ expect(r.problems[0]).toMatch(/\(line \d+\)/);
269
+ });
270
+
271
+ test("a trigger body with a CREATE inside it does not leak statements", () => {
272
+ const r = t("CREATE TRIGGER trg ON t AFTER INSERT AS BEGIN CREATE TABLE inner_t (a int); INSERT INTO x VALUES (1); END");
273
+ expect(r.problems).toHaveLength(1);
274
+ expect(r.objects).toEqual([]);
275
+ });
276
+
277
+ test("a column of a type it does not know names the table, the column and the type", () => {
278
+ const r = t("CREATE TABLE f (a int NOT NULL, v sql_variant, w dbo.flag)");
279
+ expect(r.problems).toEqual([expect.stringContaining("f.v (line 1): sql_variant"), expect.stringContaining("f.w (line 1): the type flag")]);
280
+ expect(r.objects).toEqual([]);
281
+ });
282
+
283
+ test("with skipUnreadable the rest imports and each refusal is a warning that names it", () => {
284
+ const r = t("CREATE TABLE ok (a int NOT NULL);\nGO\nCREATE TRIGGER trg ON ok AFTER INSERT AS SELECT 1", { skipUnreadable: true });
285
+ expect(r.problems).toEqual([]);
286
+ expect(r.objects.map((o) => o.name)).toEqual(["ok"]);
287
+ const left = r.diagnostics.find((d) => d.message.startsWith("Left out"))!;
288
+ expect(left).toMatchObject({ severity: "warning", ruleId: "SQLRV011" });
289
+ expect(left.message).toContain("trigger trg");
290
+ });
291
+ });
292
+
293
+ describe("what it notes without a decision", () => {
294
+ test("session settings and DROPs are info diagnostics, not silence", () => {
295
+ const r = t("SET ANSI_NULLS ON\nGO\nDROP TABLE IF EXISTS dbo.old;\nGO\nUSE shop\nGO\nCREATE TABLE a (x int NOT NULL)");
296
+ const info = r.diagnostics.filter((d) => d.severity === "info");
297
+ expect(info.map((d) => d.entity)).toEqual(["SET ANSI_NULLS ON", "DROP TABLE IF EXISTS dbo.old", "USE shop"]);
298
+ expect(r.problems).toEqual([]);
299
+ });
300
+
301
+ test("a description becomes a COMMENT; another property is an info diagnostic", () => {
302
+ const sql = `CREATE TABLE a (x int NOT NULL);
303
+ GO
304
+ EXECUTE sp_addextendedproperty @name = N'Description', @value = N'About a', @level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'a';
305
+ GO
306
+ EXECUTE sp_addextendedproperty @name = N'Description', @value = 'The x', @level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'a', @level2type = N'COLUMN', @level2name = N'x';
307
+ GO
308
+ EXECUTE sp_addextendedproperty @name = N'Owner', @value = 'finance', @level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'a';`;
309
+ const ddl = table(sql);
310
+ expect(ddl).toContain("x Int32 COMMENT 'The x'");
311
+ expect(ddl).toContain("COMMENT 'About a'");
312
+ const info = t(sql).diagnostics.filter((d) => d.severity === "info");
313
+ expect(info).toHaveLength(1);
314
+ expect(info[0]!.message).toContain("Owner = finance");
315
+ });
316
+ });
317
+
318
+ describe("schemas", () => {
319
+ test("a schema other than dbo is a database, created once, before the tables in it", () => {
320
+ const r = t("CREATE TABLE [Sales].[A] (x int NOT NULL);\nGO\nCREATE TABLE Sales.B (y int NOT NULL)");
321
+ expect(r.objects.map((o) => [o.type, o.database ?? "", o.name])).toEqual([
322
+ ["ClickHouse::Database", "", "Sales"],
323
+ ["ClickHouse::Table", "Sales", "A"],
324
+ ["ClickHouse::Table", "Sales", "B"],
325
+ ]);
326
+ expect(r.diagnostics.find((d) => d.ruleId === "SQLRV011")!.message).toContain("Schema Sales becomes the database Sales");
327
+ });
328
+
329
+ test("a CREATE SCHEMA in the file is the database, with no note about it", () => {
330
+ const r = t("CREATE SCHEMA Sales AUTHORIZATION dbo;\nGO\nCREATE TABLE Sales.A (x int NOT NULL)");
331
+ expect(r.objects[0]).toMatchObject({ type: "ClickHouse::Database", name: "Sales" });
332
+ expect(r.diagnostics.filter((d) => d.ruleId === "SQLRV011")).toEqual([]);
333
+ });
334
+
335
+ test("names are matched without regard to case, as SQL Server does", () => {
336
+ const r = t("CREATE TABLE Sales.Orders (Id int NOT NULL);\nGO\nCREATE CLUSTERED INDEX cx ON sales.orders (ID)");
337
+ expect(r.problems).toEqual([]);
338
+ expect(r.objects.find((o) => o.name === "Orders")!.ddl).toContain("ORDER BY Id");
339
+ });
340
+
341
+ test("a name that is not a plain identifier is backquoted", () => {
342
+ expect(table("CREATE TABLE [my table] ([my col] int NOT NULL)")).toContain("CREATE TABLE `my table`\n(\n `my col` Int32");
343
+ });
344
+ });
345
+
346
+ describe("views", () => {
347
+ test("the body is copied with identifiers requoted and dbo dropped, and a finding lists the T-SQL it found", () => {
348
+ const sql = "CREATE VIEW [WebApi].[V] (a, b) WITH SCHEMABINDING AS SELECT TOP (5) [x].[a], ISNULL(b, N'z') AS b FROM dbo.[t x] AS x NOLOCK;";
349
+ const r = t(sql);
350
+ const v = r.objects.find((o) => o.type === "ClickHouse::View")!;
351
+ expect(v.ddl).toBe("CREATE VIEW WebApi.V AS\nSELECT TOP (5) x.a, ISNULL(b, 'z') AS b FROM `t x` AS x NOLOCK");
352
+ const d = r.diagnostics.find((x) => x.ruleId === "SQLRV010")!;
353
+ expect(d.severity).toBe("warning");
354
+ expect(d.message).toContain("T-SQL constructs found by name: ISNULL, NOLOCK, TOP");
355
+ expect(d.message).toContain("the column list (a, b) is not carried");
356
+ expect(d.message).toContain("view options not carried: SCHEMABINDING");
357
+ });
358
+
359
+ test("a view with no T-SQL-only word still raises the finding, and says the server decides", () => {
360
+ const d = of("CREATE VIEW v AS SELECT a FROM t", "SQLRV010")[0]!;
361
+ expect(d.message).toContain("no T-SQL-only construct was found by name");
362
+ });
363
+ });
364
+
365
+ describe("storage options", () => {
366
+ test("filegroups, TEXTIMAGE_ON, data compression and partition schemes are listed in one finding per table", () => {
367
+ const sql = `CREATE TABLE f (a int NOT NULL, CONSTRAINT pk PRIMARY KEY CLUSTERED (a) WITH (DATA_COMPRESSION = PAGE) ON [PRIMARY]) ON [PS_Date] ([a]) TEXTIMAGE_ON [PRIMARY]`;
368
+ const d = of(sql, "SQLRV009");
369
+ expect(d).toHaveLength(1);
370
+ for (const w of ["ON [PS_Date] ([a])", "TEXTIMAGE_ON [PRIMARY]", "DATA_COMPRESSION = PAGE"]) expect(d[0]!.message).toContain(w);
371
+ });
372
+
373
+ test("a collation and a column option are findings on the column", () => {
374
+ const sql = "CREATE TABLE f (a nvarchar(5) COLLATE Latin1_General_CI_AS NOT NULL, b uniqueidentifier ROWGUIDCOL NOT NULL, c nvarchar(9) SPARSE NULL)";
375
+ expect(of(sql, "SQLRV007")[0]!).toMatchObject({ line: 1 });
376
+ expect(of(sql, "SQLRV007")[0]!.message).toContain("Latin1_General_CI_AS");
377
+ expect(of(sql, "SQLRV009").map((d) => d.message)).toEqual([expect.stringContaining("ROWGUIDCOL"), expect.stringContaining("SPARSE")]);
378
+ });
379
+ });
380
+
381
+ describe("LowCardinality", () => {
382
+ test("a String column named for a small set is a candidate and keeps String", () => {
383
+ const sql = "CREATE TABLE f (order_status nvarchar(20) NOT NULL, notes nvarchar(20) NOT NULL)";
384
+ expect(table(sql)).toContain("order_status String,");
385
+ const d = of(sql, "SQLRV004");
386
+ expect(d).toHaveLength(1);
387
+ expect(d[0]!.message).toContain("the declared length is 20");
388
+ });
389
+ });
390
+
391
+ describe("diagnostics carry the source position", () => {
392
+ test("a column finding points at the column's line and column", () => {
393
+ const sql = "CREATE TABLE f (\n a int NOT NULL,\n b int NULL\n)";
394
+ expect(of(sql, "SQLRV003")[0]).toMatchObject({ line: 3, column: 5 });
395
+ });
396
+ });