@intentius/chant-lexicon-sql 0.124.31 → 0.124.33

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
@@ -0,0 +1,889 @@
1
+ /**
2
+ * Normalization: a Postgres object's definition in a form two definitions can
3
+ * be compared in, so a declaration and what the catalog prints for it are
4
+ * equal when they mean the same thing (#3279, #3275 question 6).
5
+ *
6
+ * The catalog's printers rewrite what they were given. `format_type()` spells
7
+ * every type its SQL way (`timestamptz` is `timestamp with time zone`,
8
+ * `int4` is `integer`, `numeric(12, 2)` is `numeric(12,2)`); `pg_get_expr()`
9
+ * casts every literal (`'placed'::app.order_status`, `0::numeric`, a negative
10
+ * number as `'-1'::integer`), rewrites `x IN ('a', 'b')` as
11
+ * `x = ANY (ARRAY['a'::text, 'b'::text])` and parenthesizes a check;
12
+ * `pg_get_constraintdef()` names every constraint; `pg_get_indexdef()` adds
13
+ * `USING btree`; with an empty `search_path` every name outside `pg_catalog`
14
+ * is schema-qualified. Each of those is undone here, on both sides, by rule:
15
+ *
16
+ * - an expression compares as its token sequence: whitespace and comments
17
+ * left out, unquoted words in lower case, a quoted name that needs no quotes
18
+ * written bare, a cast on a literal dropped, a quoted number cast to a
19
+ * number read as the number, `IN (literals)` read as `= ANY (ARRAY[...])`,
20
+ * and parentheses around the whole of it dropped;
21
+ * - a type is its `format_type()` spelling, from the pinned catalog's alias
22
+ * table; a type outside `pg_catalog` is qualified with the default schema;
23
+ * - a name with no schema is in the default schema (`public` unless the
24
+ * profile says otherwise), as `search_path` would resolve it;
25
+ * - a constraint the declaration leaves unnamed is matched by what it says,
26
+ * not by the name Postgres gave it; a primary key's and an identity
27
+ * column's columns are `NOT NULL` whether or not that is written;
28
+ * - an identity column's or a sequence's options at their defaults are left
29
+ * out; storage parameters compare as a map;
30
+ * - an index's access method defaults to `btree`;
31
+ * - a function's or procedure's parameter types drop their modifiers (the
32
+ * catalog keeps none: `varchar(20)` is `character varying`), a parameter's
33
+ * mode is `in` unless written, its attributes at their defaults (VOLATILE,
34
+ * CALLED ON NULL INPUT, SECURITY INVOKER, PARALLEL UNSAFE, COST 100 or 1,
35
+ * ROWS 1000 or 0) are left out, and its body is compared as written, since
36
+ * the server keeps it verbatim;
37
+ * - a trigger's events compare as a set, `UPDATE OF` columns sorted, and its
38
+ * arguments as the strings the catalog stores.
39
+ *
40
+ * What the rules cannot see (a view's `SELECT *`, which the server expands, or
41
+ * an expression it parenthesizes) is asked of the live server itself when a
42
+ * plan or a deep read has one (`./server-normalize.ts`). Offline, between two
43
+ * builds, only the rules apply.
44
+ */
45
+ import { isTrivia, tokenizeText } from "../tokens.js";
46
+ import { identValue } from "../parser.js";
47
+ import { quoteIdent } from "../keywords.js";
48
+ import { COLUMN_TYPES, FUNCTIONS } from "../../generated/postgres.js";
49
+ import { stripMarker } from "../../core/ownership.js";
50
+ import { sequenceDefaults } from "../live/catalog.js";
51
+ import { stringValue } from "../entities.js";
52
+ import { parseStatements } from "../parser.js";
53
+ // ── Types ──────────────────────────────────────────────────────────────
54
+ /** Every spelling of a built-in type, to its `format_type()` spelling. */
55
+ const TYPE_SPELLING = (() => {
56
+ const m = new Map();
57
+ for (const [sql, spec] of Object.entries(COLUMN_TYPES)) {
58
+ m.set(sql, sql);
59
+ for (const a of spec.aliases)
60
+ m.set(a, sql);
61
+ if (spec.catalogName)
62
+ m.set(spec.catalogName, sql);
63
+ }
64
+ // Spellings the parser accepts that the catalog does not list.
65
+ for (const [a, sql] of [
66
+ ["int", "integer"],
67
+ ["float", "double precision"],
68
+ ["decimal", "numeric"],
69
+ ["dec", "numeric"],
70
+ ["char", "character"],
71
+ ["timestamp without time zone", "timestamp without time zone"],
72
+ ["time without time zone", "time without time zone"],
73
+ ]) {
74
+ if (!m.has(a))
75
+ m.set(a, sql);
76
+ }
77
+ return m;
78
+ })();
79
+ /**
80
+ * `serial`, `bigserial` and `smallserial` are not types in the catalog: each
81
+ * is shorthand for an integer column with an owned sequence and a `nextval`
82
+ * default, so they are never schema-qualified. The live read prints such a
83
+ * column as the shorthand; `serialBase` gives the integer type underneath.
84
+ */
85
+ const SERIAL_BASE = { serial: "integer", bigserial: "bigint", smallserial: "smallint" };
86
+ /** The integer type a serial spelling stands for, or undefined for any other type. */
87
+ export const serialBase = (type) => (type === undefined ? undefined : SERIAL_BASE[type]);
88
+ /** A qualified name in canonical form: each piece unquoted where it can be, the default schema added to a bare one. */
89
+ export function canonicalName(text, defaultSchema) {
90
+ const pieces = text
91
+ .split(/\.(?=(?:[^"]*"[^"]*")*[^"]*$)/)
92
+ .map((p) => p.trim())
93
+ .filter(Boolean)
94
+ .map((p) => quoteIdent(identValue(p)));
95
+ if (pieces.length === 1 && defaultSchema !== undefined)
96
+ pieces.unshift(quoteIdent(defaultSchema));
97
+ return pieces.join(".");
98
+ }
99
+ /**
100
+ * A type as `format_type()` prints it: `int4` and `int` are `integer`,
101
+ * `timestamptz` is `timestamp with time zone`, `varchar(20)` is
102
+ * `character varying(20)`, `int[]` is `integer[]`, `numeric(12, 2)` is
103
+ * `numeric(12,2)`, a type outside `pg_catalog` is schema-qualified.
104
+ */
105
+ export function canonicalType(text, defaultSchema = "public") {
106
+ if (text === undefined)
107
+ return undefined;
108
+ const t = text.trim();
109
+ // Array bounds: `int[3]`, `int ARRAY[3]`, `int ARRAY` are all `integer[]`.
110
+ const arrayMatch = /^(.*?)(\s*(\[\s*\d*\s*\]|\s+array(\s*\[\s*\d*\s*\])?)\s*)+$/i.exec(t);
111
+ const base = arrayMatch ? arrayMatch[1].trim() : t;
112
+ const dims = arrayMatch ? Math.max(1, (t.slice(arrayMatch[1].length).match(/\[/g) ?? []).length) : 0;
113
+ const m = /^(.*?)\s*(\(([^()]*)\))?\s*((with|without)\s+time\s+zone)?$/i.exec(base);
114
+ let name = m[1].trim().replace(/\s+/g, " ");
115
+ const modifiers = m[3] !== undefined ? `(${m[3].split(",").map((x) => x.trim()).join(",")})` : "";
116
+ const zone = m[4] ? ` ${m[4].toLowerCase().replace(/\s+/g, " ")}` : "";
117
+ const lower = /"/.test(name) ? name : name.toLowerCase();
118
+ let spelled;
119
+ const builtin = TYPE_SPELLING.get(`${lower}${zone}`) ?? TYPE_SPELLING.get(lower);
120
+ if (SERIAL_BASE[lower] !== undefined && modifiers === "" && !zone) {
121
+ spelled = lower;
122
+ }
123
+ else if (builtin !== undefined) {
124
+ spelled = builtin;
125
+ // `timestamp(3) with time zone`: the modifier sits before the zone.
126
+ if (modifiers && /^(timestamp|time) with(out)? time zone$/.test(spelled)) {
127
+ spelled = spelled.replace(/^(timestamp|time)/, `$1${modifiers}`);
128
+ return spelled + "[]".repeat(dims);
129
+ }
130
+ if (zone && !TYPE_SPELLING.has(`${lower}${zone}`))
131
+ spelled += zone;
132
+ }
133
+ else if (/^(timestamp|time)$/.test(lower)) {
134
+ spelled = `${lower} without time zone`;
135
+ if (modifiers)
136
+ return `${lower}${modifiers} without time zone${"[]".repeat(dims)}`;
137
+ }
138
+ else {
139
+ name = canonicalName(name, defaultSchema);
140
+ spelled = name.startsWith("pg_catalog.") ? name.slice("pg_catalog.".length) : name;
141
+ }
142
+ return `${spelled}${modifiers}${"[]".repeat(dims)}`;
143
+ }
144
+ // ── Expressions ────────────────────────────────────────────────────────
145
+ const NUMERIC_TYPES = new Set(["smallint", "integer", "bigint", "numeric", "real", "double precision", "int2", "int4", "int8", "float4", "float8", "decimal"]);
146
+ /** The tokens of a cast's type after `::`, from index `i`; returns the index after it and the type's text. */
147
+ function castEnd(sig, i) {
148
+ let j = i;
149
+ const words = [];
150
+ const word = (t) => t !== undefined && (t.kind === "ident" || t.kind === "qident");
151
+ if (!word(sig[j]))
152
+ return { end: i, type: "" };
153
+ words.push(sig[j].text);
154
+ j++;
155
+ while (sig[j]?.kind === "punct" && sig[j].text === "." && word(sig[j + 1])) {
156
+ words.push(".", sig[j + 1].text);
157
+ j += 2;
158
+ }
159
+ while (sig[j]?.kind === "ident" && /^(varying|precision|with|without|time|zone)$/i.test(sig[j].text)) {
160
+ words.push(" ", sig[j].text);
161
+ j++;
162
+ }
163
+ if (sig[j]?.kind === "punct" && sig[j].text === "(") {
164
+ let depth = 0;
165
+ for (; j < sig.length; j++) {
166
+ if (sig[j].text === "(")
167
+ depth++;
168
+ if (sig[j].text === ")" && --depth === 0) {
169
+ j++;
170
+ break;
171
+ }
172
+ }
173
+ }
174
+ while (sig[j]?.kind === "punct" && sig[j].text === "[" && sig[j + 1]?.text === "]")
175
+ j += 2;
176
+ return { end: j, type: words.join("").toLowerCase() };
177
+ }
178
+ const isLiteral = (t) => t !== undefined && (t.kind === "string" || t.kind === "number" || (t.kind === "ident" && t.text.toUpperCase() === "NULL"));
179
+ /** One token's canonical text. */
180
+ function tokenText(t) {
181
+ if (t.kind === "ident")
182
+ return t.text.toLowerCase();
183
+ if (t.kind === "qident")
184
+ return quoteIdent(identValue(t));
185
+ if (t.kind === "string" && /^[Ee]'/.test(t.text) && !t.text.includes("\\"))
186
+ return t.text.slice(1);
187
+ return t.text;
188
+ }
189
+ /**
190
+ * An expression in canonical form: a string of tokens joined by single
191
+ * spaces. Two expressions that read the same after the rules in this file's
192
+ * header have the same canonical form.
193
+ */
194
+ export function canonicalExpr(text) {
195
+ if (text === undefined)
196
+ return undefined;
197
+ let tokens;
198
+ try {
199
+ tokens = tokenizeText(text, 0).filter((t) => !isTrivia(t));
200
+ }
201
+ catch {
202
+ return text.trim().replace(/\s+/g, " ");
203
+ }
204
+ const out = [];
205
+ for (let i = 0; i < tokens.length; i++) {
206
+ const t = tokens[i];
207
+ // A cast on a literal: `'placed'::app.order_status` is `'placed'`, `'-1'::integer` is `-1`.
208
+ if (isLiteral(t) && tokens[i + 1]?.kind === "op" && tokens[i + 1].text === "::") {
209
+ const { end, type } = castEnd(tokens, i + 2);
210
+ if (end > i + 2) {
211
+ const numeric = /^'(-?\d+(\.\d+)?)'$/.exec(t.text);
212
+ if (numeric && NUMERIC_TYPES.has(type))
213
+ out.push(...(numeric[1].startsWith("-") ? ["-", numeric[1].slice(1)] : [numeric[1]]));
214
+ else
215
+ out.push(tokenText(t));
216
+ i = end - 1;
217
+ continue;
218
+ }
219
+ }
220
+ out.push(tokenText(t));
221
+ }
222
+ return rewriteIn(dropOuterParens(out)).join(" ");
223
+ }
224
+ /** Parentheses around the whole expression, repeatedly. */
225
+ function dropOuterParens(tokens) {
226
+ let t = tokens;
227
+ while (t.length >= 2 && t[0] === "(" && t[t.length - 1] === ")") {
228
+ let depth = 0;
229
+ let wraps = true;
230
+ for (let i = 0; i < t.length; i++) {
231
+ if (t[i] === "(")
232
+ depth++;
233
+ if (t[i] === ")")
234
+ depth--;
235
+ if (depth === 0 && i < t.length - 1) {
236
+ wraps = false;
237
+ break;
238
+ }
239
+ }
240
+ if (!wraps)
241
+ break;
242
+ t = t.slice(1, -1);
243
+ }
244
+ return t;
245
+ }
246
+ /** `x IN (a, b)` as `x = ANY (ARRAY[a, b])` and `x NOT IN (a, b)` as `x <> ALL (ARRAY[a, b])`, for a list of literals. */
247
+ function rewriteIn(tokens) {
248
+ const out = [];
249
+ for (let i = 0; i < tokens.length; i++) {
250
+ const negated = tokens[i] === "not" && tokens[i + 1] === "in";
251
+ const at = negated ? i + 1 : i;
252
+ if (tokens[at] === "in" && tokens[at + 1] === "(") {
253
+ let j = at + 2;
254
+ const items = [];
255
+ let ok = true;
256
+ for (; j < tokens.length && tokens[j] !== ")"; j++) {
257
+ if (tokens[j] === ",")
258
+ continue;
259
+ if (!/^('.*'|-?\d+(\.\d+)?)$/s.test(tokens[j])) {
260
+ ok = false;
261
+ break;
262
+ }
263
+ items.push(tokens[j]);
264
+ }
265
+ if (ok && tokens[j] === ")" && items.length > 0) {
266
+ out.push(negated ? "<>" : "=", negated ? "all" : "any", "(", "array", "[", ...items.flatMap((x, k) => (k === 0 ? [x] : [",", x])), "]", ")");
267
+ i = j;
268
+ continue;
269
+ }
270
+ }
271
+ out.push(tokens[i]);
272
+ }
273
+ return out;
274
+ }
275
+ /** A storage parameter list as a sorted map: `(fillfactor = 70)` and `WITH (fillfactor=70)` are `{ fillfactor: "70" }`. */
276
+ export function canonicalOptions(text) {
277
+ if (text === undefined)
278
+ return undefined;
279
+ const inner = text.trim().replace(/^\(/, "").replace(/\)$/, "");
280
+ const out = {};
281
+ for (const item of inner.split(",").map((s) => s.trim()).filter(Boolean)) {
282
+ const [k, v] = item.split("=").map((s) => s.trim());
283
+ out[k.toLowerCase()] = (v ?? "true").replace(/^'(.*)'$/, "$1").toLowerCase();
284
+ }
285
+ return Object.keys(out).length ? Object.fromEntries(Object.entries(out).sort(([a], [b]) => (a < b ? -1 : 1))) : undefined;
286
+ }
287
+ /** The kinds whose declarations are privileges, compared as the access they add up to (`../access/`), not one by one. */
288
+ export const ACCESS_KINDS = new Set(["grant", "defaultPrivileges"]);
289
+ /** The kinds a profile manages only when it says so (`sql.profiles.<env>.access`). */
290
+ export const ACCESS_MANAGED_KINDS = new Set(["policy", "role", "grant", "defaultPrivileges"]);
291
+ const kindOf = {
292
+ "Postgres::Schema": "schema",
293
+ "Postgres::Table": "table",
294
+ "Postgres::Index": "index",
295
+ "Postgres::View": "view",
296
+ "Postgres::MaterializedView": "materializedView",
297
+ "Postgres::Sequence": "sequence",
298
+ "Postgres::Enum": "enum",
299
+ "Postgres::CompositeType": "composite",
300
+ "Postgres::RangeType": "range",
301
+ "Postgres::Domain": "domain",
302
+ "Postgres::Aggregate": "aggregate",
303
+ "Postgres::Collation": "collation",
304
+ "Postgres::Cast": "cast",
305
+ "Postgres::EventTrigger": "eventTrigger",
306
+ "Postgres::Extension": "extension",
307
+ "Postgres::Function": "function",
308
+ "Postgres::Procedure": "procedure",
309
+ "Postgres::Trigger": "trigger",
310
+ "Postgres::Policy": "policy",
311
+ "Postgres::Role": "role",
312
+ "Postgres::Grant": "grant",
313
+ "Postgres::DefaultPrivileges": "defaultPrivileges",
314
+ };
315
+ const comment = (c) => {
316
+ if (c === undefined)
317
+ return undefined;
318
+ const own = stripMarker(c);
319
+ return own === "" ? undefined : own;
320
+ };
321
+ const refTableOf = (fk, defaultSchema) => canonicalName(fk.refTable, defaultSchema);
322
+ /** Identity or sequence options, at their defaults left out, as a sorted `key value` list. */
323
+ export function canonicalSequenceOptions(o, ownerType) {
324
+ const type = canonicalType(o.type ?? ownerType ?? "bigint");
325
+ const inc = BigInt((o.increment ?? "1").replace(/\s+/g, ""));
326
+ const d = sequenceDefaults(type, inc);
327
+ const min = o.minValue === null || o.minValue === undefined ? d.min : o.minValue.replace(/\s+/g, "");
328
+ const max = o.maxValue === null || o.maxValue === undefined ? d.max : o.maxValue.replace(/\s+/g, "");
329
+ const start = o.start === undefined ? (inc > 0n ? min : max) : o.start.replace(/\s+/g, "");
330
+ const out = [
331
+ ...(o.type !== undefined && type !== (ownerType ? canonicalType(ownerType) : "bigint") ? [`type ${type}`] : []),
332
+ ...(inc !== 1n ? [`increment ${inc}`] : []),
333
+ ...(min !== d.min ? [`min ${min}`] : []),
334
+ ...(max !== d.max ? [`max ${max}`] : []),
335
+ ...(start !== (inc > 0n ? min : max) ? [`start ${start}`] : []),
336
+ ...((o.cache ?? "1") !== "1" ? [`cache ${o.cache}`] : []),
337
+ ...(o.cycle ? ["cycle"] : []),
338
+ ];
339
+ return out.join(", ");
340
+ }
341
+ /** An identity column's `(START WITH 5 INCREMENT BY 2)` as sequence options. */
342
+ function identityOptions(text) {
343
+ const o = {};
344
+ if (!text)
345
+ return o;
346
+ const s = text.replace(/^\(|\)$/g, " ");
347
+ const num = (re) => re.exec(s)?.[1]?.replace(/\s+/g, "");
348
+ o.increment = num(/increment\s+(?:by\s+)?(-?\s*\d+)/i);
349
+ o.start = num(/start\s+(?:with\s+)?(-?\s*\d+)/i);
350
+ o.cache = num(/cache\s+(\d+)/i);
351
+ if (/no\s+minvalue/i.test(s))
352
+ o.minValue = null;
353
+ else
354
+ o.minValue = num(/minvalue\s+(-?\s*\d+)/i);
355
+ if (/no\s+maxvalue/i.test(s))
356
+ o.maxValue = null;
357
+ else
358
+ o.maxValue = num(/maxvalue\s+(-?\s*\d+)/i);
359
+ if (/\bno\s+cycle\b/i.test(s))
360
+ o.cycle = false;
361
+ else if (/\bcycle\b/i.test(s))
362
+ o.cycle = true;
363
+ o.type = /\bas\s+(\w+)/i.exec(s)?.[1];
364
+ return o;
365
+ }
366
+ const deferralText = (c) => [c.deferrable ? "deferrable" : "", c.initiallyDeferred ? "initially deferred" : ""].filter(Boolean).join(" ");
367
+ const keyBody = (kind, k) => [kind, `(${k.columns.join(",")})`, k.nullsNotDistinct ? "nulls not distinct" : "", k.include ? `include (${canonicalExpr(k.include)})` : "", deferralText(k)]
368
+ .filter(Boolean)
369
+ .join(" ");
370
+ /** The pseudo-types (`pg_type.typtype = 'p'`), in `pg_catalog`: a routine's parameter or result, never a column's. */
371
+ const PSEUDO_TYPES = new Set([
372
+ "any", "anyarray", "anycompatible", "anycompatiblearray", "anycompatiblemultirange", "anycompatiblenonarray", "anycompatiblerange",
373
+ "anyelement", "anyenum", "anymultirange", "anynonarray", "anyrange", "cstring", "event_trigger", "fdw_handler", "index_am_handler",
374
+ "internal", "language_handler", "pg_ddl_command", "record", "table_am_handler", "trigger", "tsm_handler", "unknown", "void",
375
+ ]);
376
+ /** A parameter's or result's type as the catalog keeps it: canonical, its modifiers dropped (`varchar(20)` is `character varying`). */
377
+ export function canonicalArgType(text, defaultSchema) {
378
+ const m = /^\s*(?:pg_catalog\s*\.\s*)?([A-Za-z_]+)\s*((?:\[\s*\]\s*)*)$/.exec(text);
379
+ if (m && PSEUDO_TYPES.has(m[1].toLowerCase()))
380
+ return `${m[1].toLowerCase()}${"[]".repeat((m[2].match(/\[/g) ?? []).length)}`;
381
+ return canonicalType(text, defaultSchema).replace(/\([^()]*\)/g, "");
382
+ }
383
+ /** A routine's input parameters: those whose mode is `in`, `inout` or `variadic`. */
384
+ const isInput = (a) => a.mode !== "out";
385
+ /** A routine's identity among its overloads: its input parameter types, `(integer,text)`. */
386
+ export function routineSignature(p, defaultSchema) {
387
+ return `(${p.args.filter(isInput).map((a) => canonicalArgType(a.type, defaultSchema)).join(",")})`;
388
+ }
389
+ /** The result a function declares, or the one its OUT parameters give it: one OUT is its type, several are `record`. */
390
+ function routineResult(p, defaultSchema) {
391
+ if (p.returnsTable)
392
+ return `table(${p.returnsTable.map((c) => `${quoteIdent(c.name)} ${canonicalArgType(c.type, defaultSchema)}`).join(",")})`;
393
+ if (p.returns !== undefined) {
394
+ const m = /^setof\s+(.*)$/is.exec(p.returns.trim());
395
+ return m ? `setof ${canonicalArgType(m[1], defaultSchema)}` : canonicalArgType(p.returns, defaultSchema);
396
+ }
397
+ const outs = p.args.filter((a) => a.mode === "out" || a.mode === "inout");
398
+ if (outs.length === 1)
399
+ return canonicalArgType(outs[0].type, defaultSchema);
400
+ if (outs.length > 1)
401
+ return "record";
402
+ return undefined;
403
+ }
404
+ /** A `SET` value as compared: each item unquoted, a bare word folded, `TO` and `=` alike. */
405
+ function canonicalSetting(value) {
406
+ const items = [];
407
+ let tokens;
408
+ try {
409
+ tokens = tokenizeText(value, 0).filter((t) => !isTrivia(t));
410
+ }
411
+ catch {
412
+ return value.trim();
413
+ }
414
+ let sign = "";
415
+ for (const t of tokens) {
416
+ if (t.kind === "punct" && t.text === ",")
417
+ continue;
418
+ if (t.kind === "op" && (t.text === "-" || t.text === "+")) {
419
+ sign = t.text === "-" ? "-" : "";
420
+ continue;
421
+ }
422
+ if (t.kind === "string")
423
+ items.push(stringValue(t.text) ?? t.text);
424
+ else if (t.kind === "qident")
425
+ items.push(identValue(t));
426
+ else
427
+ items.push(`${sign}${t.kind === "ident" ? t.text.toLowerCase() : t.text}`);
428
+ sign = "";
429
+ }
430
+ return items.join(", ");
431
+ }
432
+ /** The canonical fields of a function or procedure. */
433
+ function routineFields(kind, p, defaultSchema, fields) {
434
+ const lang = (p.language ?? "sql").toLowerCase();
435
+ fields.args = p.args.map((a) => [a.mode, a.name ? quoteIdent(a.name) : "", canonicalArgType(a.type, defaultSchema), a.default !== undefined ? `default ${canonicalExpr(a.default)}` : ""].filter(Boolean).join(" "));
436
+ fields.language = lang;
437
+ // Line endings aside, the body is what the server keeps and prints back.
438
+ fields.body = p.body.replace(/\r\n/g, "\n");
439
+ fields.link = p.link;
440
+ fields.securityDefiner = p.securityDefiner === true;
441
+ fields.set = p.set && Object.keys(p.set).length > 0
442
+ ? Object.fromEntries(Object.entries(p.set).map(([k, v]) => [k.toLowerCase(), canonicalSetting(v)]).sort(([a], [b]) => (a < b ? -1 : 1)))
443
+ : undefined;
444
+ fields.transform = p.transform ? canonicalExpr(p.transform) : undefined;
445
+ if (kind === "procedure")
446
+ return;
447
+ const result = routineResult(p, defaultSchema);
448
+ const setReturning = result !== undefined && /^(setof |table\()/.test(result);
449
+ fields.returns = result;
450
+ fields.volatility = p.volatility && p.volatility !== "volatile" ? p.volatility : undefined;
451
+ fields.strict = p.strict === true;
452
+ fields.leakproof = p.leakproof === true;
453
+ fields.parallel = p.parallel && p.parallel !== "unsafe" ? p.parallel : undefined;
454
+ const defaultCost = lang === "c" || lang === "internal" ? 1 : 100;
455
+ fields.cost = p.cost !== undefined && Number(p.cost) !== defaultCost ? String(Number(p.cost)) : undefined;
456
+ const defaultRows = setReturning ? 1000 : 0;
457
+ fields.rows = p.rows !== undefined && Number(p.rows) !== defaultRows ? String(Number(p.rows)) : undefined;
458
+ fields.support = p.support ? canonicalName(p.support, "pg_catalog").replace(/^pg_catalog\./, "") : undefined;
459
+ fields.window = p.window === true;
460
+ }
461
+ /** A routine named with its parameters as a GRANT writes it (`app.f(int, text)`), as the target the catalog names: `app.f(integer,text)`. */
462
+ export function canonicalRoutineTarget(text, defaultSchema) {
463
+ const open = text.indexOf("(");
464
+ const name = canonicalName(open < 0 ? text : text.slice(0, open), defaultSchema);
465
+ if (open < 0)
466
+ return name;
467
+ try {
468
+ // The parameter list read the way a CREATE FUNCTION reads it: modes, names and types.
469
+ const tokens = tokenizeText(`CREATE FUNCTION x${text.slice(open)} RETURNS void LANGUAGE sql AS ''`, 0);
470
+ const [node] = parseStatements(tokens);
471
+ const args = node.args.map((a) => ({ mode: (a.mode?.toLowerCase() ?? "in"), type: tokens.slice(a.type.from, a.type.to).map((t) => t.text).join("") }));
472
+ return `${name}${routineSignature({ args }, defaultSchema)}`;
473
+ }
474
+ catch {
475
+ return `${name}${text.slice(open).replace(/\s+/g, "")}`;
476
+ }
477
+ }
478
+ /** A role's attributes that differ from `CREATE ROLE`'s defaults (NOLOGIN, INHERIT and every other NO...), sorted. */
479
+ const ROLE_DEFAULTS = { superuser: false, createdb: false, createrole: false, inherit: true, login: false, replication: false, bypassrls: false };
480
+ export function canonicalRoleAttributes(options) {
481
+ const set = { ...ROLE_DEFAULTS };
482
+ for (const o of options) {
483
+ const word = o.toLowerCase();
484
+ if (word.startsWith("no") && word.slice(2) in ROLE_DEFAULTS)
485
+ set[word.slice(2)] = false;
486
+ else if (word in ROLE_DEFAULTS)
487
+ set[word] = true;
488
+ }
489
+ return Object.entries(set)
490
+ .filter(([k, v]) => ROLE_DEFAULTS[k] !== v)
491
+ .map(([k, v]) => (v ? k : `no${k}`))
492
+ .sort();
493
+ }
494
+ /** The order the catalog prints a trigger's events in. */
495
+ const EVENT_ORDER = ["insert", "delete", "update", "truncate"];
496
+ /** A trigger argument as the catalog stores it: a string. */
497
+ function triggerArg(a) {
498
+ const t = a.trim();
499
+ if (/^'/.test(t) || /^[Ee]'/.test(t) || /^\$/.test(t))
500
+ return stringValue(t) ?? t;
501
+ if (/^"/.test(t))
502
+ return identValue(t);
503
+ if (/^-?[0-9.]/.test(t))
504
+ return t.replace(/\s+/g, "");
505
+ return t.toLowerCase();
506
+ }
507
+ function triggerFields(p, defaultSchema, fields) {
508
+ fields.table = canonicalName(p.tableName, defaultSchema);
509
+ fields.constraint = p.constraint === true;
510
+ fields.timing = p.timing;
511
+ fields.events = [...p.events]
512
+ .sort((a, b) => EVENT_ORDER.indexOf(a.event) - EVENT_ORDER.indexOf(b.event))
513
+ .map((e) => (e.columns && e.columns.length > 0 ? `${e.event} of ${[...e.columns].sort().map(quoteIdent).join(",")}` : e.event));
514
+ fields.forEach = p.forEach;
515
+ fields.when = canonicalExpr(p.when);
516
+ fields.function = canonicalName(p.functionName, defaultSchema);
517
+ fields.args = p.args.length > 0 ? p.args.map(triggerArg) : undefined;
518
+ fields.from = p.from ? canonicalName(p.from, defaultSchema) : undefined;
519
+ fields.deferrable = p.deferrable === true;
520
+ fields.initiallyDeferred = p.initiallyDeferred === true;
521
+ fields.referencing = p.referencing && (p.referencing.old || p.referencing.new)
522
+ ? [p.referencing.old ? `old table as ${quoteIdent(p.referencing.old)}` : "", p.referencing.new ? `new table as ${quoteIdent(p.referencing.new)}` : ""].filter(Boolean).join(" ")
523
+ : undefined;
524
+ }
525
+ // ── Types, aggregates, collations, casts, event triggers (#3911) ──────
526
+ /** The kinds that live in no schema. */
527
+ const UNPLACED = new Set(["schema", "extension", "role", "cast", "eventTrigger"]);
528
+ /**
529
+ * A function named where an object calls it (an aggregate's SFUNC, a cast's
530
+ * or an event trigger's function), as the catalog's `regproc` prints it with
531
+ * an empty search_path: a `pg_catalog` function bare, any other qualified. A
532
+ * bare name that is no built-in function's is in the default schema.
533
+ */
534
+ export function canonicalFunctionName(text, defaultSchema) {
535
+ const pieces = canonicalName(text).split(/\.(?=(?:[^"]*"[^"]*")*[^"]*$)/);
536
+ if (pieces.length >= 2)
537
+ return pieces[0] === "pg_catalog" ? pieces.slice(1).join(".") : pieces.join(".");
538
+ const bare = identValue(pieces[0]);
539
+ return FUNCTIONS[bare] !== undefined ? pieces[0] : `${quoteIdent(defaultSchema)}.${pieces[0]}`;
540
+ }
541
+ /** A collation named in a type or an attribute: canonical, a `pg_catalog` one bare (`"C"`), as the catalog prints it. */
542
+ export const canonicalCollation = (text) => canonicalName(text).replace(/^pg_catalog\./, "");
543
+ /** A definition list's value as compared: a string constant's value, a word folded, other text canonical. */
544
+ function defValue(v) {
545
+ const t = v.trim();
546
+ if (/^([Ee]?'|\$)/.test(t))
547
+ return stringValue(t) ?? t;
548
+ if (/^"/.test(t))
549
+ return identValue(t);
550
+ if (/^[A-Za-z_][A-Za-z0-9_$]*$/.test(t))
551
+ return t.toLowerCase();
552
+ return canonicalExpr(t) ?? t;
553
+ }
554
+ /** A Postgres boolean option's value (`true`, `on`, `1`, `yes`), as written or given with no value. */
555
+ const isTrue = (v) => v !== undefined && /^(true|on|1|yes|t|y)$/i.test(defValue(v));
556
+ /** The name Postgres gives a range type's multirange type when none is written: `range` in its name becomes `multirange`, else `_multirange` is added. */
557
+ export function defaultMultirangeName(range) {
558
+ const at = range.indexOf("range");
559
+ return at >= 0 ? `${range.slice(0, at)}multi${range.slice(at)}` : `${range}_multirange`;
560
+ }
561
+ /** An aggregate's identity among its overloads: `(integer)`, `(*)`, `(text ORDER BY integer)`. */
562
+ export function aggregateSignature(p, defaultSchema) {
563
+ const types = (l) => l.map((a) => canonicalArgType(a.type, defaultSchema)).join(",");
564
+ if (p.orderBy)
565
+ return `(${p.args.length > 0 ? `${types(p.args)} ` : ""}ORDER BY ${types(p.orderBy)})`;
566
+ return p.args.length === 0 ? "(*)" : `(${types(p.args)})`;
567
+ }
568
+ const AGGREGATE_FUNCTIONS = new Set(["sfunc", "finalfunc", "combinefunc", "serialfunc", "deserialfunc", "msfunc", "minvfunc", "mfinalfunc"]);
569
+ const AGGREGATE_TYPES = new Set(["stype", "mstype"]);
570
+ const AGGREGATE_FLAGS = new Set(["finalfunc_extra", "mfinalfunc_extra", "hypothetical"]);
571
+ /** An aggregate's definition as compared: each option canonical, the ones at their defaults left out. */
572
+ function aggregateOptions(p, defaultSchema) {
573
+ const out = {};
574
+ // A FINALFUNC_MODIFY not written is READ_ONLY for a plain aggregate, READ_WRITE for an ordered-set one.
575
+ const modifyDefault = p.orderBy ? "read_write" : "read_only";
576
+ for (const [k, v] of Object.entries(p.options)) {
577
+ if (AGGREGATE_FUNCTIONS.has(k))
578
+ out[k] = canonicalFunctionName(v, defaultSchema);
579
+ else if (AGGREGATE_TYPES.has(k))
580
+ out[k] = canonicalType(v, defaultSchema);
581
+ else if (AGGREGATE_FLAGS.has(k)) {
582
+ if (isTrue(v))
583
+ out[k] = "true";
584
+ }
585
+ else if (k === "sspace" || k === "msspace") {
586
+ if (Number(defValue(v)) !== 0)
587
+ out[k] = String(Number(defValue(v)));
588
+ }
589
+ else if (k === "finalfunc_modify" || k === "mfinalfunc_modify") {
590
+ if (defValue(v) !== modifyDefault)
591
+ out[k] = defValue(v);
592
+ }
593
+ else if (k === "sortop") {
594
+ out[k] = v.trim().replace(/^OPERATOR\s*\(\s*(.*?)\s*\)$/is, "$1").replace(/^pg_catalog\s*\.\s*/i, "");
595
+ }
596
+ else if (k === "parallel") {
597
+ if (defValue(v) !== "unsafe")
598
+ out[k] = defValue(v);
599
+ }
600
+ else
601
+ out[k] = defValue(v);
602
+ }
603
+ return Object.fromEntries(Object.entries(out).sort(([a], [b]) => (a < b ? -1 : 1)));
604
+ }
605
+ /** A collation's definition as compared: its provider (`libc` unless written) and locales, `deterministic` only when false. VERSION is the provider's, and is not compared. */
606
+ function collationFields(p, fields) {
607
+ const o = Object.fromEntries(Object.entries(p.options).map(([k, v]) => [k, defValue(v)]));
608
+ const provider = o.provider ?? "libc";
609
+ fields.provider = provider;
610
+ if (provider === "libc") {
611
+ fields.lcCollate = o.lc_collate ?? o.locale;
612
+ fields.lcCtype = o.lc_ctype ?? o.locale;
613
+ }
614
+ else
615
+ fields.locale = o.locale ?? o.lc_collate;
616
+ fields.nondeterministic = p.options.deterministic !== undefined && !isTrue(p.options.deterministic);
617
+ fields.rules = o.rules;
618
+ }
619
+ /** A cast's name, `(source AS target)`, its types canonical. */
620
+ export const castName = (p, defaultSchema) => `(${canonicalType(p.sourceType, defaultSchema)} AS ${canonicalType(p.targetType, defaultSchema)})`;
621
+ /**
622
+ * A definition in canonical form. `props` is an entity's props, declared or
623
+ * parsed from what the server printed; `defaultSchema` qualifies a bare name.
624
+ */
625
+ export function canonicalPgObject(entityType, props, defaultSchema = "public") {
626
+ const kind = kindOf[entityType];
627
+ if (!kind)
628
+ throw new Error(`not a Postgres entity type: ${entityType}`);
629
+ const schemaOf = (p) => (UNPLACED.has(kind) || ACCESS_KINDS.has(kind) ? undefined : (p.schema ?? defaultSchema));
630
+ const base = { kind, schema: schemaOf(props), name: String(props.name) };
631
+ const fields = { comment: comment(props.comment) };
632
+ const columns = [];
633
+ const constraints = [];
634
+ switch (kind) {
635
+ case "schema": {
636
+ const p = props;
637
+ fields.authorization = p.authorization ? identValue(p.authorization) : undefined;
638
+ break;
639
+ }
640
+ case "extension": {
641
+ const p = props;
642
+ fields.schema = p.schema;
643
+ fields.version = p.version;
644
+ break;
645
+ }
646
+ case "enum":
647
+ fields.labels = props.labels;
648
+ break;
649
+ case "composite": {
650
+ const p = props;
651
+ fields.attributeDefs = p.attributes.map((a) => `${quoteIdent(a.name)} ${canonicalType(a.type, defaultSchema)}${a.collate ? ` collate ${canonicalCollation(a.collate)}` : ""}`);
652
+ break;
653
+ }
654
+ case "range": {
655
+ const p = props;
656
+ fields.subtype = canonicalType(p.subtype, defaultSchema);
657
+ fields.subtypeOpclass = p.subtypeOpclass ? canonicalName(p.subtypeOpclass).replace(/^pg_catalog\./, "") : undefined;
658
+ fields.collation = p.collation ? canonicalCollation(p.collation) : undefined;
659
+ fields.canonical = p.canonical ? canonicalFunctionName(p.canonical, defaultSchema) : undefined;
660
+ fields.subtypeDiff = p.subtypeDiff ? canonicalFunctionName(p.subtypeDiff, defaultSchema) : undefined;
661
+ // The multirange type is always named: as written, else as Postgres derives it, in the range's schema.
662
+ fields.multirange = p.multirangeTypeName
663
+ ? canonicalName(p.multirangeTypeName, base.schema)
664
+ : canonicalName(quoteIdent(defaultMultirangeName(base.name)), base.schema);
665
+ break;
666
+ }
667
+ case "aggregate": {
668
+ const p = props;
669
+ base.signature = aggregateSignature(p, defaultSchema);
670
+ fields.definition = aggregateOptions(p, defaultSchema);
671
+ break;
672
+ }
673
+ case "collation":
674
+ collationFields(props, fields);
675
+ break;
676
+ case "cast": {
677
+ const p = props;
678
+ base.name = castName(p, defaultSchema);
679
+ fields.castMethod = p.method;
680
+ fields.function = p.functionName ? canonicalFunctionName(p.functionName, defaultSchema) : undefined;
681
+ fields.context = p.context === "explicit" ? undefined : p.context;
682
+ break;
683
+ }
684
+ case "eventTrigger": {
685
+ const p = props;
686
+ fields.event = p.event.toLowerCase();
687
+ fields.tags = p.tags && p.tags.length > 0 ? [...new Set(p.tags.map((t) => t.toUpperCase().replace(/\s+/g, " ").trim()))].sort() : undefined;
688
+ fields.function = canonicalFunctionName(p.functionName, defaultSchema);
689
+ break;
690
+ }
691
+ case "function":
692
+ case "procedure": {
693
+ const p = props;
694
+ base.signature = routineSignature(p, defaultSchema);
695
+ routineFields(kind, p, defaultSchema, fields);
696
+ break;
697
+ }
698
+ case "trigger": {
699
+ const p = props;
700
+ triggerFields(p, defaultSchema, fields);
701
+ base.signature = ` ON ${String(fields.table)}`;
702
+ break;
703
+ }
704
+ case "policy": {
705
+ const p = props;
706
+ fields.table = canonicalName(p.tableName, defaultSchema);
707
+ base.signature = ` ON ${String(fields.table)}`;
708
+ fields.restrictive = p.permissive === false;
709
+ fields.command = p.command && p.command !== "all" ? p.command : undefined;
710
+ const roles = [...new Set(p.roles.map((r) => r.toLowerCase() === "public" ? "public" : quoteIdent(r)))].sort();
711
+ fields.roles = roles.length === 1 && roles[0] === "public" ? undefined : roles;
712
+ fields.using = canonicalExpr(p.using);
713
+ fields.check = canonicalExpr(p.check);
714
+ break;
715
+ }
716
+ case "role": {
717
+ const p = props;
718
+ const attributes = canonicalRoleAttributes(p.options);
719
+ fields.attributes = attributes.length > 0 ? attributes : undefined;
720
+ fields.connectionLimit = p.connectionLimit !== undefined && Number(p.connectionLimit) !== -1 ? String(Number(p.connectionLimit)) : undefined;
721
+ break;
722
+ }
723
+ case "grant": {
724
+ const p = props;
725
+ const routine = p.on === "function" || p.on === "procedure" || p.on === "routine";
726
+ fields.grant = {
727
+ action: p.action,
728
+ on: p.on,
729
+ objects: p.objectNames.map((n) => (routine ? canonicalRoutineTarget(n, defaultSchema) : p.on === "schema" ? canonicalName(n) : canonicalName(n, defaultSchema))),
730
+ privileges: p.privileges.map((x) => ({ privilege: x.privilege.toLowerCase(), ...(x.columns ? { columns: x.columns } : {}) })),
731
+ grantees: p.grantees,
732
+ ...(p.withGrantOption ? { withGrantOption: true } : {}),
733
+ ...(p.grantOptionFor ? { grantOptionFor: true } : {}),
734
+ };
735
+ break;
736
+ }
737
+ case "defaultPrivileges": {
738
+ const p = props;
739
+ fields.grant = {
740
+ action: p.action,
741
+ on: p.on,
742
+ privileges: p.privileges.map((x) => ({ privilege: x.toLowerCase() })),
743
+ grantees: p.grantees,
744
+ ...(p.forRoles ? { forRoles: p.forRoles } : {}),
745
+ ...(p.inSchemas ? { inSchemas: p.inSchemas } : {}),
746
+ ...(p.withGrantOption ? { withGrantOption: true } : {}),
747
+ ...(p.grantOptionFor ? { grantOptionFor: true } : {}),
748
+ };
749
+ break;
750
+ }
751
+ case "domain": {
752
+ const p = props;
753
+ fields.dataType = canonicalType(p.dataType, defaultSchema);
754
+ fields.collate = p.collate ? canonicalName(p.collate) : undefined;
755
+ fields.default = canonicalExpr(p.default);
756
+ fields.notNull = p.notNull === true;
757
+ for (const c of p.checks)
758
+ constraints.push({ kind: "CHECK", name: c.name, body: `check ${canonicalExpr(c.expr)}`, ...(c.notValid ? { notValid: true } : {}), comment: comment(c.comment) });
759
+ break;
760
+ }
761
+ case "sequence": {
762
+ const p = props;
763
+ fields.options = canonicalSequenceOptions({ ...p, type: p.dataType });
764
+ fields.unlogged = p.persistence === "unlogged";
765
+ fields.ownedBy = p.ownedBy && p.ownedBy.toUpperCase() !== "NONE" ? canonicalName(p.ownedBy) : undefined;
766
+ break;
767
+ }
768
+ case "index": {
769
+ const p = props;
770
+ fields.table = canonicalName(p.tableName, defaultSchema);
771
+ fields.unique = p.unique === true;
772
+ fields.method = (p.method ?? "btree").toLowerCase();
773
+ fields.elements = p.elements.map((e) => canonicalExpr(e.expr).replace(/ asc$/, "").replace(/ asc nulls last$/, "").replace(/ desc nulls first$/, " desc"));
774
+ fields.include = p.include ? canonicalExpr(p.include) : undefined;
775
+ fields.nullsNotDistinct = p.nullsNotDistinct === true;
776
+ fields.with = canonicalOptions(p.with);
777
+ fields.where = canonicalExpr(p.where);
778
+ fields.tablespace = p.tablespace;
779
+ break;
780
+ }
781
+ case "view":
782
+ case "materializedView": {
783
+ const p = props;
784
+ fields.query = canonicalExpr(p.query);
785
+ fields.columns = p.columns.length > 0 ? p.columns : undefined;
786
+ const opts = canonicalOptions(p.with);
787
+ fields.with = opts;
788
+ fields.checkOption = p.checkOption ? (/local/i.test(p.checkOption) ? "local" : "cascaded") : undefined;
789
+ if (kind === "materializedView")
790
+ fields.withData = p.withData !== false;
791
+ fields.columnComments = p.columnComments && Object.keys(p.columnComments).length ? p.columnComments : undefined;
792
+ break;
793
+ }
794
+ case "table": {
795
+ const p = props;
796
+ const pkColumns = new Set(p.primaryKey?.columns ?? []);
797
+ p.columns.forEach((c, position) => {
798
+ const identity = c.generated?.kind === "identity";
799
+ const serial = c.type !== undefined && /^(small|big)?serial$/i.test(c.type.trim());
800
+ columns.push({
801
+ name: c.name,
802
+ position,
803
+ type: canonicalType(c.type, defaultSchema),
804
+ notNull: c.notNull === true || pkColumns.has(c.name) || identity || serial,
805
+ default: canonicalExpr(c.default),
806
+ generated: c.generated && !identity ? `${c.generated.kind} ${canonicalExpr(c.generated.expr)}` : undefined,
807
+ identity: identity
808
+ ? `${c.generated.always ? "always" : "by default"}${(() => {
809
+ const o = canonicalSequenceOptions(identityOptions(c.generated.options), canonicalType(c.type));
810
+ return o ? ` (${o})` : "";
811
+ })()}`
812
+ : undefined,
813
+ collate: c.collate ? canonicalName(c.collate) : undefined,
814
+ compression: c.compression?.toLowerCase(),
815
+ storage: c.storage?.toLowerCase(),
816
+ comment: comment(c.comment),
817
+ });
818
+ });
819
+ if (p.primaryKey)
820
+ constraints.push({ kind: "PRIMARY KEY", name: p.primaryKey.name, body: keyBody("primary key", p.primaryKey), comment: comment(p.primaryKey.comment) });
821
+ for (const u of p.uniques)
822
+ constraints.push({ kind: "UNIQUE", name: u.name, body: keyBody("unique", u), comment: comment(u.comment) });
823
+ for (const c of p.checks) {
824
+ constraints.push({
825
+ kind: "CHECK",
826
+ name: c.name,
827
+ body: ["check", canonicalExpr(c.expr), c.noInherit ? "no inherit" : "", c.notEnforced ? "not enforced" : ""].filter(Boolean).join(" "),
828
+ ...(c.notValid ? { notValid: true } : {}),
829
+ comment: comment(c.comment),
830
+ });
831
+ }
832
+ for (const f of p.foreignKeys) {
833
+ const body = [
834
+ `foreign key (${f.columns.join(",")}) references ${refTableOf(f, defaultSchema)}${f.refColumns.length ? `(${f.refColumns.join(",")})` : ""}`,
835
+ f.match && f.match.toUpperCase() !== "SIMPLE" ? `match ${f.match.toLowerCase()}` : "",
836
+ f.onUpdate && !/^no action$/i.test(f.onUpdate) ? `on update ${f.onUpdate.toLowerCase().replace(/\s+/g, " ")}` : "",
837
+ f.onDelete && !/^no action$/i.test(f.onDelete) ? `on delete ${f.onDelete.toLowerCase().replace(/\s+/g, " ")}` : "",
838
+ deferralText(f),
839
+ f.notEnforced ? "not enforced" : "",
840
+ ]
841
+ .filter(Boolean)
842
+ .join(" ");
843
+ constraints.push({ kind: "FOREIGN KEY", name: f.name, body, ...(f.notValid ? { notValid: true } : {}), comment: comment(f.comment) });
844
+ }
845
+ for (const x of p.exclusions) {
846
+ const body = [
847
+ `exclude using ${(x.using ?? "btree").toLowerCase()} (${canonicalExpr(x.elements)})`,
848
+ x.include ? `include (${canonicalExpr(x.include)})` : "",
849
+ x.where ? `where (${canonicalExpr(x.where)})` : "",
850
+ deferralText(x),
851
+ ]
852
+ .filter(Boolean)
853
+ .join(" ");
854
+ constraints.push({ kind: "EXCLUDE", name: x.name, body, comment: comment(x.comment) });
855
+ }
856
+ fields.unlogged = p.persistence === "unlogged";
857
+ fields.partitionBy = canonicalExpr(p.partitionBy);
858
+ fields.partitionOf = p.partitionOf ? canonicalName(typeof p.partitionOf === "string" ? p.partitionOf : p.partitionOf.sqlName, defaultSchema) : undefined;
859
+ fields.partitionBound = canonicalExpr(p.partitionBound);
860
+ fields.inherits = p.inherits.length ? p.inherits.map((i) => canonicalName(typeof i === "string" ? i : i.sqlName, defaultSchema)) : undefined;
861
+ fields.using = p.using && p.using.toLowerCase() !== "heap" ? p.using.toLowerCase() : undefined;
862
+ fields.with = canonicalOptions(p.with);
863
+ fields.tablespace = p.tablespace;
864
+ fields.rowSecurity = p.rowSecurity === true;
865
+ fields.forceRowSecurity = p.forceRowSecurity === true;
866
+ break;
867
+ }
868
+ }
869
+ // Clauses the parser does not know (#3872): compared as text, by clause.
870
+ const unparsed = props.unparsed;
871
+ if (unparsed && unparsed.length > 0) {
872
+ const out = {};
873
+ for (const u of unparsed) {
874
+ const base = u.column !== undefined ? `columns.${u.column}.${u.clause}` : u.clause;
875
+ let key = base;
876
+ for (let n = 2; key in out; n++)
877
+ key = `${base}#${n}`;
878
+ out[key] = canonicalExpr(u.text) ?? u.text;
879
+ }
880
+ fields.unparsed = out;
881
+ }
882
+ return { ...base, fields: Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined && v !== false)), columns, constraints };
883
+ }
884
+ /** Whether two canonical constraints are the same one: the same body, and the same name when both declare one. */
885
+ export function sameConstraint(a, b) {
886
+ return a.kind === b.kind && a.body === b.body && (a.name === undefined || b.name === undefined || a.name === b.name);
887
+ }
888
+ /** A column without its position, for comparing two columns. */
889
+ export const columnShape = ({ position: _p, ...rest }) => JSON.stringify(rest);