@intentius/chant-lexicon-sql 0.124.30 → 0.124.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/dist/change-set.js +250 -0
  2. package/dist/clickhouse/access.js +722 -0
  3. package/dist/clickhouse/apply/apply.js +243 -0
  4. package/dist/clickhouse/apply/mutations.js +66 -0
  5. package/dist/clickhouse/apply/statements.js +545 -0
  6. package/dist/clickhouse/catalog-types.js +7 -0
  7. package/dist/clickhouse/container.js +152 -0
  8. package/dist/clickhouse/engine-syntax.js +159 -0
  9. package/dist/clickhouse/entities.js +593 -0
  10. package/dist/clickhouse/expressions.js +107 -0
  11. package/dist/clickhouse/http.js +55 -0
  12. package/dist/clickhouse/import/generator.js +192 -0
  13. package/dist/clickhouse/import/inlined.js +207 -0
  14. package/dist/clickhouse/import/ir.js +160 -0
  15. package/dist/clickhouse/import/live-export.js +131 -0
  16. package/dist/clickhouse/import/parser.js +17 -0
  17. package/dist/clickhouse/live/bind.js +116 -0
  18. package/dist/clickhouse/live/catalog.js +223 -0
  19. package/dist/clickhouse/live/describe-resources.js +91 -0
  20. package/dist/clickhouse/live/undeclarable.js +59 -0
  21. package/dist/clickhouse/overlays/codecs.js +57 -0
  22. package/dist/clickhouse/overlays/engines.js +101 -0
  23. package/dist/clickhouse/overlays/kinds.js +1 -0
  24. package/dist/clickhouse/overlays/projections.js +26 -0
  25. package/dist/clickhouse/overlays/skip-indexes.js +40 -0
  26. package/dist/clickhouse/overlays/ttl.js +34 -0
  27. package/dist/clickhouse/overlays/types.js +59 -0
  28. package/dist/clickhouse/ownership.js +83 -0
  29. package/dist/clickhouse/parser.js +674 -0
  30. package/dist/clickhouse/plan/commands.js +159 -0
  31. package/dist/clickhouse/plan/deep.js +345 -0
  32. package/dist/clickhouse/plan/diff.js +312 -0
  33. package/dist/clickhouse/plan/disruption.js +73 -0
  34. package/dist/clickhouse/plan/normalize.js +545 -0
  35. package/dist/clickhouse/plan/rebuild-handoff.js +57 -0
  36. package/dist/clickhouse/plan/report.js +26 -0
  37. package/dist/clickhouse/plan/rules.js +72 -0
  38. package/dist/clickhouse/plan/schema.js +67 -0
  39. package/dist/clickhouse/plan/server-format.js +35 -0
  40. package/dist/clickhouse/rebuild/backfill.js +265 -0
  41. package/dist/clickhouse/rebuild/engines.js +146 -0
  42. package/dist/clickhouse/rebuild/observe.js +260 -0
  43. package/dist/clickhouse/rebuild/op.js +152 -0
  44. package/dist/clickhouse/rebuild/partitions.js +65 -0
  45. package/dist/clickhouse/rebuild/receipts.js +120 -0
  46. package/dist/clickhouse/rebuild/replicas.js +121 -0
  47. package/dist/clickhouse/rebuild/shards.js +76 -0
  48. package/dist/clickhouse/rebuild/steps.js +383 -0
  49. package/dist/clickhouse/rebuild/verify.js +279 -0
  50. package/dist/clickhouse/row-types.js +151 -0
  51. package/dist/clickhouse/testing/fake-server.js +47 -0
  52. package/dist/clickhouse/testing/roundtrip-schema.js +25 -0
  53. package/dist/clickhouse/testing/writable-server.js +123 -0
  54. package/dist/clickhouse/tokens.js +23 -0
  55. package/dist/clickhouse/topology.js +463 -0
  56. package/dist/clickhouse/type-syntax.js +73 -0
  57. package/dist/clickhouse.js +25 -0
  58. package/dist/codegen/classifier-page.js +93 -0
  59. package/dist/codegen/clickhouse-module.js +249 -0
  60. package/dist/codegen/coverage-page.js +70 -0
  61. package/dist/codegen/docs-cli.js +3 -0
  62. package/dist/codegen/docs.js +222 -0
  63. package/dist/codegen/generate-cli.js +13 -0
  64. package/dist/codegen/generate.js +97 -0
  65. package/dist/codegen/package.js +28 -0
  66. package/dist/codegen/postgres-classifier-page.js +95 -0
  67. package/dist/codegen/postgres-module.js +418 -0
  68. package/dist/codegen/registry.js +52 -0
  69. package/dist/composites/audit-log-table.js +35 -0
  70. package/dist/composites/catalog.js +524 -0
  71. package/dist/composites/cdc-mirror.js +32 -0
  72. package/dist/composites/clickhouse.js +6 -0
  73. package/dist/composites/events-table.js +26 -0
  74. package/dist/composites/index.js +8 -0
  75. package/dist/composites/join-table.js +25 -0
  76. package/dist/composites/postgres.js +6 -0
  77. package/dist/composites/refreshed-view.js +24 -0
  78. package/dist/composites/replacing-table.js +28 -0
  79. package/dist/composites/rollup-view.js +29 -0
  80. package/dist/composites/sharded-table.js +27 -0
  81. package/dist/composites/soft-delete-table.js +34 -0
  82. package/dist/composites/tenant-table.js +26 -0
  83. package/dist/composites/typed.js +8 -0
  84. package/dist/config.js +206 -0
  85. package/dist/core/apply.js +93 -0
  86. package/dist/core/classifier.js +115 -0
  87. package/dist/core/container.js +69 -0
  88. package/dist/core/cursor.js +261 -0
  89. package/dist/core/diff.js +48 -0
  90. package/dist/core/entity.js +58 -0
  91. package/dist/core/find-templates.js +118 -0
  92. package/dist/core/foreign-tables.js +27 -0
  93. package/dist/core/handoff.js +32 -0
  94. package/dist/core/index.js +64 -0
  95. package/dist/core/interpolation.js +191 -0
  96. package/dist/core/normalize.js +43 -0
  97. package/dist/core/ownership.js +119 -0
  98. package/dist/core/receipts.js +22 -0
  99. package/dist/core/references.js +61 -0
  100. package/dist/core/statements.js +42 -0
  101. package/dist/core/template.js +79 -0
  102. package/dist/core/testing/http-stub.js +21 -0
  103. package/dist/core/tokens.js +202 -0
  104. package/dist/core/typed.js +40 -0
  105. package/dist/core/undeclarable.js +46 -0
  106. package/dist/coverage.js +161 -0
  107. package/dist/detect.js +32 -0
  108. package/dist/dialects.js +18 -0
  109. package/dist/files/build-root.js +72 -0
  110. package/dist/files/clickhouse.js +138 -0
  111. package/dist/files/common.js +58 -0
  112. package/dist/files/postgres.js +389 -0
  113. package/dist/generated/clickhouse-types.js +5 -0
  114. package/dist/generated/clickhouse.js +4361 -0
  115. package/dist/generated/postgres-types.js +227 -0
  116. package/dist/generated/postgres.js +3667 -0
  117. package/dist/import-generator.js +14 -0
  118. package/dist/import-parser.js +71 -0
  119. package/dist/index.js +24 -0
  120. package/dist/init-templates.js +290 -0
  121. package/dist/integrity.json +2 -2
  122. package/dist/lint/audit-catalog.js +31 -0
  123. package/dist/lint/audit-lineage.js +22 -0
  124. package/dist/lint/post-synth/audit-entries.js +34 -0
  125. package/dist/lint/post-synth/clickhouse-helpers.js +79 -0
  126. package/dist/lint/post-synth/clickhouse-type-check.js +162 -0
  127. package/dist/lint/post-synth/index.js +112 -0
  128. package/dist/lint/post-synth/postgres-audit-entries.js +35 -0
  129. package/dist/lint/post-synth/postgres-helpers.js +65 -0
  130. package/dist/lint/post-synth/postgres-names.js +507 -0
  131. package/dist/lint/post-synth/sql-helpers.js +23 -0
  132. package/dist/lint/post-synth/sql101.js +59 -0
  133. package/dist/lint/post-synth/sql102.js +34 -0
  134. package/dist/lint/post-synth/sqlch101.js +34 -0
  135. package/dist/lint/post-synth/sqlch102.js +23 -0
  136. package/dist/lint/post-synth/sqlch103.js +45 -0
  137. package/dist/lint/post-synth/sqlch104.js +41 -0
  138. package/dist/lint/post-synth/sqlch105.js +36 -0
  139. package/dist/lint/post-synth/sqlch106.js +27 -0
  140. package/dist/lint/post-synth/sqlch107.js +39 -0
  141. package/dist/lint/post-synth/sqlch108.js +28 -0
  142. package/dist/lint/post-synth/sqlch109.js +21 -0
  143. package/dist/lint/post-synth/sqlch110.js +31 -0
  144. package/dist/lint/post-synth/sqlch111.js +41 -0
  145. package/dist/lint/post-synth/sqlch112.js +28 -0
  146. package/dist/lint/post-synth/sqlch113.js +19 -0
  147. package/dist/lint/post-synth/sqlch114.js +26 -0
  148. package/dist/lint/post-synth/sqlch115.js +30 -0
  149. package/dist/lint/post-synth/sqlch116.js +22 -0
  150. package/dist/lint/post-synth/sqlch117.js +26 -0
  151. package/dist/lint/post-synth/sqlch118.js +27 -0
  152. package/dist/lint/post-synth/sqlch119.js +32 -0
  153. package/dist/lint/post-synth/sqlch120.js +20 -0
  154. package/dist/lint/post-synth/sqlch121.js +11 -0
  155. package/dist/lint/post-synth/sqlch122.js +11 -0
  156. package/dist/lint/post-synth/sqlch123.js +10 -0
  157. package/dist/lint/post-synth/sqlch124.js +21 -0
  158. package/dist/lint/post-synth/sqlch125.js +55 -0
  159. package/dist/lint/post-synth/sqlch126.js +62 -0
  160. package/dist/lint/post-synth/sqlch127.js +132 -0
  161. package/dist/lint/post-synth/sqlpg101.js +25 -0
  162. package/dist/lint/post-synth/sqlpg102.js +35 -0
  163. package/dist/lint/post-synth/sqlpg103.js +23 -0
  164. package/dist/lint/post-synth/sqlpg104.js +23 -0
  165. package/dist/lint/post-synth/sqlpg105.js +22 -0
  166. package/dist/lint/post-synth/sqlpg106.js +22 -0
  167. package/dist/lint/post-synth/sqlpg107.js +22 -0
  168. package/dist/lint/post-synth/sqlpg108.js +28 -0
  169. package/dist/lint/post-synth/sqlpg109.js +39 -0
  170. package/dist/lint/post-synth/sqlpg110.js +38 -0
  171. package/dist/lint/post-synth/sqlpg111.js +23 -0
  172. package/dist/lint/post-synth/sqlpg112.js +25 -0
  173. package/dist/lint/post-synth/sqlpg113.js +29 -0
  174. package/dist/lint/post-synth/sqlpg114.js +39 -0
  175. package/dist/lint/post-synth/sqlpg115.js +48 -0
  176. package/dist/lint/post-synth/sqlpg116.js +26 -0
  177. package/dist/lint/post-synth/sqlpg117.js +24 -0
  178. package/dist/lint/post-synth/sqlpg118.js +22 -0
  179. package/dist/lint/post-synth/sqlpg119.js +41 -0
  180. package/dist/lint/post-synth/sqlpg120.js +83 -0
  181. package/dist/lint/post-synth/sqlpg121.js +33 -0
  182. package/dist/lint/post-synth/sqlpg122.js +27 -0
  183. package/dist/lint/post-synth/sqlpg123.js +69 -0
  184. package/dist/lint/post-synth/sqlpg124.js +50 -0
  185. package/dist/lint/post-synth/sqlpg125.js +41 -0
  186. package/dist/lint/post-synth/sqlpg126.js +38 -0
  187. package/dist/lint/rules/index.js +12 -0
  188. package/dist/lint/rules/postgres-templates.js +27 -0
  189. package/dist/lint/rules/sqlch001.js +79 -0
  190. package/dist/lint/rules/sqlch002.js +124 -0
  191. package/dist/lint/rules/sqlch003.js +69 -0
  192. package/dist/lint/rules/sqlch004.js +83 -0
  193. package/dist/lint/rules/sqlpg001.js +68 -0
  194. package/dist/lint/rules/sqlpg002.js +69 -0
  195. package/dist/lint/rules/sqlpg003.js +58 -0
  196. package/dist/lint/rules/sqlpg004.js +77 -0
  197. package/dist/lint/rules/sqlpg005.js +86 -0
  198. package/dist/lint/rules/templates.js +25 -0
  199. package/dist/lint/rules/typed-names.js +132 -0
  200. package/dist/live-dialect.js +32 -0
  201. package/dist/lsp/catalog.js +44 -0
  202. package/dist/lsp/completions.js +136 -0
  203. package/dist/lsp/hover.js +115 -0
  204. package/dist/lsp/postgres-catalog.js +176 -0
  205. package/dist/lsp/postgres-template.js +171 -0
  206. package/dist/lsp/postgres.js +266 -0
  207. package/dist/lsp/refs.js +60 -0
  208. package/dist/lsp/registry.d.ts.map +1 -1
  209. package/dist/lsp/registry.js +19 -0
  210. package/dist/lsp/template.js +143 -0
  211. package/dist/manifest.json +1 -1
  212. package/dist/mcp/index.d.ts.map +1 -1
  213. package/dist/mcp/index.js +293 -0
  214. package/dist/mcp/postgres.js +117 -0
  215. package/dist/migration-statements.js +243 -0
  216. package/dist/op/activities/clickhouse-apply.js +83 -0
  217. package/dist/op/activities/clickhouse-emulator.js +53 -0
  218. package/dist/op/activities/clickhouse-rebuild.js +119 -0
  219. package/dist/op/activities/index.js +26 -0
  220. package/dist/op/activities/postgres-apply.js +126 -0
  221. package/dist/op/activities/postgres-emulator.js +70 -0
  222. package/dist/op/activities/postgres-migration.js +156 -0
  223. package/dist/op/activities/sql-exec.js +57 -0
  224. package/dist/op/activity-contracts.js +103 -0
  225. package/dist/package-cli.js +14 -0
  226. package/dist/plugin.js +259 -0
  227. package/dist/postgres/access/acl.js +344 -0
  228. package/dist/postgres/access/changes.js +30 -0
  229. package/dist/postgres/access/import.js +61 -0
  230. package/dist/postgres/access/live.js +96 -0
  231. package/dist/postgres/apply/apply.js +557 -0
  232. package/dist/postgres/apply/statements.js +882 -0
  233. package/dist/postgres/catalog-types.js +18 -0
  234. package/dist/postgres/container.js +58 -0
  235. package/dist/postgres/entities.js +1390 -0
  236. package/dist/postgres/entity-types.js +28 -0
  237. package/dist/postgres/import/generator.js +283 -0
  238. package/dist/postgres/import/ir.js +53 -0
  239. package/dist/postgres/import/live-export.js +83 -0
  240. package/dist/postgres/keywords.js +32 -0
  241. package/dist/postgres/live/bind.js +130 -0
  242. package/dist/postgres/live/catalog-kinds.js +301 -0
  243. package/dist/postgres/live/catalog.js +644 -0
  244. package/dist/postgres/live/client.js +151 -0
  245. package/dist/postgres/live/describe-resources.js +135 -0
  246. package/dist/postgres/live/undeclarable.js +111 -0
  247. package/dist/postgres/migrate/backfill.js +170 -0
  248. package/dist/postgres/migrate/batches.js +122 -0
  249. package/dist/postgres/migrate/carry.js +518 -0
  250. package/dist/postgres/migrate/handoff.js +59 -0
  251. package/dist/postgres/migrate/names.js +61 -0
  252. package/dist/postgres/migrate/observe.js +360 -0
  253. package/dist/postgres/migrate/op.js +178 -0
  254. package/dist/postgres/migrate/receipts.js +117 -0
  255. package/dist/postgres/migrate/replication.js +106 -0
  256. package/dist/postgres/migrate/steps.js +514 -0
  257. package/dist/postgres/overlays/columns.js +31 -0
  258. package/dist/postgres/overlays/constraints.js +52 -0
  259. package/dist/postgres/overlays/extension-objects.js +88 -0
  260. package/dist/postgres/overlays/indexes.js +25 -0
  261. package/dist/postgres/overlays/kinds.js +2 -0
  262. package/dist/postgres/overlays/partitioning.js +23 -0
  263. package/dist/postgres/overlays/relations.js +25 -0
  264. package/dist/postgres/overlays/storage.js +65 -0
  265. package/dist/postgres/overlays/type-ddl.js +36 -0
  266. package/dist/postgres/overlays/types.js +74 -0
  267. package/dist/postgres/parser.js +1824 -0
  268. package/dist/postgres/plan/commands.js +137 -0
  269. package/dist/postgres/plan/deep.js +324 -0
  270. package/dist/postgres/plan/diff.js +499 -0
  271. package/dist/postgres/plan/disruption.js +64 -0
  272. package/dist/postgres/plan/normalize.js +889 -0
  273. package/dist/postgres/plan/report.js +21 -0
  274. package/dist/postgres/plan/rules.js +115 -0
  275. package/dist/postgres/plan/schema.js +105 -0
  276. package/dist/postgres/plan/server-normalize.js +166 -0
  277. package/dist/postgres/providers/aurora.js +44 -0
  278. package/dist/postgres/providers/azure.js +53 -0
  279. package/dist/postgres/providers/cloud-sql.js +41 -0
  280. package/dist/postgres/providers/index.js +73 -0
  281. package/dist/postgres/providers/neon.js +44 -0
  282. package/dist/postgres/providers/rds.js +47 -0
  283. package/dist/postgres/providers/supabase.js +45 -0
  284. package/dist/postgres/providers/types.js +21 -0
  285. package/dist/postgres/row-types.js +107 -0
  286. package/dist/postgres/testing/fake-client.js +34 -0
  287. package/dist/postgres/testing/migration.js +48 -0
  288. package/dist/postgres/testing/roundtrip-schema.js +64 -0
  289. package/dist/postgres/testing/server.js +47 -0
  290. package/dist/postgres/testing/writable-server.js +160 -0
  291. package/dist/postgres/tokens.js +41 -0
  292. package/dist/postgres.js +15 -0
  293. package/dist/read/clickhouse.js +82 -0
  294. package/dist/read/objects.js +44 -0
  295. package/dist/read/postgres.js +104 -0
  296. package/dist/read.js +52 -0
  297. package/dist/receipts.js +207 -0
  298. package/dist/serializer.js +172 -0
  299. package/dist/skill-defs.js +93 -0
  300. package/dist/spec/catalog.js +191 -0
  301. package/dist/spec/fetch.d.ts.map +1 -1
  302. package/dist/spec/fetch.js +90 -0
  303. package/dist/spec/pin.js +42 -0
  304. package/dist/spec/postgres-catalog.js +171 -0
  305. package/dist/spec/postgres-fetch.js +91 -0
  306. package/dist/spec/postgres-pin.js +82 -0
  307. package/dist/sql-files.js +206 -0
  308. package/dist/sql-types-command.js +97 -0
  309. package/dist/sql-types.js +323 -0
  310. package/dist/token-source.js +156 -0
  311. package/dist/validate-cli.js +5 -0
  312. package/dist/validate.js +151 -0
  313. package/dist/workspace-outputs.js +191 -0
  314. package/package.json +7 -7
  315. package/src/codegen/package.ts +2 -2
  316. package/src/lint/rules/typescript-7.test.ts +2 -1
  317. package/src/lsp/catalog.ts +2 -2
  318. package/src/lsp/postgres-catalog.ts +2 -2
  319. package/src/lsp/registry.ts +2 -1
  320. package/src/mcp/index.ts +4 -2
  321. package/src/spec/fetch.ts +2 -2
  322. package/src/spec/postgres-fetch.ts +2 -2
