@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
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { join } from "node:path";
16
- import { describe, expect, test } from "vitest";
16
+ import { beforeAll, describe, expect, test } from "vitest";
17
17
  import { generateOpsPipeline } from "@intentius/chant/op";
18
18
  import { parseYAML } from "@intentius/chant/yaml";
19
19
  import type { ScheduledOpSpec } from "@intentius/chant/lexicon";
@@ -58,6 +58,13 @@ async function render(forge: "github" | "gitlab" | "forgejo", spec: ScheduledOpS
58
58
  const runLine = (steps: Array<Record<string, unknown>>) => steps.find((s) => s.run === "chant run schema-watch");
59
59
 
60
60
  describe("generateOpsPipeline renders the sql drift watch (#3642)", () => {
61
+ // The first render loads the github, gitlab and forgejo plugins and discovers the example's Ops. On a loaded CI shard
62
+ // that cold load took 5.7s and put the first test over the 5s unit-test budget (chant #2817). A file's beforeAll is
63
+ // not counted against the budget, so the load is paid here and each test below times only its own render.
64
+ beforeAll(async () => {
65
+ await generateOpsPipeline([reader(actionsSecret)], "github", { beforeScript: INSTALL.actions }, EXAMPLE);
66
+ }, 60_000);
67
+
61
68
  test("GitHub: the Op's own cron, a read-only token and the reader's credentials", async () => {
62
69
  const { file, doc } = await render("github", reader(actionsSecret));
63
70
  expect(file.name).toBe("schema-watch.yml");
package/src/plugin.ts CHANGED
@@ -18,7 +18,7 @@ import { completions } from "./lsp/completions";
18
18
  import { hover } from "./lsp/hover";
19
19
  import { sqlMcpResources, sqlMcpTools } from "./mcp";
20
20
  import { sqlConfigSchema } from "./config";
21
- import { SqlFileParser, dialectOption } from "./import-parser";
21
+ import { SqlFileParser, dialectOption, sourceOption } from "./import-parser";
22
22
  import { sqlTemplateGenerator } from "./import-generator";
23
23
  import { sqlCommands } from "./clickhouse/plan/commands";
24
24
  import { sqlDeepNormalizationHooks } from "./clickhouse/plan/deep";
@@ -29,6 +29,7 @@ import { CLICKHOUSE_EMULATOR } from "./op/activities/clickhouse-emulator";
29
29
  import { POSTGRES_EMULATOR } from "./op/activities/postgres-emulator";
30
30
  import { sqlSkills } from "./skill-defs";
31
31
  import { detectTemplate } from "./detect";
32
+ import { looksLikePostgres } from "./migrate/from-postgres/detect";
32
33
  import { sqlWorkspaceOutputs } from "./workspace-outputs";
33
34
  import { CDC_TEMPLATE, DEFAULT_TEMPLATE, EVENTS_TEMPLATE, POSTGRES_EVENTS_TEMPLATE, POSTGRES_TEMPLATE, POSTGRES_TENANT_TEMPLATE } from "./init-templates";
34
35
  import { compositeCatalog } from "./composites/catalog";
@@ -113,6 +114,16 @@ export const sqlPlugin: LexiconPlugin = {
113
114
  { name: "aggregate", isTag: true, description: "A Postgres CREATE AGGREGATE, parsed into an aggregate entity identified by its argument types" },
114
115
  { name: "trigger", isTag: true, description: "A Postgres CREATE TRIGGER, parsed into a trigger entity on its table" },
115
116
  { name: "eventTrigger", isTag: true, description: "A Postgres CREATE EVENT TRIGGER, parsed into the database's event trigger entity" },
117
+ { name: "operator", isTag: true, description: "A Postgres CREATE OPERATOR, parsed into an operator entity identified by its name and operand types" },
118
+ { name: "operatorClass", isTag: true, description: "A Postgres CREATE OPERATOR CLASS, parsed into an operator class entity identified by its name and index method" },
119
+ { name: "operatorFamily", isTag: true, description: "A Postgres CREATE OPERATOR FAMILY, parsed into an operator family entity identified by its name and index method" },
120
+ { name: "foreignTable", isTag: true, description: "A Postgres CREATE FOREIGN TABLE, parsed into a foreign table entity with its columns, server and options" },
121
+ { name: "textSearchConfiguration", isTag: true, description: "A Postgres CREATE TEXT SEARCH CONFIGURATION with its ADD MAPPING statements, parsed into a configuration entity" },
122
+ { name: "textSearchDictionary", isTag: true, description: "A Postgres CREATE TEXT SEARCH DICTIONARY, parsed into a dictionary entity with its template and options" },
123
+ { name: "statistics", isTag: true, description: "A Postgres CREATE STATISTICS, parsed into an extended statistics entity on its table" },
124
+ { name: "foreignDataWrapper", isTag: true, description: "A Postgres CREATE FOREIGN DATA WRAPPER, parsed into the database's wrapper entity" },
125
+ { name: "foreignServer", isTag: true, description: "A Postgres CREATE SERVER, parsed into the database's foreign server entity (user mappings are not declared)" },
126
+ { name: "publication", isTag: true, description: "A Postgres CREATE PUBLICATION, parsed into the database's publication entity" },
116
127
  { name: "collation", isTag: true, description: "A Postgres CREATE COLLATION, parsed into a collation entity" },
117
128
  { name: "cast", isTag: true, description: "A Postgres CREATE CAST, parsed into a cast entity named by its two types" },
118
129
  { name: "policy", isTag: true, description: "A Postgres CREATE POLICY, parsed into a row-level security policy on its table, or a ClickHouse CREATE ROW POLICY" },
@@ -166,7 +177,31 @@ export const sqlPlugin: LexiconPlugin = {
166
177
 
167
178
  /** `chant import schema.sql`: a file of Postgres or ClickHouse DDL (`./import-parser.ts`). */
168
179
  templateParser(options) {
169
- return new SqlFileParser(dialectOption(options), { skipUndeclarable: options?.skipUndeclarable === true });
180
+ const from = sourceOption(options);
181
+ return new SqlFileParser(dialectOption(options), { skipUndeclarable: options?.skipUndeclarable === true, ...(from ? { from } : {}) });
182
+ },
183
+
184
+ /** `chant import --validate` (#4398): create the imported DDL on the pinned ClickHouse emulator. */
185
+ async validateImport(ir) {
186
+ const { validateOnEmulator } = await import("./sqlserver/validate");
187
+ return validateOnEmulator(ir);
188
+ },
189
+
190
+ /**
191
+ * `chant migrate --from sql/postgres --to sql/clickhouse` (#4399): the
192
+ * Postgres tables a CDC pipeline mirrors, as ClickHouse tables or
193
+ * `CdcMirror` calls (`./migrate/from-postgres/index.ts`).
194
+ */
195
+ migrationSource(from, to) {
196
+ if (from !== "sql/postgres" || (to !== undefined && to !== "sql/clickhouse")) return undefined;
197
+ return {
198
+ detect: looksLikePostgres,
199
+ directory: true,
200
+ async transform(content, opts) {
201
+ const { transform } = await import("./migrate/from-postgres/index");
202
+ return transform(content, opts);
203
+ },
204
+ };
170
205
  },
171
206
 
172
207
  parserOptions() {
@@ -176,6 +211,11 @@ export const sqlPlugin: LexiconPlugin = {
176
211
  type: "string" as const,
177
212
  description: "the DDL's dialect, clickhouse or postgres; read off its statements when omitted",
178
213
  },
214
+ {
215
+ name: "from",
216
+ type: "string" as const,
217
+ description: "read the file as a SQL Server schema (sqlserver) and translate it to ClickHouse; each judgment call is a needs-review diagnostic for --report and --strict",
218
+ },
179
219
  {
180
220
  name: "skipUndeclarable",
181
221
  type: "boolean" as const,
@@ -260,6 +300,15 @@ export const sqlPlugin: LexiconPlugin = {
260
300
  return { ...classifyDisruption(options), ...classifyPgDisruption(options) };
261
301
  },
262
302
 
303
+ /**
304
+ * The migration files a pull request adds (#4407): each up statement given
305
+ * the class the schema-diff classifier gives the same change. Offline.
306
+ */
307
+ async classifyMigrations(options) {
308
+ const { classifyMigrationFiles } = await import("./migration-classify");
309
+ return classifyMigrationFiles(options);
310
+ },
311
+
263
312
  /** `chant sql diff` and `chant sql plan`: schema changes, classified. */
264
313
  commands() {
265
314
  return sqlCommands;
@@ -188,7 +188,7 @@ export function declaredAccess(objects: ReadonlyArray<{ key: string; canonical:
188
188
  for (const { canonical: o } of objects) {
189
189
  const name = `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${quoteIdent(o.name)}`;
190
190
  if (o.kind === "schema") declaredTargets.push({ kind: "schema", name: quoteIdent(o.name) });
191
- else if (o.kind === "table" || o.kind === "view" || o.kind === "materializedView") {
191
+ else if (o.kind === "table" || o.kind === "foreignTable" || o.kind === "view" || o.kind === "materializedView") {
192
192
  relationKind.set(name, "table");
193
193
  declaredTargets.push({ kind: "table", name });
194
194
  } else if (o.kind === "sequence") {
@@ -21,7 +21,7 @@ function targetsOf(objects: readonly LivePgObject[]): AclTarget[] {
21
21
  for (const o of objects) {
22
22
  const name = `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${quoteIdent(o.name)}`;
23
23
  if (o.type === POSTGRES_ENTITY_TYPES.schema) out.push({ kind: "schema", name: quoteIdent(o.name) });
24
- else if (o.type === POSTGRES_ENTITY_TYPES.table || o.type === POSTGRES_ENTITY_TYPES.view || o.type === POSTGRES_ENTITY_TYPES.materializedView) out.push({ kind: "table", name });
24
+ else if (o.type === POSTGRES_ENTITY_TYPES.table || o.type === POSTGRES_ENTITY_TYPES.foreignTable || o.type === POSTGRES_ENTITY_TYPES.view || o.type === POSTGRES_ENTITY_TYPES.materializedView) out.push({ kind: "table", name });
25
25
  else if (o.type === POSTGRES_ENTITY_TYPES.sequence) out.push({ kind: "sequence", name });
26
26
  else if (o.type === POSTGRES_ENTITY_TYPES.function || o.type === POSTGRES_ENTITY_TYPES.procedure) {
27
27
  out.push({ kind: o.type === POSTGRES_ENTITY_TYPES.function ? "function" : "procedure", name: `${name}${o.signature ?? ""}` });
@@ -553,6 +553,9 @@ async function applyAccess(
553
553
  }
554
554
  }
555
555
 
556
+ /** The kinds that belong to the database, not a schema. */
557
+ const UNPLACED_KINDS: ReadonlySet<CanonicalKind> = new Set(["eventTrigger", "foreignDataWrapper", "foreignServer", "publication"]);
558
+
556
559
  /** The address a live object is keyed by in the diff. */
557
560
  function liveCanonicalAddress(o: LivePgObject): { kind: CanonicalKind; schema?: string; name: string; signature?: string } {
558
561
  const kind = (
@@ -571,6 +574,16 @@ function liveCanonicalAddress(o: LivePgObject): { kind: CanonicalKind; schema?:
571
574
  "Postgres::Collation": "collation",
572
575
  "Postgres::Cast": "cast",
573
576
  "Postgres::EventTrigger": "eventTrigger",
577
+ "Postgres::Operator": "operator",
578
+ "Postgres::OperatorClass": "operatorClass",
579
+ "Postgres::OperatorFamily": "operatorFamily",
580
+ "Postgres::ForeignTable": "foreignTable",
581
+ "Postgres::TextSearchConfiguration": "textSearchConfiguration",
582
+ "Postgres::TextSearchDictionary": "textSearchDictionary",
583
+ "Postgres::Statistics": "statistics",
584
+ "Postgres::ForeignDataWrapper": "foreignDataWrapper",
585
+ "Postgres::ForeignServer": "foreignServer",
586
+ "Postgres::Publication": "publication",
574
587
  "Postgres::Extension": "extension",
575
588
  "Postgres::Function": "function",
576
589
  "Postgres::Procedure": "procedure",
@@ -583,7 +596,7 @@ function liveCanonicalAddress(o: LivePgObject): { kind: CanonicalKind; schema?:
583
596
  )[o.type];
584
597
  return {
585
598
  kind,
586
- ...(o.schema !== undefined && kind !== "schema" && kind !== "extension" && kind !== "role" && kind !== "cast" && kind !== "eventTrigger" ? { schema: o.schema } : {}),
599
+ ...(o.schema !== undefined && kind !== "schema" && kind !== "extension" && kind !== "role" && kind !== "cast" && !UNPLACED_KINDS.has(kind) ? { schema: o.schema } : {}),
587
600
  name: o.name,
588
601
  ...(o.signature !== undefined ? { signature: o.signature } : {}),
589
602
  };
@@ -34,7 +34,7 @@ import { ACCESS_KINDS, canonicalOptions, sameConstraint, type CanonicalConstrain
34
34
  import type { PgDiffObject, PgSchemaObject } from "../plan/schema";
35
35
  import { migrationTarget } from "../migrate/handoff";
36
36
  import { pgName } from "../migrate/names";
37
- import type { CheckDef, ColumnDef, DomainProps, ExclusionDef, ForeignKeyDef, IndexProps, KeyDef, PolicyProps, SequenceProps, TableProps } from "../entities";
37
+ import type { CheckDef, ColumnDef, DomainProps, ForeignTableProps, TextSearchConfigurationProps, ExclusionDef, ForeignKeyDef, IndexProps, KeyDef, PolicyProps, SequenceProps, TableProps } from "../entities";
38
38
 
39
39
  const same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);
40
40
 
@@ -108,10 +108,12 @@ export const pgQualified = (schema: string | undefined, name: string): string =>
108
108
  export const objectIdent = (o: { kind: CanonicalKind; schema?: string; name: string; signature?: string }): string =>
109
109
  o.kind === "cast"
110
110
  ? o.name
111
- : o.kind === "schema" || o.kind === "extension" || o.kind === "role" || o.kind === "eventTrigger"
111
+ : o.kind === "schema" || o.kind === "extension" || o.kind === "role" || o.kind === "eventTrigger" || o.kind === "foreignDataWrapper" || o.kind === "foreignServer" || o.kind === "publication"
112
112
  ? quoteIdent(o.name)
113
113
  : o.kind === "trigger" || o.kind === "policy"
114
114
  ? `${quoteIdent(o.name)}${o.signature ?? ""}`
115
+ : o.kind === "operator"
116
+ ? `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${o.name}${o.signature ?? ""}`
115
117
  : `${pgQualified(o.schema, o.name)}${o.signature ?? ""}`;
116
118
 
117
119
  /** The words a statement names the object's kind with. */
@@ -133,6 +135,16 @@ export const KIND_WORDS: Readonly<Record<CanonicalKind, string>> = {
133
135
  collation: "COLLATION",
134
136
  cast: "CAST",
135
137
  eventTrigger: "EVENT TRIGGER",
138
+ operator: "OPERATOR",
139
+ operatorClass: "OPERATOR CLASS",
140
+ operatorFamily: "OPERATOR FAMILY",
141
+ foreignTable: "FOREIGN TABLE",
142
+ textSearchConfiguration: "TEXT SEARCH CONFIGURATION",
143
+ textSearchDictionary: "TEXT SEARCH DICTIONARY",
144
+ statistics: "STATISTICS",
145
+ foreignDataWrapper: "FOREIGN DATA WRAPPER",
146
+ foreignServer: "SERVER",
147
+ publication: "PUBLICATION",
136
148
  trigger: "TRIGGER",
137
149
  policy: "POLICY",
138
150
  role: "ROLE",
@@ -315,7 +327,7 @@ function identitySetClauses(options: string | undefined): string[] {
315
327
  return out;
316
328
  }
317
329
 
318
- const COLUMN_FIELD = /^columns\.(.+?)(?:\.(type|default|notNull|generated|identity|collate|storage|comment))?$/;
330
+ const COLUMN_FIELD = /^columns\.(.+?)(?:\.(type|default|notNull|generated|identity|collate|storage|options|comment))?$/;
319
331
 
320
332
  // ── Constraints ────────────────────────────────────────────────────────
321
333
 
@@ -569,6 +581,181 @@ export function setNotNullSteps(table: string, column: string, live?: Pick<PgDif
569
581
  return steps;
570
582
  }
571
583
 
584
+ /** An `OPTIONS (...)` clause that takes the options `have` to `want`: ADD, SET and DROP per option; empty when they agree. */
585
+ function optionsClause(want: unknown, have: unknown): string {
586
+ const w = (want ?? {}) as Record<string, string>;
587
+ const h = (have ?? {}) as Record<string, string>;
588
+ const parts = [
589
+ ...Object.keys(w).filter((k) => !(k in h)).map((k) => `ADD ${quoteIdent(k)} ${pgString(w[k]!)}`),
590
+ ...Object.keys(w).filter((k) => k in h && h[k] !== w[k]).map((k) => `SET ${quoteIdent(k)} ${pgString(w[k]!)}`),
591
+ ...Object.keys(h).filter((k) => !(k in w)).map((k) => `DROP ${quoteIdent(k)}`),
592
+ ];
593
+ return parts.length > 0 ? `OPTIONS (${parts.join(", ")})` : "";
594
+ }
595
+
596
+ /** A publication's table as `ADD TABLE` writes it: its name, column list and row filter. */
597
+ const publicationTable = (t: { table: string; columns?: string[]; where?: string }): string =>
598
+ `${t.table}${t.columns && t.columns.length > 0 ? ` (${t.columns.map(quoteIdent).join(", ")})` : ""}${t.where ? ` WHERE (${t.where})` : ""}`;
599
+
600
+ /**
601
+ * The ALTER statements that take a foreign data wrapper, a foreign server or
602
+ * a publication to the declaration, in place (SQLPG303): one ALTER for a
603
+ * wrapper or server; for a publication one SET (...) for its options, and a
604
+ * DROP and ADD per table or schema that changed.
605
+ */
606
+ function alteredSteps(d: PgDiffObject, live: PgDiffObject, target: string): PgStep[] {
607
+ const out: string[] = [];
608
+ const f = d.fields;
609
+ const l = live.fields;
610
+ if (d.kind === "foreignDataWrapper") {
611
+ const clauses = [
612
+ !same(f.handler, l.handler) ? (f.handler ? `HANDLER ${String(f.handler)}` : "NO HANDLER") : "",
613
+ !same(f.validator, l.validator) ? (f.validator ? `VALIDATOR ${String(f.validator)}` : "NO VALIDATOR") : "",
614
+ optionsClause(f.options, l.options),
615
+ ].filter(Boolean);
616
+ if (clauses.length > 0) out.push(`ALTER FOREIGN DATA WRAPPER ${target} ${clauses.join(" ")}`);
617
+ } else if (d.kind === "foreignServer") {
618
+ const clauses = [!same(f.version, l.version) ? `VERSION ${f.version === undefined ? "NULL" : pgString(String(f.version))}` : "", optionsClause(f.options, l.options)].filter(Boolean);
619
+ if (clauses.length > 0) out.push(`ALTER SERVER ${target} ${clauses.join(" ")}`);
620
+ } else if (d.kind === "publication") {
621
+ const sets: string[] = [];
622
+ if (!same(f.publish, l.publish)) sets.push(`publish = ${pgString(((f.publish as string[] | undefined) ?? ["insert", "update", "delete", "truncate"]).join(", "))}`);
623
+ if (!same(f.viaRoot, l.viaRoot)) sets.push(`publish_via_partition_root = ${f.viaRoot === true}`);
624
+ if (!same(f.generatedColumns, l.generatedColumns)) sets.push(`publish_generated_columns = ${String(f.generatedColumns ?? "none")}`);
625
+ if (sets.length > 0) out.push(`ALTER PUBLICATION ${target} SET (${sets.join(", ")})`);
626
+ type Member = { table: string; columns?: string[]; where?: string };
627
+ const want = (f.tables as Member[] | undefined) ?? [];
628
+ const have = (l.tables as Member[] | undefined) ?? [];
629
+ const kept = (m: Member) => want.some((w) => w.table === m.table && same(w, m));
630
+ for (const m of have) if (!kept(m)) out.push(`ALTER PUBLICATION ${target} DROP TABLE ${m.table}`);
631
+ for (const m of want) if (!have.some((h) => h.table === m.table && same(h, m))) out.push(`ALTER PUBLICATION ${target} ADD TABLE ${publicationTable(m)}`);
632
+ const wantSchemas = (f.schemas as string[] | undefined) ?? [];
633
+ const haveSchemas = (l.schemas as string[] | undefined) ?? [];
634
+ for (const s of haveSchemas) if (!wantSchemas.includes(s)) out.push(`ALTER PUBLICATION ${target} DROP TABLES IN SCHEMA ${s}`);
635
+ for (const s of wantSchemas) if (!haveSchemas.includes(s)) out.push(`ALTER PUBLICATION ${target} ADD TABLES IN SCHEMA ${s}`);
636
+ }
637
+ return out.map((sql) => step(sql, "metadata", "SQLPG303"));
638
+ }
639
+
640
+
641
+ // ── Operators, text search and foreign tables (#4406) ──────────────────
642
+
643
+ /**
644
+ * The clauses of `ALTER OPERATOR ... SET (...)` for the attributes the diff
645
+ * found changeable in place (SQLPG304): RESTRICT and JOIN to the declared
646
+ * estimator or NONE, COMMUTATOR and NEGATOR to the declared operator, and
647
+ * HASHES and MERGES as bare words, since they can only be turned on.
648
+ */
649
+ function operatorSetClauses(d: PgDiffObject, changes: readonly PgChange[]): string[] {
650
+ const definition = (d.fields.definition ?? {}) as Record<string, string>;
651
+ return changes.map((c) => {
652
+ const key = c.field.replace(/^definition\./, "");
653
+ const value = definition[key];
654
+ if (key === "hashes" || key === "merges") return key.toUpperCase();
655
+ if (key === "commutator" || key === "negator") return `${key.toUpperCase()} = OPERATOR(${value})`;
656
+ return `${key.toUpperCase()} = ${value ?? "NONE"}`;
657
+ });
658
+ }
659
+
660
+ /**
661
+ * A text search configuration's mappings, or a dictionary's options, taken to
662
+ * the declaration in place (SQLPG305). For a configuration: DROP MAPPING for
663
+ * the token types no longer mapped, ALTER MAPPING for those whose dictionaries
664
+ * changed, ADD MAPPING for the new ones, one statement for the token types
665
+ * that share their dictionaries; the dictionaries are named as the
666
+ * declaration wrote them. For a dictionary: one ALTER with each option that
667
+ * differs set, and each option the declaration dropped named with no value,
668
+ * which removes it.
669
+ */
670
+ function textSearchSteps(obj: DeclaredPgObject, live: PgDiffObject, target: string): PgStep[] {
671
+ const d = obj.canonical;
672
+ if (d.kind === "textSearchDictionary") {
673
+ const want = (d.fields.options ?? {}) as Record<string, string>;
674
+ const have = (live.fields.options ?? {}) as Record<string, string>;
675
+ const parts = [
676
+ ...Object.keys(want).filter((k) => have[k] !== want[k]).map((k) => `${quoteIdent(k)} = ${pgString(want[k]!)}`),
677
+ ...Object.keys(have).filter((k) => !(k in want)).map((k) => quoteIdent(k)),
678
+ ];
679
+ return parts.length > 0 ? [step(`ALTER TEXT SEARCH DICTIONARY ${target} (${parts.join(", ")})`, "metadata", "SQLPG305")] : [];
680
+ }
681
+ const want = (d.fields.mappings ?? {}) as Record<string, string[]>;
682
+ const have = (live.fields.mappings ?? {}) as Record<string, string[]>;
683
+ const written = new Map<string, string[]>();
684
+ for (const m of (obj.props as unknown as TextSearchConfigurationProps).mappings) for (const t of m.tokens) written.set(t.toLowerCase(), m.dictionaries);
685
+ const group = (tokens: string[]): Map<string, string[]> => {
686
+ const out = new Map<string, string[]>();
687
+ for (const t of tokens) {
688
+ const key = JSON.stringify(want[t]);
689
+ out.set(key, [...(out.get(key) ?? []), t]);
690
+ }
691
+ return out;
692
+ };
693
+ const dropped = Object.keys(have).filter((t) => !(t in want));
694
+ const added = Object.keys(want).filter((t) => !(t in have));
695
+ const changed = Object.keys(want).filter((t) => t in have && JSON.stringify(have[t]) !== JSON.stringify(want[t]));
696
+ const names = (tokens: string[]) => tokens.map(quoteIdent).join(", ");
697
+ const out: PgStep[] = [];
698
+ if (dropped.length > 0) out.push(step(`ALTER TEXT SEARCH CONFIGURATION ${target} DROP MAPPING FOR ${names(dropped)}`, "metadata", "SQLPG305"));
699
+ for (const [verb, tokens] of [["ALTER", changed], ["ADD", added]] as const) {
700
+ for (const members of group(tokens).values()) out.push(step(`ALTER TEXT SEARCH CONFIGURATION ${target} ${verb} MAPPING FOR ${names(members)} WITH ${written.get(members[0]!)!.join(", ")}`, "metadata", "SQLPG305"));
701
+ }
702
+ return out;
703
+ }
704
+
705
+ /**
706
+ * A foreign table's changes in place (SQLPG306), as ALTER FOREIGN TABLE
707
+ * makes them: ADD and DROP COLUMN, a column's type, default, NOT NULL and
708
+ * OPTIONS, CHECK constraints added and dropped, and the table's OPTIONS.
709
+ * A column added with options gets them in an ALTER of its own, since ADD
710
+ * COLUMN has no OPTIONS clause.
711
+ */
712
+ function foreignTableSteps(c: PgChange, obj: DeclaredPgObject, live: PgDiffObject, alterKind: string): PgStep[] {
713
+ const d = obj.canonical;
714
+ const props = obj.props as unknown as ForeignTableProps;
715
+ const make = (sql: string) => step(sql, "metadata", "SQLPG306");
716
+ if (c.field === "options") {
717
+ const clause = optionsClause(d.fields.options, live.fields.options);
718
+ return clause ? [make(`${alterKind} ${clause}`)] : [];
719
+ }
720
+ if (c.field.startsWith("constraints.")) {
721
+ const key = c.field.slice("constraints.".length);
722
+ // A declared CHECK the server lacks is added; one it has and the declaration dropped is dropped by the server's name.
723
+ if (c.before === undefined) {
724
+ const i = d.constraints.findIndex((a) => (a.name ?? a.kind.toLowerCase()) === key && !live.constraints.some((b) => sameConstraint(a, b)));
725
+ return i >= 0 && props.checks[i] ? [make(`${alterKind} ADD ${constraintSql("CHECK", props.checks[i]!)}`)] : [];
726
+ }
727
+ return [make(`${alterKind} DROP CONSTRAINT ${quoteIdent(key)}`)];
728
+ }
729
+ const m = COLUMN_FIELD.exec(c.field);
730
+ if (!m) return [];
731
+ const name = m[1]!;
732
+ const prop = m[2];
733
+ const def = props.columns.find((x) => x.name === name);
734
+ const column = `${alterKind} ALTER COLUMN ${quoteIdent(name)}`;
735
+ const liveColumn = live.columns.find((x) => x.name === name);
736
+ if (prop === undefined) {
737
+ if (!def) return [make(`${alterKind} DROP COLUMN ${quoteIdent(name)}`)];
738
+ const options = optionsClause(def.options, undefined);
739
+ return [make(`${alterKind} ADD COLUMN ${columnDefinition(def)}`), ...(options ? [make(`${column} ${options}`)] : [])];
740
+ }
741
+ if (!def) return [];
742
+ switch (prop) {
743
+ case "type":
744
+ case "collate":
745
+ return def.type ? [make(`${column} TYPE ${def.type}${def.collate ? ` COLLATE ${def.collate}` : prop === "collate" ? ' COLLATE "default"' : ""}`)] : [];
746
+ case "default":
747
+ return [make(def.default !== undefined ? `${column} SET DEFAULT ${def.default}` : `${column} DROP DEFAULT`)];
748
+ case "notNull":
749
+ return [make(`${column} ${def.notNull ? "SET" : "DROP"} NOT NULL`)];
750
+ case "options": {
751
+ const clause = optionsClause(def.options, liveColumn?.options);
752
+ return clause ? [make(`${column} ${clause}`)] : [];
753
+ }
754
+ default:
755
+ return [];
756
+ }
757
+ }
758
+
572
759
  /**
573
760
  * The statements for an object that exists on the server, change by change.
574
761
  * `live` is the server's definition, matched to the declaration (a renamed
@@ -604,13 +791,23 @@ export function alterSteps(
604
791
  steps.push(...createSteps(obj, opts.marker, { cls: c.class, rule: c.rule }));
605
792
  continue;
606
793
  }
794
+ if (c.rule === "SQLPG306") {
795
+ steps.push(...foreignTableSteps(c, obj, live, alterKind));
796
+ continue;
797
+ }
798
+ if (d.kind === "foreignTable" && c.rule === "SQLPG216" && /^constraints\.[^.]+\.comment$/.test(c.field)) {
799
+ const name = c.field.split(".")[1]!;
800
+ const declared = d.constraints.find((a) => (a.name ?? a.kind.toLowerCase()) === name);
801
+ steps.push(step(`COMMENT ON CONSTRAINT ${quoteIdent(name)} ON ${target} IS ${declared?.comment === undefined ? "NULL" : pgString(declared.comment)}`, c.class, c.rule));
802
+ continue;
803
+ }
607
804
  if (c.field.startsWith("constraints.")) {
608
805
  if (!constraintsDone) steps.push(...constraintSteps(obj, live, notNullChecks));
609
806
  constraintsDone = true;
610
807
  continue;
611
808
  }
612
809
  const col = COLUMN_FIELD.exec(c.field);
613
- if (col && (d.kind === "table")) {
810
+ if (col && (d.kind === "table" || d.kind === "foreignTable")) {
614
811
  const name = col[1]!;
615
812
  const prop = col[2];
616
813
  const def = columns.get(name);
@@ -704,6 +901,15 @@ export function alterSteps(
704
901
  case "SQLPG229":
705
902
  steps.push(step(`ALTER INDEX ${objectIdent(live)} RENAME TO ${quoteIdent(d.name)}`, c.class, c.rule));
706
903
  break;
904
+ case "SQLPG303":
905
+ if (!steps.some((s) => s.rule === "SQLPG303")) steps.push(...alteredSteps(d, live, target));
906
+ break;
907
+ case "SQLPG304":
908
+ if (!steps.some((s) => s.rule === "SQLPG304")) steps.push(step(`ALTER OPERATOR ${target} SET (${operatorSetClauses(d, changes.filter((x) => x.rule === "SQLPG304")).join(", ")})`, c.class, c.rule));
909
+ break;
910
+ case "SQLPG305":
911
+ if (!steps.some((s) => s.rule === "SQLPG305")) steps.push(...textSearchSteps(obj, live, target));
912
+ break;
707
913
  case "SQLPG250":
708
914
  case "SQLPG280":
709
915
  case "SQLPG302":
@@ -882,15 +1088,30 @@ const DROP_ORDER: Record<CanonicalKind, number> = {
882
1088
  defaultPrivileges: 0,
883
1089
  policy: 0,
884
1090
  trigger: 0,
1091
+ // A publication reads tables, extended statistics are on one: both go before it.
1092
+ publication: 0,
1093
+ statistics: 0,
885
1094
  materializedView: 1,
886
1095
  view: 2,
887
1096
  index: 3,
888
1097
  table: 4,
1098
+ // A foreign table reads a foreign server (below) and a type; a view on it goes before.
1099
+ foreignTable: 4,
889
1100
  sequence: 5,
890
1101
  // An event trigger and a cast before the function they call; an aggregate before its functions.
891
1102
  eventTrigger: 5.5,
892
1103
  cast: 5.5,
893
1104
  aggregate: 5.5,
1105
+ operator: 5.5,
1106
+ // A class names operators and functions; a family goes after its classes.
1107
+ operatorClass: 5.2,
1108
+ operatorFamily: 5.3,
1109
+ // A configuration maps token types to dictionaries, so it goes first.
1110
+ textSearchConfiguration: 5.7,
1111
+ textSearchDictionary: 5.8,
1112
+ // A server before its wrapper, a wrapper before the handler function it calls.
1113
+ foreignServer: 5.5,
1114
+ foreignDataWrapper: 5.6,
894
1115
  // A routine after what calls it (a view, a trigger, a default), before the types its parameters use.
895
1116
  function: 6,
896
1117
  procedure: 6,
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The names Postgres gives a constraint or an index that the DDL leaves
3
+ * unnamed, so a reader of the declarations can spell them without a server.
4
+ *
5
+ * `makeObjectName` and the `Choose*Name` functions are from the Postgres
6
+ * source (`src/backend/commands/indexcmds.c`, `src/backend/catalog/heap.c`,
7
+ * `src/backend/commands/tablecmds.c`): the table name, then the column
8
+ * names joined with `_`, then a label (`pkey`, `key`, `fkey`, `check`),
9
+ * cut to 63 bytes by shortening the longer of the first two parts one
10
+ * character at a time. A name already taken gets a number after the label
11
+ * (`orders_pkey1`).
12
+ *
13
+ * A name the author writes always wins; these are for the rest. Drizzle's
14
+ * own derived names (`orders_user_id_users_id_fk`) differ from them, which is
15
+ * why the Drizzle export names every constraint it writes.
16
+ */
17
+
18
+ const NAMEDATALEN = 64;
19
+
20
+ /** `makeObjectName(name1, name2, label)`: ASCII names only. */
21
+ export function makeObjectName(name1: string, name2: string | undefined, label: string | undefined): string {
22
+ let overhead = 0;
23
+ let n2 = 0;
24
+ if (name2 !== undefined) {
25
+ n2 = name2.length;
26
+ overhead++;
27
+ }
28
+ if (label !== undefined) overhead += label.length + 1;
29
+ const avail = NAMEDATALEN - 1 - overhead;
30
+ let n1 = name1.length;
31
+ while (n1 + n2 > avail) {
32
+ if (n1 > n2) n1--;
33
+ else n2--;
34
+ }
35
+ let name = name1.slice(0, n1);
36
+ if (name2 !== undefined) name += `_${name2.slice(0, n2)}`;
37
+ if (label !== undefined) name += `_${label}`;
38
+ return name;
39
+ }
40
+
41
+ /** The first of `label`, `label1`, `label2`, ... that gives a name not in `taken`. */
42
+ export function chooseName(name1: string, name2: string | undefined, label: string, taken: ReadonlySet<string>): string {
43
+ let pass = 0;
44
+ let candidate = makeObjectName(name1, name2, label);
45
+ while (taken.has(candidate)) candidate = makeObjectName(name1, name2, `${label}${++pass}`);
46
+ return candidate;
47
+ }
48
+
49
+ /** The column names of a key or foreign key, joined as Postgres joins them. */
50
+ export const columnsAddition = (columns: readonly string[]): string => columns.join("_");
51
+
52
+ /**
53
+ * The identifiers `expr` reads as column names: words outside quotes and
54
+ * string literals that are not followed by `(`, and are in `columns`.
55
+ */
56
+ export function referencedColumns(expr: string, columns: readonly string[]): string[] {
57
+ const known = new Set(columns);
58
+ const found = new Set<string>();
59
+ const re = /'(?:[^']|'')*'|"((?:[^"]|"")+)"|([A-Za-z_][\w$]*)(?!\s*\()(?!\s*\.)/g;
60
+ for (let m = re.exec(expr); m; m = re.exec(expr)) {
61
+ const name = m[1] !== undefined ? m[1].replace(/""/g, '"') : m[2];
62
+ if (name !== undefined && known.has(m[1] !== undefined ? name : name.toLowerCase())) found.add(m[1] !== undefined ? name : name.toLowerCase());
63
+ }
64
+ return [...found];
65
+ }
66
+
67
+ /** A check constraint's default name: the column when the expression reads one, else just the table. */
68
+ export function checkName(table: string, expr: string, columns: readonly string[], taken: ReadonlySet<string>): string {
69
+ const refs = referencedColumns(expr, columns);
70
+ return chooseName(table, refs.length === 1 ? refs[0] : undefined, "check", taken);
71
+ }