package/dist/plugin.js ADDED
@@ -0,0 +1,259 @@
1
+ /**
2
+ * The sql lexicon plugin.
3
+ *
4
+ * One lexicon, a subpath per database dialect (#3047, "Packaging"): ClickHouse
5
+ * at `@intentius/chant-lexicon-sql/clickhouse`, Postgres at
6
+ * `@intentius/chant-lexicon-sql/postgres` (#3289). Generation reads
7
+ * the pinned ClickHouse server's catalog (src/spec/), and the rest of the
8
+ * plugin is the shared machinery every dialect plugs into.
9
+ */
10
+ import { sqlSerializer } from "./serializer.js";
11
+ import { rules } from "./lint/rules/index.js";
12
+ import { postSynthChecks as postSynthCheckList } from "./lint/post-synth/index.js";
13
+ import { sqlAuditCatalog } from "./lint/audit-catalog.js";
14
+ import { completions } from "./lsp/completions.js";
15
+ import { hover } from "./lsp/hover.js";
16
+ import { sqlMcpResources, sqlMcpTools } from "./mcp/index.js";
17
+ import { sqlConfigSchema } from "./config.js";
18
+ import { SqlFileParser, dialectOption } from "./import-parser.js";
19
+ import { sqlTemplateGenerator } from "./import-generator.js";
20
+ import { sqlCommands } from "./clickhouse/plan/commands.js";
21
+ import { sqlDeepNormalizationHooks } from "./clickhouse/plan/deep.js";
22
+ import { versionFromReleaseTag } from "./spec/pin.js";
23
+ import { POSTGRES_MAJORS, postgresUpstreamPin } from "./spec/postgres-pin.js";
24
+ import { SQL_OWNERSHIP_CHANNEL } from "./clickhouse/ownership.js";
25
+ import { CLICKHOUSE_EMULATOR } from "./op/activities/clickhouse-emulator.js";
26
+ import { POSTGRES_EMULATOR } from "./op/activities/postgres-emulator.js";
27
+ import { sqlSkills } from "./skill-defs.js";
28
+ import { detectTemplate } from "./detect.js";
29
+ import { sqlWorkspaceOutputs } from "./workspace-outputs.js";
30
+ import { CDC_TEMPLATE, DEFAULT_TEMPLATE, EVENTS_TEMPLATE, POSTGRES_EVENTS_TEMPLATE, POSTGRES_TEMPLATE, POSTGRES_TENANT_TEMPLATE } from "./init-templates.js";
31
+ import { compositeCatalog } from "./composites/catalog.js";
32
+ export const sqlPlugin = {
33
+ name: "sql",
34
+ serializer: sqlSerializer,
35
+ configSchema: sqlConfigSchema,
36
+ /** The pinned clickhouse-server (#3208) and postgres (#3280), for `chant emulator up`. */
37
+ emulator: [CLICKHOUSE_EMULATOR, POSTGRES_EMULATOR],
38
+ /** chant's marker is a trailer on the object's comment (#3208, ./clickhouse/ownership.ts). */
39
+ ownershipChannel: SQL_OWNERSHIP_CHANNEL,
40
+ async generate(options) {
41
+ const { generate, writeGeneratedFiles } = await import("./codegen/generate.js");
42
+ writeGeneratedFiles(await generate(options));
43
+ },
44
+ async validate(_options) {
45
+ const { validate } = await import("./validate.js");
46
+ const { printValidationResult } = await import("@intentius/chant/codegen/validate");
47
+ printValidationResult(await validate());
48
+ },
49
+ async coverage(options) {
50
+ const { analyze, printCoverage } = await import("./coverage.js");
51
+ printCoverage(analyze(), options);
52
+ },
53
+ async package(options) {
54
+ const { packageLexicon } = await import("./codegen/package.js");
55
+ const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
56
+ const { join, dirname } = await import("path");
57
+ const { fileURLToPath } = await import("url");
58
+ const { spec, stats } = await packageLexicon(options);
59
+ writeBundleSpec(spec, join(dirname(dirname(fileURLToPath(import.meta.url))), "dist"));
60
+ console.error(`Packaged ${stats.ruleCount} rules, ${stats.skillCount} skills`);
61
+ },
62
+ lintRules() {
63
+ return rules;
64
+ },
65
+ postSynthChecks() {
66
+ return postSynthCheckList;
67
+ },
68
+ auditCatalog() {
69
+ return sqlAuditCatalog;
70
+ },
71
+ /**
72
+ * The tags fold: `chant build` reduces a `table`, `view` or other template
73
+ * to the entity the tag builds, without running the file. An intrinsic is
74
+ * registered by name and folds as the function the file imports, so
75
+ * `table` from `/clickhouse` and `table` from `/postgres` are each their
76
+ * own dialect's. `literal(...)` folds as a call so it can sit inside a tag.
77
+ */
78
+ intrinsics() {
79
+ return [
80
+ { name: "database", isTag: true, description: "A ClickHouse CREATE DATABASE, parsed into a database entity" },
81
+ { name: "table", isTag: true, description: "A ClickHouse or Postgres CREATE TABLE, parsed into a table entity" },
82
+ {
83
+ name: "view",
84
+ isTag: true,
85
+ description: "A CREATE VIEW or CREATE MATERIALIZED VIEW, parsed into a view entity with its lineage",
86
+ },
87
+ { name: "dictionary", isTag: true, description: "A ClickHouse CREATE DICTIONARY, parsed into a dictionary entity with its attributes, key, source and layout" },
88
+ { name: "user", isTag: true, description: "A ClickHouse CREATE USER, parsed into a user entity; its password is the environment's" },
89
+ { name: "quota", isTag: true, description: "A ClickHouse CREATE QUOTA, parsed into a quota entity with its key, its intervals' limits and whom it applies to" },
90
+ { name: "settingsProfile", isTag: true, description: "A ClickHouse CREATE SETTINGS PROFILE, parsed into a settings profile entity with its settings, constraints and inherited profiles" },
91
+ { name: "schema", isTag: true, description: "A Postgres CREATE SCHEMA, parsed into a schema entity" },
92
+ { name: "index", isTag: true, description: "A Postgres CREATE INDEX, parsed into an index entity" },
93
+ { name: "sequence", isTag: true, description: "A Postgres CREATE SEQUENCE, parsed into a sequence entity" },
94
+ { name: "type", isTag: true, description: "A Postgres CREATE TYPE ... AS ENUM, AS (...) or AS RANGE, parsed into an enum, composite or range type entity" },
95
+ { name: "domain", isTag: true, description: "A Postgres CREATE DOMAIN, parsed into a domain entity" },
96
+ { name: "extension", isTag: true, description: "A Postgres CREATE EXTENSION, parsed into an extension entity" },
97
+ { name: "func", isTag: true, description: "A Postgres CREATE FUNCTION, parsed into a function entity with its body kept verbatim, or a ClickHouse CREATE FUNCTION (a named lambda)" },
98
+ { name: "procedure", isTag: true, description: "A Postgres CREATE PROCEDURE, parsed into a procedure entity with its body kept verbatim" },
99
+ { name: "aggregate", isTag: true, description: "A Postgres CREATE AGGREGATE, parsed into an aggregate entity identified by its argument types" },
100
+ { name: "trigger", isTag: true, description: "A Postgres CREATE TRIGGER, parsed into a trigger entity on its table" },
101
+ { name: "eventTrigger", isTag: true, description: "A Postgres CREATE EVENT TRIGGER, parsed into the database's event trigger entity" },
102
+ { name: "collation", isTag: true, description: "A Postgres CREATE COLLATION, parsed into a collation entity" },
103
+ { name: "cast", isTag: true, description: "A Postgres CREATE CAST, parsed into a cast entity named by its two types" },
104
+ { 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" },
105
+ { name: "role", isTag: true, description: "A Postgres CREATE ROLE without a password or memberships, parsed into a role entity, or a ClickHouse CREATE ROLE" },
106
+ { name: "grant", isTag: true, description: "A Postgres GRANT, REVOKE or ALTER DEFAULT PRIVILEGES, parsed into the privileges it declares, or a ClickHouse GRANT of privileges or roles" },
107
+ {
108
+ name: "literal",
109
+ isTag: false,
110
+ foldsAsCall: true,
111
+ description: "A quoted, escaped SQL string literal, for a string interpolated as a value rather than as SQL text",
112
+ },
113
+ ];
114
+ },
115
+ skills: sqlSkills,
116
+ /** The ClickHouse and Postgres composites (./composites), as the catalog generated from their exports. */
117
+ composites() {
118
+ return compositeCatalog;
119
+ },
120
+ // `chant init --lexicon sql [--template events|cdc|postgres|postgres-tenant|postgres-events]`; see ./init-templates.ts.
121
+ initTemplates(template) {
122
+ if (template === "events")
123
+ return EVENTS_TEMPLATE;
124
+ if (template === "cdc")
125
+ return CDC_TEMPLATE;
126
+ if (template === "postgres")
127
+ return POSTGRES_TEMPLATE;
128
+ if (template === "postgres-tenant")
129
+ return POSTGRES_TENANT_TEMPLATE;
130
+ if (template === "postgres-events")
131
+ return POSTGRES_EVENTS_TEMPLATE;
132
+ return DEFAULT_TEMPLATE;
133
+ },
134
+ detectTemplate(data) {
135
+ return detectTemplate(data);
136
+ },
137
+ completionProvider(ctx) {
138
+ return completions(ctx);
139
+ },
140
+ hoverProvider(ctx) {
141
+ return hover(ctx);
142
+ },
143
+ mcpTools() {
144
+ return sqlMcpTools();
145
+ },
146
+ mcpResources() {
147
+ return sqlMcpResources();
148
+ },
149
+ /** `chant import schema.sql`: a file of Postgres or ClickHouse DDL (`./import-parser.ts`). */
150
+ templateParser(options) {
151
+ return new SqlFileParser(dialectOption(options), { skipUndeclarable: options?.skipUndeclarable === true });
152
+ },
153
+ parserOptions() {
154
+ return [
155
+ {
156
+ name: "dialect",
157
+ type: "string",
158
+ description: "the DDL's dialect, clickhouse or postgres; read off its statements when omitted",
159
+ },
160
+ {
161
+ name: "skipUndeclarable",
162
+ type: "boolean",
163
+ description: "import the rest of the file, and list each CREATE of a kind chant cannot declare (an aggregate, a cast, a quota) as a warning instead of failing",
164
+ },
165
+ ];
166
+ },
167
+ /**
168
+ * The `.sql` files in the source directory, read into declarations beside
169
+ * the tagged templates (`./files/build-root.ts`, #3646).
170
+ */
171
+ async buildRoots(ctx) {
172
+ const { sqlFilesBuildRoot } = await import("./files/build-root.js");
173
+ return sqlFilesBuildRoot(ctx);
174
+ },
175
+ /**
176
+ * Each table and view, and each of their columns, as workspace outputs
177
+ * (`./workspace-outputs.ts`, #3931): `public.orders`,
178
+ * `public.orders.customer_id`. Another member's link to a dropped or
179
+ * renamed column then fails `chant workspace check`.
180
+ */
181
+ workspaceOutputs(entities, unread) {
182
+ return sqlWorkspaceOutputs(entities, unread);
183
+ },
184
+ /** One generator for both dialects: each IR resource's type says whose declarations it is. */
185
+ templateGenerator() {
186
+ return sqlTemplateGenerator;
187
+ },
188
+ /**
189
+ * Which declared objects exist on the environment's server
190
+ * (`sql.profiles.<env>`, else `CLICKHOUSE_URL` or `POSTGRES_URL`). The
191
+ * declarations' dialect picks the reader; node-postgres loads only for a
192
+ * Postgres one.
193
+ */
194
+ async describeResources(options) {
195
+ const { dialectOfEntities } = await import("./live-dialect.js");
196
+ if (dialectOfEntities(options.entities) === "postgres") {
197
+ const { describeResources } = await import("./postgres/live/describe-resources.js");
198
+ return describeResources(options);
199
+ }
200
+ const { describeResources } = await import("./clickhouse/live/describe-resources.js");
201
+ return describeResources(options);
202
+ },
203
+ /** `chant import --from <env>`: the server's schema as declarations, from `SHOW CREATE` or the Postgres catalog's printers. */
204
+ async exportResources(options) {
205
+ const { resolveBindingDialect } = await import("./live-dialect.js");
206
+ if ((await resolveBindingDialect(options)) === "postgres") {
207
+ const { exportResources } = await import("./postgres/import/live-export.js");
208
+ return exportResources(options);
209
+ }
210
+ const { exportResources } = await import("./clickhouse/import/live-export.js");
211
+ return exportResources(options);
212
+ },
213
+ /** Each declared object's live definition, in the declaration's own shape. */
214
+ async observeResourcesDeep(options) {
215
+ const { dialectOfEntities } = await import("./live-dialect.js");
216
+ if (dialectOfEntities(options.entities) === "postgres") {
217
+ const { observeResourcesDeep } = await import("./postgres/plan/deep.js");
218
+ return observeResourcesDeep(options);
219
+ }
220
+ const { observeResourcesDeep } = await import("./clickhouse/plan/deep.js");
221
+ return observeResourcesDeep(options);
222
+ },
223
+ deepNormalizationHooks: sqlDeepNormalizationHooks,
224
+ /**
225
+ * What an update costs. ClickHouse: metadata in-place, a background rewrite
226
+ * rolling, a rebuild replace. Postgres: metadata in-place, a validation, a
227
+ * CONCURRENTLY build or an ACCESS EXCLUSIVE rewrite rolling, expand and
228
+ * contract replace. Each dialect answers for its own types.
229
+ */
230
+ async classifyDisruption(options) {
231
+ const { classifyDisruption } = await import("./clickhouse/plan/disruption.js");
232
+ const { classifyPgDisruption } = await import("./postgres/plan/disruption.js");
233
+ return { ...classifyDisruption(options), ...classifyPgDisruption(options) };
234
+ },
235
+ /** `chant sql diff` and `chant sql plan`: schema changes, classified. */
236
+ commands() {
237
+ return sqlCommands;
238
+ },
239
+ async docs(options) {
240
+ const { generateDocs } = await import("./codegen/docs.js");
241
+ await generateDocs(options);
242
+ },
243
+ /**
244
+ * The pin is a ClickHouse LTS release. GitHub tags them `v26.8.15.10-lts`;
245
+ * the constant holds `26.8.15.10`, which is also the docker tag. A move also
246
+ * needs `CLICKHOUSE_IMAGE_DIGEST` updated: generation refuses a server whose
247
+ * version is not the pin.
248
+ */
249
+ upstreamPin: {
250
+ label: "clickhouse",
251
+ file: "src/spec/pin.ts",
252
+ pattern: /export const CLICKHOUSE_VERSION\s*=\s*"([^"]+)"/,
253
+ replace: (v, line) => line.replace(/= "[^"]+"/, `= "${versionFromReleaseTag(v)}"`),
254
+ alsoMoves: "CLICKHOUSE_IMAGE_DIGEST in src/spec/pin.ts moves with the version: set CLICKHOUSE_VERSION to the new release, set the digest to that tag's image digest (docker buildx imagetools inspect clickhouse/clickhouse-server:<version>), then run `chant dev generate` and read the diff of src/spec/clickhouse-catalog.snapshot.json.",
255
+ upstream: { owner: "ClickHouse", repo: "ClickHouse", kind: "releases", tagSuffix: "-lts" },
256
+ },
257
+ /** The Postgres servers, one pin per major (`postgres-14` to `postgres-18`). */
258
+ upstreamPins: POSTGRES_MAJORS.map(postgresUpstreamPin),
259
+ };
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Access as chant compares it (#3681): the privileges each role holds on each
3
+ * object, and the default privileges objects are created with.
4
+ *
5
+ * A `grant` declaration is a statement, not an object: `GRANT SELECT ON a, b
6
+ * TO r1, r2` is four privileges, and the catalog keeps no statement, only
7
+ * each object's access control list. So declarations and the server are both
8
+ * brought to the same form, one entry per object (or column) and grantee
9
+ * holding a set of privileges, and those entries are compared.
10
+ *
11
+ * What a declared object's entries should be:
12
+ *
13
+ * - what Postgres gives a new object of its kind, the owner's own privileges
14
+ * left out (a function or procedure: EXECUTE to PUBLIC; anything else:
15
+ * nothing);
16
+ * - with the declared default privileges for objects of that kind created by
17
+ * the role that applies, globally and in the object's schema, since chant
18
+ * creates it as that role;
19
+ * - with every declared GRANT added and every declared REVOKE taken away, in
20
+ * the build's order.
21
+ *
22
+ * The owner's own privileges are never compared: the owner has them by
23
+ * owning the object. A grant on an object the build does not declare (the
24
+ * `public` schema) is compared too, from what Postgres gives a new object.
25
+ *
26
+ * Default privileges are compared for the role that applies (and each role a
27
+ * declaration names with `FOR ROLE`): globally, where none declared means
28
+ * Postgres's own defaults, and per schema, where none declared means none.
29
+ */
30
+ import { quoteIdent } from "../keywords.js";
31
+ import { PG_CLASSIFIER_RULES } from "../plan/rules.js";
32
+ import { classifiedChange } from "../../core/classifier.js";
33
+ const SELF = "";
34
+ /** A target's key: relations (tables, views, sequences) share one namespace, as in the catalog. */
35
+ export function targetKey(t) {
36
+ switch (t.kind) {
37
+ case "schema":
38
+ return `schema ${t.name}`;
39
+ case "table":
40
+ case "sequence":
41
+ return `relation ${t.name}`;
42
+ case "column":
43
+ return `relation ${t.name} (${quoteIdent(t.column ?? "")})`;
44
+ case "function":
45
+ case "procedure":
46
+ return `routine ${t.name}`;
47
+ case "default":
48
+ return `default privileges${t.role ? ` for ${quoteIdent(t.role)}` : ""}${t.schema !== undefined ? ` in ${quoteIdent(t.schema)}` : ""} on ${t.objects}`;
49
+ }
50
+ }
51
+ export const entryKey = (t, grantee) => `${targetKey(t)} TO ${grantee === "public" ? "PUBLIC" : quoteIdent(grantee)}`;
52
+ /** Every privilege of a kind of object, as ALL PRIVILEGES grants them. MAINTAIN is a table's from 17. */
53
+ export function allPrivileges(kind, major) {
54
+ switch (kind) {
55
+ case "table":
56
+ case "tables":
57
+ return ["select", "insert", "update", "delete", "truncate", "references", "trigger", ...(major >= 17 ? ["maintain"] : [])];
58
+ case "sequence":
59
+ case "sequences":
60
+ return ["usage", "select", "update"];
61
+ case "schema":
62
+ case "schemas":
63
+ return ["usage", "create"];
64
+ case "function":
65
+ case "procedure":
66
+ case "functions":
67
+ return ["execute"];
68
+ case "types":
69
+ return ["usage"];
70
+ case "column":
71
+ return ["select", "insert", "update", "references"];
72
+ case "default":
73
+ return [];
74
+ }
75
+ }
76
+ /** What Postgres gives a new object of a kind, the owner's own privileges left out: EXECUTE on a routine and USAGE on a type to PUBLIC. */
77
+ export function builtinEntries(kind) {
78
+ if (kind === "function" || kind === "procedure" || kind === "functions")
79
+ return [{ grantee: "public", privilege: "execute" }];
80
+ if (kind === "types")
81
+ return [{ grantee: "public", privilege: "usage" }];
82
+ return [];
83
+ }
84
+ /** The default-privileges kind an object kind is created as. */
85
+ const defaultObjectsOf = (kind) => kind === "table" ? "tables" : kind === "sequence" ? "sequences" : kind === "function" || kind === "procedure" ? "functions" : kind === "schema" ? "schemas" : undefined;
86
+ function add(state, target, grantee, privilege, grantable) {
87
+ const key = entryKey(target, grantee);
88
+ let e = state.get(key);
89
+ if (!e)
90
+ state.set(key, (e = { target, grantee, privileges: new Map() }));
91
+ e.privileges.set(privilege, grantable || e.privileges.get(privilege) === true);
92
+ }
93
+ function remove(state, target, grantee, privilege, grantOptionOnly) {
94
+ const e = state.get(entryKey(target, grantee));
95
+ if (!e || !e.privileges.has(privilege))
96
+ return;
97
+ if (grantOptionOnly)
98
+ e.privileges.set(privilege, false);
99
+ else
100
+ e.privileges.delete(privilege);
101
+ }
102
+ /** The state with every empty entry left out. */
103
+ const compact = (state) => new Map([...state].filter(([, e]) => e.privileges.size > 0));
104
+ /**
105
+ * The access a set of declarations adds up to. `objects` are canonical, in
106
+ * the build's order, keyed by export name. `self` is the role that applies,
107
+ * when known: a declaration naming it with `FOR ROLE` is its own default
108
+ * privileges, and a grant to it is the owner's, which is never compared.
109
+ */
110
+ export function declaredAccess(objects, opts) {
111
+ const state = new Map();
112
+ const exportsOf = new Map();
113
+ const targets = new Map();
114
+ const relationKind = new Map();
115
+ const routineKind = new Map();
116
+ const forRoles = new Set();
117
+ const roleOf = (r) => (r === undefined || r === opts.self ? SELF : r);
118
+ const note = (key, exportName) => exportsOf.set(key, [...new Set([...(exportsOf.get(key) ?? []), exportName])]);
119
+ // The objects themselves.
120
+ const declaredTargets = [];
121
+ for (const { canonical: o } of objects) {
122
+ const name = `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${quoteIdent(o.name)}`;
123
+ if (o.kind === "schema")
124
+ declaredTargets.push({ kind: "schema", name: quoteIdent(o.name) });
125
+ else if (o.kind === "table" || o.kind === "view" || o.kind === "materializedView") {
126
+ relationKind.set(name, "table");
127
+ declaredTargets.push({ kind: "table", name });
128
+ }
129
+ else if (o.kind === "sequence") {
130
+ relationKind.set(name, "sequence");
131
+ declaredTargets.push({ kind: "sequence", name });
132
+ }
133
+ else if (o.kind === "function" || o.kind === "procedure") {
134
+ routineKind.set(`${name}${o.signature ?? ""}`, o.kind);
135
+ declaredTargets.push({ kind: o.kind, name: `${name}${o.signature ?? ""}` });
136
+ }
137
+ }
138
+ // Default privileges, per role, schema and kind of object.
139
+ const defaults = new Map();
140
+ const declaredDefaults = new Set();
141
+ for (const { key: exportName, canonical: o } of objects) {
142
+ if (o.kind !== "defaultPrivileges")
143
+ continue;
144
+ const g = o.fields.grant;
145
+ const on = g.on;
146
+ for (const r of g.forRoles ?? [undefined]) {
147
+ const role = roleOf(r);
148
+ if (role !== SELF)
149
+ forRoles.add(role);
150
+ for (const schema of g.inSchemas ?? [undefined]) {
151
+ const target = { kind: "default", name: "", role, ...(schema !== undefined ? { schema } : {}), objects: on };
152
+ const tk = targetKey(target);
153
+ if (!declaredDefaults.has(tk)) {
154
+ declaredDefaults.add(tk);
155
+ targets.set(tk, target);
156
+ // A global entry starts from Postgres's own defaults; a schema's from nothing.
157
+ if (schema === undefined)
158
+ for (const b of builtinEntries(on))
159
+ add(defaults, target, b.grantee, b.privilege, false);
160
+ }
161
+ for (const grantee of g.grantees) {
162
+ for (const p of g.privileges) {
163
+ for (const privilege of p.privilege === "all" ? allPrivileges(on, opts.major) : [p.privilege]) {
164
+ if (g.action === "grant")
165
+ add(defaults, target, grantee, privilege, g.withGrantOption === true);
166
+ else
167
+ remove(defaults, target, grantee, privilege, g.grantOptionFor === true);
168
+ }
169
+ }
170
+ note(entryKey(target, grantee), exportName);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ for (const [k, e] of defaults)
176
+ state.set(k, e);
177
+ /** What an object chant creates is given: Postgres's defaults, or the declared global ones, and the declared ones for its schema. */
178
+ const created = (t, schema) => {
179
+ const objectsKind = defaultObjectsOf(t.kind);
180
+ const global = objectsKind ? [...defaults.values()].filter((e) => e.target.role === SELF && e.target.schema === undefined && e.target.objects === objectsKind) : [];
181
+ const anyGlobal = objectsKind !== undefined && declaredDefaults.has(targetKey({ kind: "default", name: "", role: SELF, objects: objectsKind }));
182
+ if (!anyGlobal)
183
+ for (const b of builtinEntries(t.kind))
184
+ add(state, t, b.grantee, b.privilege, false);
185
+ for (const e of global)
186
+ for (const [p, g] of e.privileges)
187
+ add(state, t, e.grantee, p, g);
188
+ if (objectsKind && schema !== undefined && objectsKind !== "schemas") {
189
+ for (const e of defaults.values()) {
190
+ if (e.target.role === SELF && e.target.schema === schema && e.target.objects === objectsKind)
191
+ for (const [p, g] of e.privileges)
192
+ add(state, t, e.grantee, p, g);
193
+ }
194
+ }
195
+ };
196
+ for (const t of declaredTargets) {
197
+ targets.set(targetKey(t), t);
198
+ const schema = t.kind === "schema" ? undefined : unquotedSchema(t.name);
199
+ created(t, schema);
200
+ }
201
+ // Grants and revokes, in the build's order.
202
+ for (const { key: exportName, canonical: o } of objects) {
203
+ if (o.kind !== "grant")
204
+ continue;
205
+ const g = o.fields.grant;
206
+ for (const name of g.objects ?? []) {
207
+ const kind = g.on === "schema"
208
+ ? "schema"
209
+ : g.on === "sequence"
210
+ ? "sequence"
211
+ : g.on === "table"
212
+ ? (relationKind.get(name) ?? "table")
213
+ : g.on === "procedure"
214
+ ? "procedure"
215
+ : g.on === "function"
216
+ ? "function"
217
+ : (routineKind.get(name) ?? "function");
218
+ const target = { kind, name };
219
+ const tk = targetKey(target);
220
+ if (!targets.has(tk)) {
221
+ // An object the build does not declare starts from what Postgres gives a new one.
222
+ targets.set(tk, target);
223
+ for (const b of builtinEntries(kind))
224
+ add(state, target, b.grantee, b.privilege, false);
225
+ }
226
+ for (const grantee of g.grantees) {
227
+ for (const p of g.privileges) {
228
+ const on = p.columns ? p.columns.map((column) => ({ kind: "column", name, column })) : [target];
229
+ for (const t of on) {
230
+ if (t.kind === "column")
231
+ targets.set(targetKey(t), t);
232
+ for (const privilege of p.privilege === "all" ? allPrivileges(t.kind, opts.major) : [p.privilege]) {
233
+ if (g.action === "grant")
234
+ add(state, t, grantee, privilege, g.withGrantOption === true);
235
+ else
236
+ remove(state, t, grantee, privilege, g.grantOptionFor === true);
237
+ }
238
+ note(entryKey(t, grantee), exportName);
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }
244
+ // A grant to the role that applies is the owner's, which owning gives.
245
+ const out = compact(new Map([...state].filter(([, e]) => opts.self === undefined || e.grantee !== opts.self)));
246
+ return { state: out, targets: [...targets.values()], exportsOf, forRoles: [...forRoles].sort() };
247
+ }
248
+ /** The schema of a canonical qualified name, unquoted. */
249
+ function unquotedSchema(name) {
250
+ const m = /^("(?:[^"]|"")*"|[^."]+)\./.exec(name);
251
+ if (!m)
252
+ return undefined;
253
+ return m[1].startsWith('"') ? m[1].slice(1, -1).replace(/""/g, '"') : m[1];
254
+ }
255
+ const show = (privileges) => privileges && privileges.size > 0 ? [...privileges].sort(([a], [b]) => (a < b ? -1 : 1)).map(([p, g]) => `${p}${g ? "*" : ""}`).join(", ") : undefined;
256
+ const ON = { schema: "SCHEMA", table: "TABLE", sequence: "SEQUENCE", function: "FUNCTION", procedure: "PROCEDURE" };
257
+ /** The `ON ...` and grantee of an entry's statements. */
258
+ function onClause(t) {
259
+ if (t.kind === "column" || t.kind === "default")
260
+ return "";
261
+ return `ON ${ON[t.kind]} ${t.name}`;
262
+ }
263
+ const granteeSql = (g) => (g === "public" ? "PUBLIC" : quoteIdent(g));
264
+ /** One GRANT or REVOKE for a set of privileges on one entry's target. */
265
+ function statement(t, grantee, action, privileges, withGrantOption = false) {
266
+ const list = privileges.map((p) => p.toUpperCase()).join(", ");
267
+ const verb = action === "grant" ? "GRANT" : action === "revoke" ? "REVOKE" : "REVOKE GRANT OPTION FOR";
268
+ const to = action === "grant" ? "TO" : "FROM";
269
+ const tail = action === "grant" && withGrantOption ? " WITH GRANT OPTION" : "";
270
+ if (t.kind === "column")
271
+ return `${verb} ${privileges.map((p) => `${p.toUpperCase()} (${quoteIdent(t.column ?? "")})`).join(", ")} ON TABLE ${t.name} ${to} ${granteeSql(grantee)}${tail}`;
272
+ if (t.kind === "default") {
273
+ const head = `ALTER DEFAULT PRIVILEGES${t.role ? ` FOR ROLE ${quoteIdent(t.role)}` : ""}${t.schema !== undefined ? ` IN SCHEMA ${quoteIdent(t.schema)}` : ""}`;
274
+ return `${head} ${verb} ${list} ON ${String(t.objects).toUpperCase()} ${to} ${granteeSql(grantee)}${tail}`;
275
+ }
276
+ return `${verb} ${list} ${onClause(t)} ${to} ${granteeSql(grantee)}${tail}`;
277
+ }
278
+ /**
279
+ * The changes from `before` to `after`, one per entry and direction, each
280
+ * with its statements: revokes first (a privilege, then a grant option), then
281
+ * grants (a grant option on its own with WITH GRANT OPTION).
282
+ */
283
+ export function diffAccess(before, after, exportsOf = new Map()) {
284
+ const out = [];
285
+ const keys = [...new Set([...before.keys(), ...after.keys()])].sort();
286
+ for (const key of keys) {
287
+ const b = before.get(key);
288
+ const a = after.get(key);
289
+ const target = (a ?? b).target;
290
+ const grantee = (a ?? b).grantee;
291
+ const bp = b?.privileges ?? new Map();
292
+ const ap = a?.privileges ?? new Map();
293
+ const revoke = [...bp.keys()].filter((p) => !ap.has(p)).sort();
294
+ const revokeOption = [...bp].filter(([p, g]) => g && ap.get(p) === false).map(([p]) => p).sort();
295
+ const grant = [...ap].filter(([p, g]) => !bp.has(p) || (g && bp.get(p) === false)).map(([p]) => p).sort();
296
+ if (revoke.length + revokeOption.length + grant.length === 0)
297
+ continue;
298
+ const exports = exportsOf.get(key) ?? [];
299
+ const rule = target.kind === "default" ? "SQLPG298" : revoke.length + revokeOption.length > 0 && grant.length === 0 ? "SQLPG297" : "SQLPG296";
300
+ const sql = [];
301
+ if (revoke.length > 0)
302
+ sql.push(statement(target, grantee, "revoke", revoke));
303
+ if (revokeOption.length > 0)
304
+ sql.push(statement(target, grantee, "revoke-option", revokeOption));
305
+ const plain = grant.filter((p) => ap.get(p) === false);
306
+ const withOption = grant.filter((p) => ap.get(p) === true);
307
+ if (plain.length > 0)
308
+ sql.push(statement(target, grantee, "grant", plain));
309
+ if (withOption.length > 0)
310
+ sql.push(statement(target, grantee, "grant", withOption, true));
311
+ const change = classifiedChange(PG_CLASSIFIER_RULES, `acl ${key}`, "privileges", rule, show(bp), show(ap), revoke.length > 0 && grant.length > 0 ? { note: `revokes ${revoke.join(", ")}` } : {});
312
+ out.push({ change, target, grantee, sql, exports });
313
+ }
314
+ return out;
315
+ }
316
+ /**
317
+ * The entries the server would give each declared object that does not exist
318
+ * yet: Postgres's defaults, or the role's global default privileges, and its
319
+ * default privileges in the object's schema.
320
+ */
321
+ export function predictedAccess(missing, live) {
322
+ const out = new Map();
323
+ const defaults = [...live.state.values()].filter((e) => e.target.kind === "default" && e.target.role === SELF);
324
+ for (const t of missing) {
325
+ const objectsKind = defaultObjectsOf(t.kind);
326
+ if (!objectsKind)
327
+ continue;
328
+ if (!live.globalDefaults.has(objectsKind))
329
+ for (const b of builtinEntries(t.kind))
330
+ add(out, t, b.grantee, b.privilege, false);
331
+ const schema = t.kind === "schema" ? undefined : unquotedSchema(t.name);
332
+ for (const e of defaults) {
333
+ if (e.target.objects !== objectsKind)
334
+ continue;
335
+ if (e.target.schema === undefined || (e.target.schema === schema && objectsKind !== "schemas"))
336
+ for (const [p, g] of e.privileges)
337
+ add(out, t, e.grantee, p, g);
338
+ }
339
+ }
340
+ return compact(out);
341
+ }
342
+ /** Adds an entry, for the live reader. */
343
+ export const addEntry = add;
344
+ export { SELF as APPLYING_ROLE };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The privilege changes a connected server needs (#3681): what `chant sql
3
+ * plan` reports, and what the deep read reports as pending so the plan digest
4
+ * an approval binds to moves with a privilege granted or revoked by hand
5
+ * (#3706).
6
+ */
7
+ import { declaredAccess, diffAccess, predictedAccess, targetKey } from "./acl.js";
8
+ import { readLiveAccess } from "./live.js";
9
+ /** The role a session runs as: the one whose own privileges owning an object gives, and whose default privileges apply. */
10
+ async function currentRole(client) {
11
+ const rows = await client.query("SELECT current_user AS self").catch(() => []);
12
+ return rows[0]?.self ? { self: rows[0].self } : {};
13
+ }
14
+ /**
15
+ * The changes from the access the server holds to the access the
16
+ * declarations (keyed by export name, in the build's order) add up to. An
17
+ * object the server does not hold yet is taken to have what the server gives
18
+ * a new one. `scope` limits the default privileges read to those schemas.
19
+ */
20
+ export async function accessChangesAgainst(client, declared, options) {
21
+ const want = declaredAccess(declared, { major: options.major, ...(await currentRole(client)) });
22
+ const have = await readLiveAccess(client, want.targets, {
23
+ ...(options.scope ? { schemas: options.scope } : {}),
24
+ forRoles: want.forRoles,
25
+ declaredDefaults: want.targets.filter((t) => t.kind === "default"),
26
+ });
27
+ const missing = want.targets.filter((t) => t.kind !== "default" && t.kind !== "column" && !have.present.has(targetKey(t)));
28
+ const now = new Map([...have.state, ...predictedAccess(missing, have)]);
29
+ return diffAccess(now, want.state, want.exportsOf);
30
+ }