@intentius/chant-lexicon-sql 0.124.31 → 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
@@ -0,0 +1,1824 @@
1
+ /**
2
+ * A hand-written, lossless parser for the Postgres statements the dialect
3
+ * declares (chant #3278, #3279): `CREATE SCHEMA`, `TABLE`, `INDEX`, `VIEW`,
4
+ * `MATERIALIZED VIEW`, `SEQUENCE`, `TYPE ... AS ENUM`, `TYPE ... AS (...)` and
5
+ * `TYPE ... AS RANGE`, `DOMAIN`, `EXTENSION`, `FUNCTION`, `PROCEDURE` and
6
+ * `TRIGGER` (#3680), `AGGREGATE`, `COLLATION`, `CAST` and `EVENT TRIGGER`
7
+ * (#3911), `POLICY` and `ROLE`, `GRANT`,
8
+ * `REVOKE` and `ALTER DEFAULT PRIVILEGES` on schemas, tables, columns,
9
+ * sequences and routines, `ALTER TABLE ... ROW LEVEL SECURITY` (#3681), and
10
+ * `COMMENT ON` for those objects.
11
+ *
12
+ * A function's or procedure's body is a string (`AS $$ ... $$`), kept
13
+ * verbatim: Postgres stores it as written and prints it back unchanged. A
14
+ * SQL-standard body (`RETURN expr`, `BEGIN ATOMIC ... END`) is refused, since
15
+ * the server stores it parsed and prints it back rewritten.
16
+ *
17
+ * As for ClickHouse (#3196), statement structure is parsed and expressions are
18
+ * not: a default, a check, a generated expression, an index element, a
19
+ * partition bound and a view's query are token spans, verbatim, with the
20
+ * interpolations inside them recorded. Postgres itself is the backstop.
21
+ *
22
+ * What the spike measured as missing is closed here: a column default is a
23
+ * `b_expr`, so a top-level `IN`, `AND`, `LIKE` or `IS NULL` in it is an error
24
+ * at that token; `LIKE ... INCLUDING` and `ON COMMIT` take only their own
25
+ * option words; a storage parameter list (`WITH (fillfactor = 70)`) is parsed
26
+ * as `name [= value]` items; a type cannot be a reserved word (`a ARRAY[4]`
27
+ * fails at `ARRAY`); and a range partition bound cannot be empty.
28
+ *
29
+ * An interpolation (`ref` token) is accepted wherever a name, a type or an
30
+ * expression goes. Lint parses with every interpolation still a `ref`.
31
+ *
32
+ * Grammar source: the Postgres 18 reference pages for each statement
33
+ * (sql-createtable, sql-createindex, sql-createview,
34
+ * sql-creatematerializedview, sql-createsequence, sql-createtype,
35
+ * sql-createdomain, sql-createextension, sql-createschema, sql-createfunction,
36
+ * sql-createprocedure, sql-createtrigger, sql-createaggregate,
37
+ * sql-createcollation, sql-createcast, sql-createeventtrigger, sql-comment) and
38
+ * `src/backend/parser/gram.y` at `REL_18_6` where a page is silent.
39
+ */
40
+ import { kw, SqlCursor } from "../core/cursor.js";
41
+ import { SqlSyntaxError } from "../core/tokens.js";
42
+ import { keywordCategory } from "./keywords.js";
43
+ export { SqlSyntaxError };
44
+ /** An identifier as Postgres reads it: an unquoted name folds to lower case, a quoted one is exact. */
45
+ export function identValue(t) {
46
+ const text = typeof t === "string" ? t : t.text;
47
+ const quoted = typeof t === "string" ? /^(U&)?"/i.test(text) : t.kind === "qident";
48
+ if (quoted) {
49
+ if (/^[Uu]&/.test(text))
50
+ return text.slice(3, -1).replace(/""/g, '"');
51
+ return text.slice(1, -1).replace(/""/g, '"');
52
+ }
53
+ return text.toLowerCase();
54
+ }
55
+ const isKeywordToken = (t) => t.kind === "ident" && keywordCategory(t.text) !== undefined;
56
+ const isNameToken = (t) => t !== undefined && (t.kind === "ident" || t.kind === "qident" || t.kind === "ref");
57
+ /** Words that end a column's type or a column clause: a column constraint, or a clause after the type. */
58
+ const COLUMN_CLAUSE = [
59
+ "CONSTRAINT",
60
+ "NOT",
61
+ "NULL",
62
+ "DEFAULT",
63
+ "GENERATED",
64
+ "UNIQUE",
65
+ "PRIMARY",
66
+ "CHECK",
67
+ "REFERENCES",
68
+ "COLLATE",
69
+ "DEFERRABLE",
70
+ "INITIALLY",
71
+ "COMPRESSION",
72
+ "STORAGE",
73
+ "ENFORCED",
74
+ ];
75
+ /** A column's clause words, and its constraints' attributes: an unknown clause stops at the next (#3872). */
76
+ const COLUMN_WORDS = [...COLUMN_CLAUSE, "REFERENCES", "ON", "MATCH", "NO", "WITH", "INCLUDE", "USING"];
77
+ /** The clauses after a table's column list. */
78
+ const TABLE_WORDS = ["INHERITS", "PARTITION", "USING", "WITH", "WITHOUT", "ON", "TABLESPACE", "FOR", "DEFAULT"];
79
+ /** The clauses after an index's elements. */
80
+ const INDEX_WORDS = ["INCLUDE", "NULLS", "WITH", "TABLESPACE", "WHERE"];
81
+ /** The clauses before a view's AS. */
82
+ const VIEW_WORDS = ["USING", "WITH", "TABLESPACE", "AS"];
83
+ /** `LIKE source_table [ like_option ... ]`'s option words. */
84
+ const LIKE_OPTIONS = ["COMMENTS", "COMPRESSION", "CONSTRAINTS", "DEFAULTS", "GENERATED", "IDENTITY", "INDEXES", "STATISTICS", "STORAGE", "ALL"];
85
+ /**
86
+ * Words a `b_expr` cannot hold at its top level (gram.y): a column default and
87
+ * a domain default are `b_expr`, which has no `IN`, boolean operators, pattern
88
+ * matching, `BETWEEN`, `IS NULL`-style tests or `AT TIME ZONE`.
89
+ */
90
+ const NOT_IN_B_EXPR = ["IN", "AND", "OR", "LIKE", "ILIKE", "SIMILAR", "BETWEEN", "ISNULL", "NOTNULL", "AT", "COLLATE", "OVERLAPS"];
91
+ /** `CREATE TYPE ... AS RANGE`'s options (sql-createtype). */
92
+ export const RANGE_OPTIONS = ["subtype", "subtype_opclass", "collation", "canonical", "subtype_diff", "multirange_type_name"];
93
+ /** `CREATE AGGREGATE`'s options (sql-createaggregate), the old form's `basetype` aside. */
94
+ export const AGGREGATE_OPTIONS = [
95
+ "sfunc",
96
+ "stype",
97
+ "sspace",
98
+ "finalfunc",
99
+ "finalfunc_extra",
100
+ "finalfunc_modify",
101
+ "combinefunc",
102
+ "serialfunc",
103
+ "deserialfunc",
104
+ "initcond",
105
+ "msfunc",
106
+ "minvfunc",
107
+ "mstype",
108
+ "msspace",
109
+ "mfinalfunc",
110
+ "mfinalfunc_extra",
111
+ "mfinalfunc_modify",
112
+ "minitcond",
113
+ "sortop",
114
+ "parallel",
115
+ "hypothetical",
116
+ ];
117
+ /** `CREATE COLLATION`'s options (sql-createcollation). */
118
+ export const COLLATION_OPTIONS = ["locale", "lc_collate", "lc_ctype", "provider", "deterministic", "rules", "version"];
119
+ /** What may follow `IS` inside a `b_expr`. */
120
+ const B_EXPR_IS = [["DISTINCT", "FROM"], ["NOT", "DISTINCT", "FROM"], ["DOCUMENT"], ["NOT", "DOCUMENT"]];
121
+ class PgParser extends SqlCursor {
122
+ /** A multi-word stop is written with spaces: `"WITH CHECK OPTION"`. A stop never ends an expression at its first token. */
123
+ stopsAt(stop) {
124
+ return stop.some((s) => this.is(...s.split(" ")));
125
+ }
126
+ /** `[` opens and `]` closes, as `(` and `)` do. */
127
+ depthChange(t) {
128
+ if (t.kind === "punct" && (t.text === "(" || t.text === "["))
129
+ return 1;
130
+ if (t.kind === "punct" && (t.text === ")" || t.text === "]"))
131
+ return -1;
132
+ return 0;
133
+ }
134
+ /**
135
+ * An expression span, up to a top-level `,` (unless `stopAtComma` is
136
+ * false), `)`, `]` or `;`, or a stop that is not its first token.
137
+ */
138
+ expr(stop = [], stopAtComma = true) {
139
+ const from = this.idx();
140
+ const refs = [];
141
+ let depth = 0;
142
+ const startP = this.p;
143
+ for (;;) {
144
+ const t = this.peek();
145
+ if (t === undefined)
146
+ break;
147
+ if (depth === 0) {
148
+ if (t.kind === "punct" && (t.text === ")" || t.text === "]" || t.text === ";" || (stopAtComma && t.text === ",")))
149
+ break;
150
+ if (this.p > startP && this.stopsAt(stop))
151
+ break;
152
+ }
153
+ depth += this.depthChange(t);
154
+ if (t.kind === "ref")
155
+ refs.push(t.part);
156
+ this.p++;
157
+ }
158
+ if (this.p === startP)
159
+ this.fail("expected an expression");
160
+ return this.span(from, refs);
161
+ }
162
+ isOp(text, n = 0) {
163
+ const t = this.peek(n);
164
+ return t !== undefined && t.kind === "op" && t.text === text;
165
+ }
166
+ /** A ColId: an identifier, or a key word that is neither reserved nor a type/function name word. */
167
+ colId(t, what) {
168
+ if (!isNameToken(t))
169
+ this.fail(`expected ${what}`, t);
170
+ const k = t.kind === "ident" ? keywordCategory(t.text) : undefined;
171
+ if (k === "R" || k === "T")
172
+ this.fail(`expected ${what} ('${t.text}' is a reserved key word; quote it to use it as a name)`, t);
173
+ }
174
+ nameTok(what) {
175
+ const t = this.next();
176
+ this.colId(t, what);
177
+ const i = this.idx(-1);
178
+ return { text: t.kind === "ref" ? "" : identValue(t), span: { from: i, to: i + 1, refs: t.kind === "ref" ? [t.part] : [] } };
179
+ }
180
+ /** `name`, `schema.name`, `db.schema.name`. The first piece is a ColId; a piece after a dot may be any word. */
181
+ qualifiedName(what = "a name") {
182
+ const from = this.idx();
183
+ const refs = [];
184
+ const pieces = [];
185
+ const one = () => {
186
+ const t = this.next();
187
+ if (pieces.length === 0)
188
+ this.colId(t, what);
189
+ else if (!isNameToken(t))
190
+ this.fail(`expected ${what}`, t);
191
+ if (t.kind === "ref")
192
+ refs.push(t.part);
193
+ pieces.push(t.kind === "ref" ? "" : identValue(t));
194
+ };
195
+ one();
196
+ while (this.isPunct(".") && isNameToken(this.peek(1))) {
197
+ this.p++;
198
+ one();
199
+ }
200
+ if (this.isPunct("."))
201
+ this.fail(`expected ${what}`, this.peek(1));
202
+ return { pieces, span: this.span(from, refs) };
203
+ }
204
+ ifNotExists() {
205
+ return this.acceptSeq("IF", "NOT", "EXISTS");
206
+ }
207
+ checkedParens() {
208
+ const span = this.parenthesized();
209
+ this.checkSpan(span);
210
+ return span;
211
+ }
212
+ /** `(a, b, c)`: a list of names. */
213
+ nameList(what = "a column name") {
214
+ this.expectPunct("(");
215
+ const out = [];
216
+ for (;;) {
217
+ out.push((({ text, span }) => ({ name: text, span }))(this.nameTok(what)));
218
+ if (this.acceptPunct(","))
219
+ continue;
220
+ this.expectPunct(")");
221
+ return out;
222
+ }
223
+ }
224
+ /** A comma-separated list of expression spans inside parentheses. */
225
+ parenList() {
226
+ this.expectPunct("(");
227
+ const out = [];
228
+ if (this.acceptPunct(")"))
229
+ return out;
230
+ for (;;) {
231
+ out.push(this.expr());
232
+ if (this.acceptPunct(","))
233
+ continue;
234
+ this.expectPunct(")");
235
+ return out;
236
+ }
237
+ }
238
+ /**
239
+ * `( name [= value] [, ...] )`: storage parameters (`WITH (fillfactor = 70)`)
240
+ * or index parameters. A name may be qualified (`toast.autovacuum_enabled`);
241
+ * a value is a number, a string, a word or a signed number.
242
+ */
243
+ reloptions() {
244
+ const from = this.idx();
245
+ this.expectPunct("(");
246
+ const refs = [];
247
+ for (;;) {
248
+ const t = this.next();
249
+ if (t.kind === "ref")
250
+ refs.push(t.part);
251
+ else if (t.kind !== "ident" && t.kind !== "qident")
252
+ this.fail("expected a storage parameter name", t);
253
+ if (this.acceptPunct(".")) {
254
+ const u = this.next();
255
+ if (u.kind !== "ident" && u.kind !== "qident")
256
+ this.fail("expected a storage parameter name", u);
257
+ }
258
+ if (this.isOp("=")) {
259
+ this.p++;
260
+ if (this.isOp("-") || this.isOp("+"))
261
+ this.p++;
262
+ const v = this.next();
263
+ if (v.kind === "ref")
264
+ refs.push(v.part);
265
+ else if (!["number", "string", "ident", "qident"].includes(v.kind))
266
+ this.fail("expected a storage parameter value", v);
267
+ }
268
+ if (this.acceptPunct(","))
269
+ continue;
270
+ this.expectPunct(")");
271
+ break;
272
+ }
273
+ // The span keeps the parentheses: `(fillfactor = 70)`.
274
+ return this.span(from, refs);
275
+ }
276
+ /** Constraint attributes after a constraint, recorded on it. */
277
+ constraintAttributes(node) {
278
+ for (;;) {
279
+ if (this.acceptSeq("NOT", "DEFERRABLE"))
280
+ node.deferrable = false;
281
+ else if (this.accept("DEFERRABLE"))
282
+ node.deferrable = true;
283
+ else if (this.acceptSeq("INITIALLY", "DEFERRED"))
284
+ node.initiallyDeferred = true;
285
+ else if (this.acceptSeq("INITIALLY", "IMMEDIATE"))
286
+ node.initiallyDeferred = false;
287
+ else if (this.acceptSeq("NOT", "VALID"))
288
+ node.notValid = true;
289
+ else if (this.acceptSeq("NO", "INHERIT"))
290
+ node.noInherit = true;
291
+ else if (this.acceptSeq("NOT", "ENFORCED"))
292
+ node.notEnforced = true;
293
+ else if (this.accept("ENFORCED"))
294
+ node.notEnforced = false;
295
+ else if (this.accept("INCLUDE"))
296
+ node.include = this.parenthesized();
297
+ else if (kw(this.peek(), "WITH") && this.isPunct("(", 1)) {
298
+ this.p++;
299
+ node.with = this.reloptions();
300
+ }
301
+ else if (this.acceptSeq("USING", "INDEX", "TABLESPACE"))
302
+ this.nameTok("a tablespace");
303
+ else
304
+ break;
305
+ }
306
+ }
307
+ references() {
308
+ const table = this.qualifiedName("a referenced table");
309
+ const node = { table, columns: [] };
310
+ if (this.isPunct("(")) {
311
+ this.expectPunct("(");
312
+ for (;;) {
313
+ this.accept("PERIOD");
314
+ const { text, span } = this.nameTok("a referenced column");
315
+ node.columns.push({ name: text, span });
316
+ if (this.acceptPunct(","))
317
+ continue;
318
+ this.expectPunct(")");
319
+ break;
320
+ }
321
+ }
322
+ if (this.accept("MATCH")) {
323
+ if (!this.accept("FULL", "PARTIAL", "SIMPLE"))
324
+ this.fail("expected FULL, PARTIAL or SIMPLE");
325
+ node.match = this.tokens[this.idx(-1)].text.toUpperCase();
326
+ }
327
+ for (;;) {
328
+ if (this.acceptSeq("ON", "DELETE"))
329
+ node.onDelete = this.fkAction();
330
+ else if (this.acceptSeq("ON", "UPDATE"))
331
+ node.onUpdate = this.fkAction();
332
+ else
333
+ break;
334
+ }
335
+ return node;
336
+ }
337
+ fkAction() {
338
+ const from = this.idx();
339
+ if (this.acceptSeq("NO", "ACTION") || this.accept("RESTRICT") || this.accept("CASCADE"))
340
+ return this.span(from, []);
341
+ if (this.acceptSeq("SET", "NULL") || this.acceptSeq("SET", "DEFAULT")) {
342
+ if (this.isPunct("("))
343
+ this.nameList();
344
+ return this.span(from, []);
345
+ }
346
+ return this.fail("expected NO ACTION, RESTRICT, CASCADE, SET NULL or SET DEFAULT");
347
+ }
348
+ /** `(a, b [, c WITHOUT OVERLAPS | PERIOD c])` for a key. */
349
+ keyColumns() {
350
+ this.expectPunct("(");
351
+ const out = [];
352
+ for (;;) {
353
+ this.accept("PERIOD");
354
+ const { text, span } = this.nameTok("a column name");
355
+ out.push({ name: text, span });
356
+ this.acceptSeq("WITHOUT", "OVERLAPS");
357
+ if (this.acceptPunct(","))
358
+ continue;
359
+ this.expectPunct(")");
360
+ return out;
361
+ }
362
+ }
363
+ /** A table constraint, after an optional `CONSTRAINT name`. Undefined when the element is not one. */
364
+ tableConstraint() {
365
+ const from = this.idx();
366
+ let name;
367
+ if (kw(this.peek(), "CONSTRAINT")) {
368
+ this.p++;
369
+ name = this.nameTok("a constraint name").text;
370
+ }
371
+ const at = (kind, extra = {}) => ({
372
+ name,
373
+ kind,
374
+ columns: [],
375
+ ...extra,
376
+ span: { from, to: from, refs: [] },
377
+ });
378
+ let node;
379
+ if (this.accept("CHECK"))
380
+ node = at("CHECK", { expr: this.checkedParens() });
381
+ else if (this.acceptSeq("PRIMARY", "KEY"))
382
+ node = at("PRIMARY KEY", { columns: this.keyColumns() });
383
+ else if (this.accept("UNIQUE")) {
384
+ // NULLS [NOT] DISTINCT comes before the column list.
385
+ const nnd = this.acceptSeq("NULLS", "NOT", "DISTINCT") ? true : this.acceptSeq("NULLS", "DISTINCT") ? false : undefined;
386
+ node = at("UNIQUE", { columns: this.keyColumns(), nullsNotDistinct: nnd });
387
+ }
388
+ else if (this.acceptSeq("FOREIGN", "KEY")) {
389
+ const columns = this.keyColumns();
390
+ this.expect("REFERENCES");
391
+ node = at("FOREIGN KEY", { columns, references: this.references() });
392
+ }
393
+ else if (kw(this.peek(), "EXCLUDE") && (this.isPunct("(", 1) || kw(this.peek(1), "USING"))) {
394
+ this.p++;
395
+ const using = this.accept("USING") ? this.nameTok("an access method").text : undefined;
396
+ if (this.isPunct("(") && this.isPunct(")", 1))
397
+ this.fail("expected an exclusion element", this.peek(1));
398
+ node = at("EXCLUDE", { expr: this.parenthesized(), using });
399
+ }
400
+ else if (this.acceptSeq("NOT", "NULL")) {
401
+ const { text, span } = this.nameTok("a column name");
402
+ node = at("NOT NULL", { columns: [{ name: text, span }] });
403
+ }
404
+ else {
405
+ if (name !== undefined)
406
+ this.fail("expected CHECK, PRIMARY KEY, UNIQUE, FOREIGN KEY, EXCLUDE or NOT NULL");
407
+ return undefined;
408
+ }
409
+ this.constraintAttributes(node);
410
+ if (node.kind === "EXCLUDE" && this.accept("WHERE")) {
411
+ node.where = this.checkedParens();
412
+ this.constraintAttributes(node);
413
+ }
414
+ node.span = this.span(from, []);
415
+ return node;
416
+ }
417
+ column(typed) {
418
+ const { text, span } = this.nameTok("a column name");
419
+ const col = { name: text, nameSpan: span, constraints: [] };
420
+ if (!typed)
421
+ this.acceptSeq("WITH", "OPTIONS");
422
+ const at = this.peek();
423
+ const bare = at === undefined || (at.kind === "punct" && (at.text === "," || at.text === ")"));
424
+ if (typed && !bare && !this.stopsAt(COLUMN_CLAUSE))
425
+ col.type = this.typeName();
426
+ else if (typed)
427
+ this.fail("expected a column type");
428
+ for (;;) {
429
+ if (this.accept("COMPRESSION"))
430
+ col.compression = this.expr(COLUMN_CLAUSE);
431
+ else if (this.accept("STORAGE"))
432
+ col.storage = this.expr(COLUMN_CLAUSE);
433
+ else if (this.accept("COLLATE"))
434
+ col.collate = this.expr(COLUMN_CLAUSE);
435
+ else if (!this.columnConstraint(col) && !this.unknownClause(COLUMN_WORDS, { inList: true, column: text || undefined }))
436
+ break;
437
+ }
438
+ const after = this.peek();
439
+ if (after && !(after.kind === "punct" && (after.text === "," || after.text === ")")))
440
+ this.fail("expected a column constraint, ',' or ')'");
441
+ return col;
442
+ }
443
+ /**
444
+ * A type name: `[schema.]name [(modifiers)]`, the SQL multi-word forms
445
+ * (`double precision`, `character varying`, `timestamp with time zone`,
446
+ * `interval day to second`, `bit varying`), `SETOF`-free, and array bounds
447
+ * (`int[]`, `int[3]`, `int ARRAY[3]`). An interpolated type is one token.
448
+ * A reserved word is never a type.
449
+ */
450
+ typeName() {
451
+ const from = this.idx();
452
+ const refs = [];
453
+ const t = this.next();
454
+ if (t.kind === "ref")
455
+ refs.push(t.part);
456
+ else if (t.kind !== "ident" && t.kind !== "qident")
457
+ this.fail("expected a type", t);
458
+ else if (t.kind === "ident" && keywordCategory(t.text) === "R")
459
+ this.fail("expected a type", t);
460
+ const first = t.kind === "ident" ? t.text.toUpperCase() : "";
461
+ if (t.kind !== "ref")
462
+ while (this.isPunct(".") && isNameToken(this.peek(1)))
463
+ this.p += 2;
464
+ const precision = () => {
465
+ if (this.isPunct("(") && this.isPunct(")", 1))
466
+ this.fail("expected a type modifier", this.peek(1));
467
+ if (this.isPunct("("))
468
+ refs.push(...this.parenthesized().refs);
469
+ };
470
+ if (first === "DOUBLE")
471
+ this.expect("PRECISION");
472
+ else if (["CHARACTER", "CHAR", "NCHAR", "NATIONAL", "BIT", "VARCHAR"].includes(first)) {
473
+ if (first === "NATIONAL")
474
+ this.expect("CHARACTER", "CHAR");
475
+ this.accept("VARYING");
476
+ precision();
477
+ }
478
+ else if (first === "TIME" || first === "TIMESTAMP") {
479
+ precision();
480
+ if (!this.acceptSeq("WITH", "TIME", "ZONE"))
481
+ this.acceptSeq("WITHOUT", "TIME", "ZONE");
482
+ }
483
+ else if (first === "INTERVAL") {
484
+ const FIELDS = ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"];
485
+ if (this.accept(...FIELDS) && this.accept("TO"))
486
+ this.expect(...FIELDS);
487
+ precision();
488
+ }
489
+ else
490
+ precision();
491
+ for (;;) {
492
+ if (this.isPunct("[")) {
493
+ this.p++;
494
+ if (this.peek()?.kind === "number")
495
+ this.p++;
496
+ this.expectPunct("]");
497
+ }
498
+ else if (this.accept("ARRAY")) {
499
+ if (this.isPunct("[")) {
500
+ this.p++;
501
+ if (this.peek()?.kind === "number")
502
+ this.p++;
503
+ this.expectPunct("]");
504
+ }
505
+ }
506
+ else
507
+ break;
508
+ }
509
+ return this.span(from, refs);
510
+ }
511
+ /** An expression span with the operand check {@link checkSpan} makes. */
512
+ checkedExpr(stop = [], stopAtComma = true) {
513
+ const span = this.expr(stop, stopAtComma);
514
+ this.checkSpan(span);
515
+ return span;
516
+ }
517
+ /** A `b_expr` (a column or domain default): an expression with none of {@link NOT_IN_B_EXPR} at its top level. */
518
+ bExpr(stop) {
519
+ const span = this.checkedExpr(stop);
520
+ let depth = 0;
521
+ const sig = [];
522
+ for (let i = span.from; i < span.to; i++) {
523
+ const t = this.tokens[i];
524
+ if (t.kind !== "ws" && t.kind !== "comment")
525
+ sig.push(t);
526
+ }
527
+ for (let k = 0; k < sig.length; k++) {
528
+ const t = sig[k];
529
+ depth += this.depthChange(t);
530
+ if (depth !== 0 || t.kind !== "ident")
531
+ continue;
532
+ const word = t.text.toUpperCase();
533
+ if (NOT_IN_B_EXPR.includes(word))
534
+ this.fail("expected the end of the default (a default cannot hold this operator unparenthesized)", t);
535
+ if (word === "IS") {
536
+ const ok = B_EXPR_IS.some((seq) => seq.every((w, n) => kw(sig[k + 1 + n], w)));
537
+ if (!ok)
538
+ this.fail("expected the end of the default (a default cannot hold IS unparenthesized)", t);
539
+ }
540
+ }
541
+ return span;
542
+ }
543
+ /**
544
+ * Since Postgres 14 removed postfix operators, an operand is never followed
545
+ * directly by a name that is not a key word, nor by another literal. That
546
+ * catches a misspelt key word after an expression (`DEFAULT 'x' CHEK (...)`)
547
+ * at its token, which a plain span would accept.
548
+ */
549
+ checkSpan(span) {
550
+ let prev;
551
+ const operandEnd = (x) => x.kind === "string" ||
552
+ x.kind === "number" ||
553
+ x.kind === "qident" ||
554
+ x.kind === "param" ||
555
+ x.kind === "ref" ||
556
+ (x.kind === "punct" && (x.text === ")" || x.text === "]")) ||
557
+ (x.kind === "ident" && !isKeywordToken(x));
558
+ for (let i = span.from; i < span.to; i++) {
559
+ const t = this.tokens[i];
560
+ if (t.kind === "ws" || t.kind === "comment")
561
+ continue;
562
+ if (prev && prev.kind === "punct" && prev.text === "," && t.kind === "punct" && t.text === ")")
563
+ this.fail("expected an expression", t);
564
+ // Two operands in a row: `z 0`, `(a) 'x'`. A name before a string is a typed literal (`date '2020-01-01'`).
565
+ if (prev && operandEnd(prev) && (t.kind === "number" || t.kind === "qident" || t.kind === "param" || (t.kind === "string" && prev.kind !== "ident"))) {
566
+ this.fail("expected an operator, a key word or the end of the expression", t);
567
+ }
568
+ if (prev && t.kind === "ident" && !isKeywordToken(t) && operandEnd(prev)) {
569
+ this.fail("expected an operator, a key word or the end of the expression", t);
570
+ }
571
+ prev = t;
572
+ }
573
+ }
574
+ /** One column constraint; false when there is none here. */
575
+ columnConstraint(col) {
576
+ const from = this.idx();
577
+ let name;
578
+ if (kw(this.peek(), "CONSTRAINT")) {
579
+ this.p++;
580
+ name = this.nameTok("a constraint name").text;
581
+ }
582
+ const push = (kind, extra = {}) => {
583
+ const node = { name, kind, columns: [], ...extra, span: { from, to: from, refs: [] } };
584
+ this.constraintAttributes(node);
585
+ node.span = this.span(from, []);
586
+ col.constraints.push(node);
587
+ };
588
+ if (this.acceptSeq("NOT", "NULL")) {
589
+ col.notNull = true;
590
+ if (name !== undefined)
591
+ col.notNullName = name;
592
+ this.acceptSeq("NO", "INHERIT");
593
+ }
594
+ else if (this.accept("NULL"))
595
+ col.notNull = false;
596
+ else if (this.accept("DEFAULT"))
597
+ col.default = this.bExpr(COLUMN_CLAUSE);
598
+ else if (kw(this.peek(), "GENERATED")) {
599
+ this.p++;
600
+ let always;
601
+ if (this.accept("ALWAYS"))
602
+ always = true;
603
+ else {
604
+ this.expect("BY");
605
+ this.expect("DEFAULT");
606
+ always = false;
607
+ }
608
+ this.expect("AS");
609
+ if (this.accept("IDENTITY")) {
610
+ col.generated = { kind: "identity", always, options: this.isPunct("(") ? this.parenthesized() : undefined };
611
+ }
612
+ else {
613
+ if (!always)
614
+ this.fail("expected IDENTITY (a generated column is GENERATED ALWAYS AS)");
615
+ const expr = this.checkedParens();
616
+ // Neither word is VIRTUAL, the default from 18 (the pinned major); 14 to 17 require STORED.
617
+ const kind = this.accept("STORED") ? "stored" : (this.accept("VIRTUAL"), "virtual");
618
+ col.generated = { kind, expr, always };
619
+ }
620
+ }
621
+ else if (this.accept("CHECK"))
622
+ push("CHECK", { expr: this.checkedParens() });
623
+ else if (this.accept("UNIQUE")) {
624
+ const nnd = this.acceptSeq("NULLS", "NOT", "DISTINCT") ? true : this.acceptSeq("NULLS", "DISTINCT") ? false : undefined;
625
+ push("UNIQUE", { nullsNotDistinct: nnd });
626
+ }
627
+ else if (this.acceptSeq("PRIMARY", "KEY"))
628
+ push("PRIMARY KEY");
629
+ else if (this.accept("REFERENCES"))
630
+ push("FOREIGN KEY", { references: this.references() });
631
+ else if (name === undefined && col.constraints.length > 0 && kw(this.peek(), "DEFERRABLE", "INITIALLY", "NOT")) {
632
+ // `DEFERRABLE` / `INITIALLY ...` written after the constraint it qualifies.
633
+ const last = col.constraints[col.constraints.length - 1];
634
+ const before = this.p;
635
+ this.constraintAttributes(last);
636
+ if (this.p === before)
637
+ return false;
638
+ }
639
+ else {
640
+ if (name !== undefined)
641
+ this.fail("expected a column constraint");
642
+ return false;
643
+ }
644
+ return true;
645
+ }
646
+ tableElements(node, typed) {
647
+ this.expectPunct("(");
648
+ if (this.acceptPunct(")"))
649
+ return;
650
+ for (;;) {
651
+ if (this.accept("LIKE"))
652
+ node.like.push(this.likeClause());
653
+ else {
654
+ const c = this.tableConstraint();
655
+ if (c)
656
+ node.constraints.push(c);
657
+ else
658
+ node.columns.push(this.column(typed));
659
+ }
660
+ if (this.acceptPunct(","))
661
+ continue;
662
+ this.expectPunct(")");
663
+ return;
664
+ }
665
+ }
666
+ /** `LIKE source [ { INCLUDING | EXCLUDING } option ... ]`, as written after `LIKE`. */
667
+ likeClause() {
668
+ const from = this.idx();
669
+ const source = this.qualifiedName("a table name");
670
+ while (this.accept("INCLUDING", "EXCLUDING")) {
671
+ if (!this.accept(...LIKE_OPTIONS))
672
+ this.fail(`expected ${LIKE_OPTIONS.join(", ")}`);
673
+ }
674
+ return this.span(from, source.span.refs);
675
+ }
676
+ persistence() {
677
+ this.accept("GLOBAL", "LOCAL");
678
+ if (this.accept("TEMPORARY", "TEMP"))
679
+ return "temporary";
680
+ if (this.accept("UNLOGGED"))
681
+ return "unlogged";
682
+ return undefined;
683
+ }
684
+ parse() {
685
+ if (this.accept("COMMENT"))
686
+ return this.comment();
687
+ if (this.accept("GRANT"))
688
+ return this.grant(false, false);
689
+ if (this.accept("REVOKE"))
690
+ return this.grant(true, false);
691
+ if (this.acceptSeq("ALTER", "DEFAULT", "PRIVILEGES"))
692
+ return this.defaultPrivileges();
693
+ if (this.acceptSeq("ALTER", "TABLE"))
694
+ return this.rowSecurity();
695
+ this.expect("CREATE");
696
+ const orReplace = this.acceptSeq("OR", "REPLACE");
697
+ if (!orReplace && this.accept("POLICY"))
698
+ return this.policy();
699
+ if (!orReplace && this.accept("ROLE"))
700
+ return this.role();
701
+ if (!orReplace && kw(this.peek(), "USER", "GROUP"))
702
+ this.fail("expected ROLE (declare a role with CREATE ROLE; LOGIN is an option of it)");
703
+ if (this.accept("FUNCTION"))
704
+ return this.routine("function", orReplace);
705
+ if (this.accept("PROCEDURE"))
706
+ return this.routine("procedure", orReplace);
707
+ if (this.accept("AGGREGATE"))
708
+ return this.aggregate(orReplace);
709
+ if (this.accept("TRIGGER"))
710
+ return this.trigger(orReplace, false);
711
+ if (this.acceptSeq("CONSTRAINT", "TRIGGER"))
712
+ return this.trigger(orReplace, true);
713
+ if (!orReplace) {
714
+ if (this.accept("SCHEMA"))
715
+ return this.schema();
716
+ if (this.accept("EXTENSION"))
717
+ return this.extension();
718
+ if (this.accept("DOMAIN"))
719
+ return this.domain();
720
+ if (this.accept("TYPE"))
721
+ return this.typeDef();
722
+ if (this.accept("COLLATION"))
723
+ return this.collation();
724
+ if (this.accept("CAST"))
725
+ return this.cast();
726
+ if (this.acceptSeq("EVENT", "TRIGGER"))
727
+ return this.eventTrigger();
728
+ if (kw(this.peek(), "UNIQUE", "INDEX"))
729
+ return this.index();
730
+ }
731
+ const persistence = this.persistence();
732
+ if (!orReplace && this.accept("TABLE"))
733
+ return this.table(persistence);
734
+ if (!orReplace && this.accept("SEQUENCE"))
735
+ return this.sequence(persistence);
736
+ if (persistence === "unlogged")
737
+ return this.fail("expected TABLE or SEQUENCE");
738
+ const recursive = this.accept("RECURSIVE");
739
+ if (!orReplace && !recursive && persistence === undefined && this.acceptSeq("MATERIALIZED", "VIEW"))
740
+ return this.view(true, false, false, false);
741
+ if (this.accept("VIEW"))
742
+ return this.view(false, orReplace, persistence === "temporary", recursive);
743
+ return this.fail("expected SCHEMA, TABLE, INDEX, VIEW, MATERIALIZED VIEW, SEQUENCE, TYPE, DOMAIN, EXTENSION, FUNCTION, PROCEDURE, AGGREGATE, TRIGGER, EVENT TRIGGER, COLLATION, CAST, POLICY or ROLE");
744
+ }
745
+ schema() {
746
+ const node = { statement: "schema", ifNotExists: this.ifNotExists() };
747
+ if (!kw(this.peek(), "AUTHORIZATION"))
748
+ node.name = this.qualifiedName("a schema name");
749
+ if (this.accept("AUTHORIZATION")) {
750
+ const from = this.idx();
751
+ const t = this.next();
752
+ if (!isNameToken(t))
753
+ this.fail("expected a role name", t);
754
+ node.authorization = this.span(from, t.kind === "ref" ? [t.part] : []);
755
+ }
756
+ if (kw(this.peek(), "CREATE", "GRANT"))
757
+ this.fail("schema elements inside CREATE SCHEMA are not declared here; declare each object on its own");
758
+ return node;
759
+ }
760
+ extension() {
761
+ const node = { statement: "extension", ifNotExists: this.ifNotExists(), name: this.qualifiedName("an extension name"), cascade: false };
762
+ this.accept("WITH");
763
+ for (;;) {
764
+ if (this.accept("SCHEMA"))
765
+ node.schema = this.nameTok("a schema name").span;
766
+ else if (this.accept("VERSION")) {
767
+ const from = this.idx();
768
+ const t = this.next();
769
+ if (!["ident", "qident", "string", "ref"].includes(t.kind))
770
+ this.fail("expected a version", t);
771
+ node.version = this.span(from, t.kind === "ref" ? [t.part] : []);
772
+ }
773
+ else if (this.accept("CASCADE"))
774
+ node.cascade = true;
775
+ else
776
+ return node;
777
+ }
778
+ }
779
+ domain() {
780
+ const name = this.qualifiedName("a domain name");
781
+ this.accept("AS");
782
+ const STOP = ["COLLATE", "DEFAULT", "CONSTRAINT", "NOT", "NULL", "CHECK"];
783
+ const node = { statement: "domain", name, type: this.typeName(), checks: [] };
784
+ for (;;) {
785
+ if (this.accept("COLLATE"))
786
+ node.collate = this.expr(STOP);
787
+ else if (this.accept("DEFAULT"))
788
+ node.default = this.bExpr(STOP);
789
+ else {
790
+ let cname;
791
+ if (this.accept("CONSTRAINT"))
792
+ cname = this.nameTok("a constraint name").text;
793
+ if (this.acceptSeq("NOT", "NULL"))
794
+ node.notNull = true;
795
+ else if (this.accept("NULL"))
796
+ node.notNull = false;
797
+ else if (this.accept("CHECK")) {
798
+ const expr = this.checkedParens();
799
+ node.checks.push({ name: cname, expr, ...(this.acceptSeq("NOT", "VALID") ? { notValid: true } : {}) });
800
+ }
801
+ else if (cname !== undefined)
802
+ this.fail("expected NOT NULL, NULL or CHECK");
803
+ else
804
+ return node;
805
+ }
806
+ }
807
+ }
808
+ /** `CREATE TYPE name AS ENUM (...)`, `AS (...)` (a composite type) or `AS RANGE (...)`. */
809
+ typeDef() {
810
+ const name = this.qualifiedName("a type name");
811
+ if (!this.accept("AS")) {
812
+ if (this.isPunct("("))
813
+ this.fail("expected AS (a base type, CREATE TYPE name (INPUT = ...), is written in C and is not declared here; declare ENUM, RANGE or composite types, or a domain with its own tag)");
814
+ this.fail("expected AS (a shell type, CREATE TYPE name with nothing after it, is not declared on its own)");
815
+ }
816
+ if (this.accept("RANGE")) {
817
+ const options = this.definition("a range option");
818
+ this.checkDefinition(options, RANGE_OPTIONS, ["subtype"], "a range type");
819
+ return { statement: "range", name, options };
820
+ }
821
+ if (this.isPunct("("))
822
+ return this.composite(name);
823
+ if (!this.accept("ENUM"))
824
+ this.fail("expected ENUM, RANGE or '(' (a domain has its own tag)");
825
+ const labels = this.parenList();
826
+ for (const l of labels) {
827
+ const sig = this.tokens.slice(l.from, l.to).filter((t) => t.kind !== "ws" && t.kind !== "comment");
828
+ if (sig.length !== 1 || (sig[0].kind !== "string" && sig[0].kind !== "ref"))
829
+ this.fail("expected a string label", sig[0]);
830
+ }
831
+ return { statement: "enum", name, labels };
832
+ }
833
+ /** A composite type's attribute list, after `AS`. */
834
+ composite(name) {
835
+ const node = { statement: "composite", name, attributes: [] };
836
+ this.expectPunct("(");
837
+ if (this.acceptPunct(")"))
838
+ return node;
839
+ for (;;) {
840
+ const { text, span } = this.nameTok("an attribute name");
841
+ const attr = { name: text, nameSpan: span, type: this.typeName() };
842
+ if (this.accept("COLLATE"))
843
+ attr.collate = this.qualifiedName("a collation").span;
844
+ node.attributes.push(attr);
845
+ if (this.acceptPunct(","))
846
+ continue;
847
+ if (!this.isPunct(")"))
848
+ this.fail("expected COLLATE, ',' or ')' (an attribute of a composite type has a name, a type and a collation, and no constraints)");
849
+ this.expectPunct(")");
850
+ return node;
851
+ }
852
+ }
853
+ /**
854
+ * `( name [= value] [, ...] )`: a definition list, as `CREATE AGGREGATE`,
855
+ * `CREATE COLLATION` and `CREATE TYPE ... AS RANGE` take it. A value is an
856
+ * expression span: a type, a function or operator name, a string, a number.
857
+ */
858
+ definition(what) {
859
+ this.expectPunct("(");
860
+ const out = [];
861
+ if (this.acceptPunct(")"))
862
+ return out;
863
+ for (;;) {
864
+ const from = this.idx();
865
+ const t = this.next();
866
+ if (t.kind !== "ident" && t.kind !== "qident")
867
+ this.fail(`expected ${what}`, t);
868
+ const elem = { name: identValue(t), nameSpan: this.span(from, []) };
869
+ if (this.isOp("=")) {
870
+ this.p++;
871
+ elem.value = this.expr();
872
+ }
873
+ out.push(elem);
874
+ if (this.acceptPunct(","))
875
+ continue;
876
+ this.expectPunct(")");
877
+ return out;
878
+ }
879
+ }
880
+ /** A definition list's names against the ones the statement takes: an unknown or repeated one fails at its name, a missing required one at the end. */
881
+ checkDefinition(elems, known, required, what) {
882
+ const seen = new Set();
883
+ for (const e of elems) {
884
+ const at = this.tokens[e.nameSpan.from];
885
+ if (!known.includes(e.name))
886
+ this.fail(`expected an option of ${what} (${known.join(", ")})`, at);
887
+ if (seen.has(e.name))
888
+ this.fail(`${e.name} is given twice`, at);
889
+ seen.add(e.name);
890
+ }
891
+ for (const r of required)
892
+ if (!seen.has(r))
893
+ this.fail(`expected ${r.toUpperCase()} (${what} needs it)`, this.lastSeen());
894
+ }
895
+ aggregate(orReplace) {
896
+ const name = this.qualifiedName("an aggregate name");
897
+ const node = { statement: "aggregate", orReplace, name, args: [], star: false, options: [] };
898
+ this.expectPunct("(");
899
+ const t = this.peek();
900
+ if ((t?.kind === "ident" || t?.kind === "qident") && this.isOp("=", 1)) {
901
+ this.fail("expected the argument types (the old form, CREATE AGGREGATE name (BASETYPE = ..., SFUNC = ...), is not declared; write name(argtype) (SFUNC = ..., STYPE = ...))");
902
+ }
903
+ const args = (into) => {
904
+ for (;;) {
905
+ const at = this.peek();
906
+ const arg = this.routineArg();
907
+ if (arg.mode === "OUT" || arg.mode === "INOUT")
908
+ this.fail("expected IN or VARIADIC (an aggregate's arguments are inputs)", at);
909
+ if (arg.default)
910
+ this.fail("expected ',' or ')' (an aggregate's argument takes no default)", this.lastSeen());
911
+ into.push(arg);
912
+ if (!this.acceptPunct(","))
913
+ return;
914
+ }
915
+ };
916
+ if (this.isOp("*")) {
917
+ this.p++;
918
+ node.star = true;
919
+ }
920
+ else if (!this.is("ORDER", "BY"))
921
+ args(node.args);
922
+ if (!node.star && this.acceptSeq("ORDER", "BY")) {
923
+ node.orderBy = [];
924
+ args(node.orderBy);
925
+ }
926
+ this.expectPunct(")");
927
+ node.options = this.definition("an aggregate option");
928
+ this.checkDefinition(node.options, AGGREGATE_OPTIONS, ["sfunc", "stype"], "an aggregate");
929
+ return node;
930
+ }
931
+ collation() {
932
+ const ifNotExists = this.ifNotExists();
933
+ const name = this.qualifiedName("a collation name");
934
+ if (this.accept("FROM")) {
935
+ this.fail("expected '(' (CREATE COLLATION ... FROM copies another collation's settings, which the server keeps as the copy's own; write them: (provider = ..., locale = ...))", this.lastSeen());
936
+ }
937
+ const options = this.definition("a collation option");
938
+ this.checkDefinition(options, COLLATION_OPTIONS, [], "a collation");
939
+ return { statement: "collation", ifNotExists, name, options };
940
+ }
941
+ cast() {
942
+ this.expectPunct("(");
943
+ const source = this.typeName();
944
+ this.expect("AS");
945
+ const target = this.typeName();
946
+ this.expectPunct(")");
947
+ const node = { statement: "cast", source, target, method: "function", context: "explicit" };
948
+ if (this.acceptSeq("WITH", "FUNCTION")) {
949
+ node.function = this.qualifiedName("a cast function");
950
+ if (this.isPunct("("))
951
+ node.functionArgs = this.parenthesized();
952
+ }
953
+ else if (this.acceptSeq("WITHOUT", "FUNCTION"))
954
+ node.method = "binary";
955
+ else if (this.acceptSeq("WITH", "INOUT"))
956
+ node.method = "inout";
957
+ else
958
+ this.fail("expected WITH FUNCTION, WITHOUT FUNCTION or WITH INOUT");
959
+ if (this.acceptSeq("AS", "ASSIGNMENT"))
960
+ node.context = "assignment";
961
+ else if (this.acceptSeq("AS", "IMPLICIT"))
962
+ node.context = "implicit";
963
+ return node;
964
+ }
965
+ eventTrigger() {
966
+ const { text, span } = this.nameTok("an event trigger name");
967
+ if (this.isPunct("."))
968
+ this.fail("expected ON (an event trigger's name is not schema-qualified; it is the database's)");
969
+ this.expect("ON");
970
+ const event = this.nameTok("an event");
971
+ const node = { statement: "eventTrigger", name: { pieces: [text], span }, event: { name: event.text, span: event.span }, filters: [], function: { pieces: [], span: { from: 0, to: 0, refs: [] } } };
972
+ if (this.accept("WHEN")) {
973
+ do {
974
+ const variable = this.nameTok("a filter variable");
975
+ if (variable.text !== "tag")
976
+ this.fail("expected TAG (the only filter variable an event trigger has)", this.tokens[variable.span.from]);
977
+ this.expect("IN");
978
+ const values = this.parenList();
979
+ for (const v of values) {
980
+ const sig = this.tokens.slice(v.from, v.to).filter((t) => t.kind !== "ws" && t.kind !== "comment");
981
+ if (sig.length !== 1 || (sig[0].kind !== "string" && sig[0].kind !== "ref"))
982
+ this.fail("expected a command tag as a string ('CREATE TABLE')", sig[0]);
983
+ }
984
+ node.filters.push({ variable: variable.text, values });
985
+ } while (this.accept("AND"));
986
+ }
987
+ this.expect("EXECUTE");
988
+ this.expect("FUNCTION", "PROCEDURE");
989
+ node.function = this.qualifiedName("an event trigger function");
990
+ this.expectPunct("(");
991
+ if (!this.isPunct(")"))
992
+ this.fail("expected ')' (an event trigger's function takes no arguments)");
993
+ this.expectPunct(")");
994
+ return node;
995
+ }
996
+ table(persistence) {
997
+ const ifNotExists = this.ifNotExists();
998
+ const node = { statement: "table", persistence, ifNotExists, name: this.qualifiedName("a table name"), columns: [], constraints: [], like: [] };
999
+ if (this.accept("OF")) {
1000
+ node.ofType = this.qualifiedName("a type name");
1001
+ if (this.isPunct("("))
1002
+ this.tableElements(node, false);
1003
+ }
1004
+ else if (this.acceptSeq("PARTITION", "OF")) {
1005
+ node.partitionOf = this.qualifiedName("a partitioned table");
1006
+ if (this.isPunct("("))
1007
+ this.tableElements(node, false);
1008
+ node.partitionBound = this.partitionBound();
1009
+ }
1010
+ else {
1011
+ if (!this.isPunct("("))
1012
+ this.fail("expected the column list (CREATE TABLE ... AS is not declared here)");
1013
+ this.tableElements(node, true);
1014
+ }
1015
+ for (;;) {
1016
+ if (this.accept("INHERITS")) {
1017
+ this.expectPunct("(");
1018
+ node.inherits = [];
1019
+ do
1020
+ node.inherits.push(this.qualifiedName("a parent table"));
1021
+ while (this.acceptPunct(","));
1022
+ this.expectPunct(")");
1023
+ }
1024
+ else if (this.acceptSeq("PARTITION", "BY")) {
1025
+ const from = this.idx();
1026
+ this.expect("RANGE", "LIST", "HASH");
1027
+ if (this.isPunct(")", 1))
1028
+ this.fail("expected a partition key", this.peek(1));
1029
+ node.partitionBy = this.span(from, this.parenthesized().refs);
1030
+ }
1031
+ else if (this.accept("USING"))
1032
+ node.using = this.nameTok("an access method").span;
1033
+ else if (kw(this.peek(), "WITH") && this.isPunct("(", 1)) {
1034
+ this.p++;
1035
+ node.with = this.reloptions();
1036
+ }
1037
+ else if (this.acceptSeq("WITHOUT", "OIDS"))
1038
+ continue;
1039
+ else if (this.acceptSeq("ON", "COMMIT")) {
1040
+ const from = this.idx();
1041
+ if (!(this.acceptSeq("PRESERVE", "ROWS") || this.acceptSeq("DELETE", "ROWS") || this.accept("DROP")))
1042
+ this.fail("expected PRESERVE ROWS, DELETE ROWS or DROP");
1043
+ node.onCommit = this.span(from, []);
1044
+ }
1045
+ else if (this.accept("TABLESPACE"))
1046
+ node.tablespace = this.nameTok("a tablespace").span;
1047
+ else if (!this.unknownClause(TABLE_WORDS))
1048
+ break;
1049
+ }
1050
+ return node;
1051
+ }
1052
+ /** `DEFAULT`, or `FOR VALUES IN (...)`, `FROM (...) TO (...)` or `WITH (MODULUS n, REMAINDER m)`. */
1053
+ partitionBound() {
1054
+ const from = this.idx();
1055
+ if (this.accept("DEFAULT"))
1056
+ return this.span(from, []);
1057
+ this.expect("FOR");
1058
+ this.expect("VALUES");
1059
+ const refs = [];
1060
+ const values = () => {
1061
+ if (this.isPunct("(") && this.isPunct(")", 1))
1062
+ this.fail("expected a partition bound value", this.peek(1));
1063
+ refs.push(...this.checkedParens().refs);
1064
+ };
1065
+ if (this.accept("IN"))
1066
+ values();
1067
+ else if (this.accept("FROM")) {
1068
+ values();
1069
+ this.expect("TO");
1070
+ values();
1071
+ }
1072
+ else if (this.accept("WITH")) {
1073
+ this.expectPunct("(");
1074
+ for (const w of ["MODULUS", "REMAINDER"]) {
1075
+ if (!this.accept(w))
1076
+ this.fail(`expected ${w}`);
1077
+ const t = this.next();
1078
+ if (t.kind !== "number" && t.kind !== "ref")
1079
+ this.fail("expected a number", t);
1080
+ if (w === "MODULUS")
1081
+ this.expectPunct(",");
1082
+ }
1083
+ this.expectPunct(")");
1084
+ }
1085
+ else
1086
+ this.fail("expected IN, FROM or WITH");
1087
+ return this.span(from, refs);
1088
+ }
1089
+ index() {
1090
+ const unique = this.accept("UNIQUE");
1091
+ this.expect("INDEX");
1092
+ const concurrently = this.accept("CONCURRENTLY");
1093
+ const ifNotExists = this.ifNotExists();
1094
+ let name;
1095
+ if (!(kw(this.peek(), "ON") && !ifNotExists)) {
1096
+ // An index lives in its table's schema: CREATE INDEX takes an unqualified name.
1097
+ const { text, span } = this.nameTok("an index name");
1098
+ if (this.isPunct("."))
1099
+ this.fail("expected ON (an index name is not schema-qualified; the index is created in its table's schema)");
1100
+ name = { pieces: [text], span };
1101
+ }
1102
+ this.expect("ON");
1103
+ const only = this.accept("ONLY");
1104
+ const node = { statement: "index", unique, concurrently, ifNotExists, name, only, table: this.qualifiedName("a table name"), elements: [] };
1105
+ if (this.accept("USING"))
1106
+ node.using = this.nameTok("an access method").text;
1107
+ if (this.isPunct("(") && this.isPunct(")", 1))
1108
+ this.fail("expected an index column or expression", this.peek(1));
1109
+ for (const span of this.parenList()) {
1110
+ const sig = this.tokens.slice(span.from, span.to).filter((t) => t.kind !== "ws" && t.kind !== "comment");
1111
+ const first = sig[0];
1112
+ // A column element is a bare name, then only opclass, collation and ordering words.
1113
+ const column = first && (first.kind === "ident" || first.kind === "qident") && sig.slice(1).every((t) => t.kind === "ident" || t.kind === "qident")
1114
+ ? identValue(first)
1115
+ : undefined;
1116
+ node.elements.push({ span, ...(column !== undefined ? { column } : {}) });
1117
+ }
1118
+ for (;;) {
1119
+ if (this.accept("INCLUDE"))
1120
+ node.include = this.parenthesized();
1121
+ else if (this.acceptSeq("NULLS", "NOT", "DISTINCT"))
1122
+ node.nullsNotDistinct = true;
1123
+ else if (this.acceptSeq("NULLS", "DISTINCT"))
1124
+ node.nullsNotDistinct = false;
1125
+ else if (kw(this.peek(), "WITH") && this.isPunct("(", 1)) {
1126
+ this.p++;
1127
+ node.with = this.reloptions();
1128
+ }
1129
+ else if (this.accept("TABLESPACE"))
1130
+ node.tablespace = this.nameTok("a tablespace").span;
1131
+ else if (this.accept("WHERE"))
1132
+ node.where = this.checkedExpr([], false);
1133
+ else if (!this.unknownClause(INDEX_WORDS))
1134
+ return node;
1135
+ }
1136
+ }
1137
+ view(materialized, orReplace, temporary, recursive) {
1138
+ const ifNotExists = materialized ? this.ifNotExists() : false;
1139
+ const node = {
1140
+ statement: "view",
1141
+ materialized,
1142
+ orReplace,
1143
+ temporary,
1144
+ recursive,
1145
+ ifNotExists,
1146
+ name: this.qualifiedName("a view name"),
1147
+ columnNames: [],
1148
+ query: { from: 0, to: 0, refs: [] },
1149
+ };
1150
+ if (this.isPunct("("))
1151
+ node.columnNames = this.nameList();
1152
+ for (;;) {
1153
+ if (materialized && this.accept("USING"))
1154
+ node.using = this.nameTok("an access method").span;
1155
+ else if (kw(this.peek(), "WITH") && this.isPunct("(", 1)) {
1156
+ this.p++;
1157
+ node.with = this.reloptions();
1158
+ }
1159
+ else if (materialized && this.accept("TABLESPACE"))
1160
+ node.tablespace = this.nameTok("a tablespace").span;
1161
+ else if (!this.unknownClause(VIEW_WORDS))
1162
+ break;
1163
+ }
1164
+ this.expect("AS");
1165
+ const tail = materialized
1166
+ ? ["WITH DATA", "WITH NO DATA"]
1167
+ : ["WITH CHECK OPTION", "WITH CASCADED CHECK OPTION", "WITH LOCAL CHECK OPTION"];
1168
+ node.query = this.expr(tail, false);
1169
+ if (materialized) {
1170
+ if (this.acceptSeq("WITH", "DATA"))
1171
+ node.withData = true;
1172
+ else if (this.acceptSeq("WITH", "NO", "DATA"))
1173
+ node.withData = false;
1174
+ }
1175
+ else if (kw(this.peek(), "WITH")) {
1176
+ const from = this.idx();
1177
+ this.p++;
1178
+ this.accept("CASCADED", "LOCAL");
1179
+ this.expect("CHECK");
1180
+ this.expect("OPTION");
1181
+ node.checkOption = this.span(from, []);
1182
+ }
1183
+ return node;
1184
+ }
1185
+ sequence(persistence) {
1186
+ const node = { statement: "sequence", persistence, ifNotExists: this.ifNotExists(), name: this.qualifiedName("a sequence name"), options: [] };
1187
+ const STOP = ["AS", "INCREMENT", "MINVALUE", "MAXVALUE", "NO", "START", "RESTART", "CACHE", "CYCLE", "OWNED", "SEQUENCE", "LOGGED", "UNLOGGED"];
1188
+ const value = () => {
1189
+ const span = this.expr(STOP);
1190
+ this.checkSpan(span);
1191
+ return span;
1192
+ };
1193
+ for (;;) {
1194
+ if (this.atEnd())
1195
+ return node;
1196
+ if (this.accept("AS"))
1197
+ node.options.push({ option: "AS", value: this.typeName() });
1198
+ else if (this.accept("INCREMENT")) {
1199
+ this.accept("BY");
1200
+ node.options.push({ option: "INCREMENT", value: value() });
1201
+ }
1202
+ else if (this.acceptSeq("NO", "MINVALUE") || this.acceptSeq("NO", "MAXVALUE") || this.acceptSeq("NO", "CYCLE")) {
1203
+ node.options.push({ option: `NO ${this.tokens[this.idx(-1)].text.toUpperCase()}` });
1204
+ }
1205
+ else if (this.accept("MINVALUE", "MAXVALUE", "CACHE")) {
1206
+ node.options.push({ option: this.tokens[this.idx(-1)].text.toUpperCase(), value: value() });
1207
+ }
1208
+ else if (this.accept("START")) {
1209
+ this.accept("WITH");
1210
+ node.options.push({ option: "START", value: value() });
1211
+ }
1212
+ else if (this.accept("CYCLE"))
1213
+ node.options.push({ option: "CYCLE" });
1214
+ else if (this.acceptSeq("OWNED", "BY")) {
1215
+ if (this.accept("NONE"))
1216
+ node.options.push({ option: "OWNED BY", value: this.span(this.idx(-1), []) });
1217
+ else
1218
+ node.options.push({ option: "OWNED BY", value: this.qualifiedName("a column").span });
1219
+ }
1220
+ else if (!this.unknownClause(STOP))
1221
+ return node;
1222
+ }
1223
+ }
1224
+ /** Whether `a b` continues one type name (`double precision`, `character varying`, `timestamp with time zone`) rather than being a name and a type. */
1225
+ continuesType(a, b) {
1226
+ if (a?.kind !== "ident" || b?.kind !== "ident")
1227
+ return false;
1228
+ const x = a.text.toUpperCase();
1229
+ const y = b.text.toUpperCase();
1230
+ if (x === "DOUBLE")
1231
+ return y === "PRECISION";
1232
+ if (["CHARACTER", "CHAR", "NCHAR", "BIT", "VARCHAR"].includes(x))
1233
+ return y === "VARYING";
1234
+ if (x === "NATIONAL")
1235
+ return y === "CHARACTER" || y === "CHAR";
1236
+ if (x === "TIME" || x === "TIMESTAMP")
1237
+ return y === "WITH" || y === "WITHOUT";
1238
+ if (x === "INTERVAL")
1239
+ return ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"].includes(y);
1240
+ return false;
1241
+ }
1242
+ /** `[IN | OUT | INOUT | VARIADIC] [name] type [{DEFAULT | =} expr]`. */
1243
+ routineArg() {
1244
+ const arg = { type: { from: 0, to: 0, refs: [] } };
1245
+ // A mode word is a mode unless it is the whole parameter (a type of that name) or the parameter's type follows nothing else.
1246
+ if (kw(this.peek(), "IN", "OUT", "INOUT", "VARIADIC") && !(this.isPunct(",", 1) || this.isPunct(")", 1))) {
1247
+ arg.mode = this.next().text.toUpperCase();
1248
+ }
1249
+ const a = this.peek();
1250
+ const b = this.peek(1);
1251
+ const named = a !== undefined &&
1252
+ (a.kind === "ident" || a.kind === "qident") &&
1253
+ b !== undefined &&
1254
+ (b.kind === "ident" || b.kind === "qident" || b.kind === "ref") &&
1255
+ !(b.kind === "ident" && keywordCategory(b.text) === "R") &&
1256
+ !kw(b, "DEFAULT") &&
1257
+ !this.continuesType(a, b);
1258
+ if (named) {
1259
+ const { text, span } = this.nameTok("a parameter name");
1260
+ arg.name = text;
1261
+ arg.nameSpan = span;
1262
+ }
1263
+ if (kw(this.peek(), "SETOF"))
1264
+ this.fail("expected a parameter type (SETOF is a return type)");
1265
+ arg.type = this.typeName();
1266
+ if (this.isOp("%"))
1267
+ this.fail("expected the end of the parameter (write the column's type: %TYPE is resolved once, when the function is created)");
1268
+ if (this.accept("DEFAULT") || (this.isOp("=") && (this.p++, true)))
1269
+ arg.default = this.checkedExpr([], true);
1270
+ return arg;
1271
+ }
1272
+ routine(kind, orReplace) {
1273
+ const node = { statement: kind, orReplace, name: this.qualifiedName(`a ${kind} name`), args: [], options: [], set: [], body: [] };
1274
+ this.expectPunct("(");
1275
+ if (!this.acceptPunct(")")) {
1276
+ for (;;) {
1277
+ node.args.push(this.routineArg());
1278
+ if (this.acceptPunct(","))
1279
+ continue;
1280
+ this.expectPunct(")");
1281
+ break;
1282
+ }
1283
+ }
1284
+ if (kind === "function" && this.accept("RETURNS")) {
1285
+ if (this.accept("TABLE")) {
1286
+ this.expectPunct("(");
1287
+ node.returnsTable = [];
1288
+ for (;;) {
1289
+ const { text, span } = this.nameTok("a column name");
1290
+ node.returnsTable.push({ name: text, nameSpan: span, type: this.typeName() });
1291
+ if (this.acceptPunct(","))
1292
+ continue;
1293
+ this.expectPunct(")");
1294
+ break;
1295
+ }
1296
+ }
1297
+ else {
1298
+ const setof = this.accept("SETOF");
1299
+ node.returns = { setof, type: this.typeName() };
1300
+ }
1301
+ }
1302
+ const word = (what) => {
1303
+ const from = this.idx();
1304
+ const t = this.next();
1305
+ if (!["ident", "qident", "string", "ref", "number"].includes(t.kind))
1306
+ this.fail(`expected ${what}`, t);
1307
+ return this.span(from, t.kind === "ref" ? [t.part] : []);
1308
+ };
1309
+ const option = (option, value, flag) => node.options.push({ option, ...(value ? { value } : {}), ...(flag !== undefined ? { flag } : {}) });
1310
+ const forFunction = (what) => {
1311
+ if (kind === "procedure")
1312
+ this.fail(`expected LANGUAGE, SECURITY, SET or AS (${what} is not an attribute of a procedure)`, this.lastSeen());
1313
+ };
1314
+ for (;;) {
1315
+ if (this.accept("LANGUAGE"))
1316
+ node.language = word("a language name");
1317
+ else if (this.accept("TRANSFORM")) {
1318
+ const from = this.idx();
1319
+ const refs = [];
1320
+ do {
1321
+ this.expect("FOR");
1322
+ this.expect("TYPE");
1323
+ refs.push(...this.typeName().refs);
1324
+ } while (this.acceptPunct(","));
1325
+ option("TRANSFORM", this.span(from, refs));
1326
+ }
1327
+ else if (kw(this.peek(), "IMMUTABLE", "STABLE", "VOLATILE")) {
1328
+ forFunction(this.peek().text.toUpperCase());
1329
+ option("VOLATILITY", word("a volatility"));
1330
+ }
1331
+ else if (this.acceptSeq("NOT", "LEAKPROOF")) {
1332
+ forFunction("LEAKPROOF");
1333
+ option("LEAKPROOF", undefined, false);
1334
+ }
1335
+ else if (this.accept("LEAKPROOF")) {
1336
+ forFunction("LEAKPROOF");
1337
+ option("LEAKPROOF", undefined, true);
1338
+ }
1339
+ else if (this.acceptSeq("CALLED", "ON", "NULL", "INPUT")) {
1340
+ forFunction("CALLED ON NULL INPUT");
1341
+ option("STRICT", undefined, false);
1342
+ }
1343
+ else if (this.acceptSeq("RETURNS", "NULL", "ON", "NULL", "INPUT") || this.accept("STRICT")) {
1344
+ forFunction("STRICT");
1345
+ option("STRICT", undefined, true);
1346
+ }
1347
+ else if (this.acceptSeq("EXTERNAL", "SECURITY") || this.accept("SECURITY")) {
1348
+ if (this.accept("DEFINER"))
1349
+ option("SECURITY", undefined, true);
1350
+ else {
1351
+ this.expect("INVOKER");
1352
+ option("SECURITY", undefined, false);
1353
+ }
1354
+ }
1355
+ else if (this.accept("PARALLEL")) {
1356
+ forFunction("PARALLEL");
1357
+ if (!kw(this.peek(), "UNSAFE", "RESTRICTED", "SAFE"))
1358
+ this.fail("expected UNSAFE, RESTRICTED or SAFE");
1359
+ option("PARALLEL", word("UNSAFE, RESTRICTED or SAFE"));
1360
+ }
1361
+ else if (kw(this.peek(), "COST", "ROWS")) {
1362
+ const o = this.next().text.toUpperCase();
1363
+ forFunction(o);
1364
+ const from = this.idx();
1365
+ const t = this.next();
1366
+ if (t.kind !== "number" && t.kind !== "ref")
1367
+ this.fail("expected a number", t);
1368
+ option(o, this.span(from, t.kind === "ref" ? [t.part] : []));
1369
+ }
1370
+ else if (this.accept("SUPPORT")) {
1371
+ forFunction("SUPPORT");
1372
+ option("SUPPORT", this.qualifiedName("a support function").span);
1373
+ }
1374
+ else if (this.accept("WINDOW")) {
1375
+ forFunction("WINDOW");
1376
+ option("WINDOW", undefined, true);
1377
+ }
1378
+ else if (this.accept("SET")) {
1379
+ const param = this.qualifiedName("a configuration parameter");
1380
+ if (this.acceptSeq("FROM", "CURRENT")) {
1381
+ this.fail("expected TO or = (SET ... FROM CURRENT takes the value of the session that runs the CREATE; write the value)", this.lastSeen());
1382
+ }
1383
+ if (!this.accept("TO")) {
1384
+ if (!this.isOp("="))
1385
+ this.fail("expected TO or =");
1386
+ this.p++;
1387
+ }
1388
+ const from = this.idx();
1389
+ const refs = [];
1390
+ for (;;) {
1391
+ if (this.isOp("-") || this.isOp("+"))
1392
+ this.p++;
1393
+ const t = this.next();
1394
+ if (t.kind === "ref")
1395
+ refs.push(t.part);
1396
+ else if (!["ident", "qident", "string", "number"].includes(t.kind))
1397
+ this.fail("expected a value", t);
1398
+ if (this.acceptPunct(","))
1399
+ continue;
1400
+ break;
1401
+ }
1402
+ node.set.push({ name: param.pieces.join("."), value: this.span(from, refs) });
1403
+ }
1404
+ else if (this.accept("AS")) {
1405
+ if (node.body.length > 0)
1406
+ this.fail("expected one AS clause", this.lastSeen());
1407
+ for (;;) {
1408
+ const from = this.idx();
1409
+ const t = this.next();
1410
+ if (t.kind !== "string")
1411
+ this.fail("expected the definition as a string constant (AS $$ ... $$)", t);
1412
+ node.body.push(this.span(from, []));
1413
+ if (this.acceptPunct(","))
1414
+ continue;
1415
+ break;
1416
+ }
1417
+ }
1418
+ else if (kw(this.peek(), "RETURN") || this.is("BEGIN", "ATOMIC")) {
1419
+ this.fail("expected AS (a SQL-standard body is stored parsed and printed back rewritten, so it cannot be compared with what was written; write the body as a string: AS $$ ... $$)");
1420
+ }
1421
+ else
1422
+ break;
1423
+ }
1424
+ if (node.body.length === 0)
1425
+ this.fail("expected AS and the body, as a string constant (AS $$ ... $$)");
1426
+ return node;
1427
+ }
1428
+ /** The last significant token read. */
1429
+ lastSeen() {
1430
+ return this.tokens[this.idx(-1)];
1431
+ }
1432
+ trigger(orReplace, constraint) {
1433
+ const { text, span } = this.nameTok("a trigger name");
1434
+ if (this.isPunct("."))
1435
+ this.fail("expected BEFORE, AFTER or INSTEAD OF (a trigger name is not schema-qualified; the trigger is its table's)");
1436
+ const name = { pieces: [text], span };
1437
+ let timing;
1438
+ if (this.accept("BEFORE"))
1439
+ timing = "BEFORE";
1440
+ else if (this.accept("AFTER"))
1441
+ timing = "AFTER";
1442
+ else if (this.acceptSeq("INSTEAD", "OF"))
1443
+ timing = "INSTEAD OF";
1444
+ else
1445
+ return this.fail("expected BEFORE, AFTER or INSTEAD OF");
1446
+ const events = [];
1447
+ do {
1448
+ if (this.accept("INSERT", "DELETE", "TRUNCATE"))
1449
+ events.push({ event: this.lastSeen().text.toUpperCase(), columns: [] });
1450
+ else if (this.accept("UPDATE")) {
1451
+ const columns = [];
1452
+ if (this.accept("OF")) {
1453
+ do
1454
+ columns.push((({ text, span }) => ({ name: text, span }))(this.nameTok("a column name")));
1455
+ while (this.acceptPunct(","));
1456
+ }
1457
+ events.push({ event: "UPDATE", columns });
1458
+ }
1459
+ else
1460
+ this.fail("expected INSERT, UPDATE, DELETE or TRUNCATE");
1461
+ } while (this.accept("OR"));
1462
+ this.expect("ON");
1463
+ const node = { statement: "trigger", orReplace, constraint, name, timing, events, table: this.qualifiedName("a table name"), referencing: [], function: { pieces: [], span: { from: 0, to: 0, refs: [] } }, args: [] };
1464
+ for (;;) {
1465
+ if (this.accept("FROM"))
1466
+ node.from = this.qualifiedName("a referenced table");
1467
+ else if (this.acceptSeq("NOT", "DEFERRABLE"))
1468
+ node.deferrable = false;
1469
+ else if (this.accept("DEFERRABLE"))
1470
+ node.deferrable = true;
1471
+ else if (this.acceptSeq("INITIALLY", "DEFERRED"))
1472
+ node.initiallyDeferred = true;
1473
+ else if (this.acceptSeq("INITIALLY", "IMMEDIATE"))
1474
+ node.initiallyDeferred = false;
1475
+ else if (this.accept("REFERENCING")) {
1476
+ do {
1477
+ if (!this.accept("OLD", "NEW"))
1478
+ this.fail("expected OLD or NEW");
1479
+ const which = this.lastSeen().text.toUpperCase();
1480
+ this.expect("TABLE");
1481
+ this.accept("AS");
1482
+ node.referencing.push({ which, name: this.nameTok("a transition table name").text });
1483
+ } while (kw(this.peek(), "OLD", "NEW"));
1484
+ }
1485
+ else if (this.accept("FOR")) {
1486
+ this.accept("EACH");
1487
+ if (!this.accept("ROW", "STATEMENT"))
1488
+ this.fail("expected ROW or STATEMENT");
1489
+ node.forEach = this.lastSeen().text.toUpperCase();
1490
+ }
1491
+ else if (this.accept("WHEN")) {
1492
+ if (!this.isPunct("("))
1493
+ this.fail("expected '(' (a WHEN condition is parenthesized)");
1494
+ node.when = this.parenthesized();
1495
+ }
1496
+ else
1497
+ break;
1498
+ }
1499
+ this.expect("EXECUTE");
1500
+ this.expect("FUNCTION", "PROCEDURE");
1501
+ node.function = this.qualifiedName("a trigger function");
1502
+ this.expectPunct("(");
1503
+ if (!this.acceptPunct(")")) {
1504
+ for (;;) {
1505
+ const from = this.idx();
1506
+ if (this.isOp("-"))
1507
+ this.p++;
1508
+ const t = this.next();
1509
+ if (!["string", "number", "ident", "qident", "ref"].includes(t.kind))
1510
+ this.fail("expected a trigger argument (a string, a number or a name)", t);
1511
+ node.args.push(this.span(from, t.kind === "ref" ? [t.part] : []));
1512
+ if (this.acceptPunct(","))
1513
+ continue;
1514
+ this.expectPunct(")");
1515
+ break;
1516
+ }
1517
+ }
1518
+ return node;
1519
+ }
1520
+ /** A role name where one is granted to or named: an identifier, an interpolation, or PUBLIC (`public`). */
1521
+ roleSpec(what = "a role name") {
1522
+ const t = this.peek();
1523
+ if (kw(t, "CURRENT_USER", "CURRENT_ROLE", "SESSION_USER"))
1524
+ this.fail(`expected ${what} (name the role: ${t.text.toUpperCase()} is whichever role runs the statement)`);
1525
+ if (kw(t, "PUBLIC")) {
1526
+ const i = this.idx();
1527
+ this.p++;
1528
+ return { name: "public", span: { from: i, to: i + 1, refs: [] } };
1529
+ }
1530
+ const n = this.next();
1531
+ if (!isNameToken(n))
1532
+ this.fail(`expected ${what}`, n);
1533
+ const i = this.idx(-1);
1534
+ return { name: n.kind === "ref" ? "" : identValue(n), span: { from: i, to: i + 1, refs: n.kind === "ref" ? [n.part] : [] } };
1535
+ }
1536
+ roleList(what) {
1537
+ const out = [this.roleSpec(what)];
1538
+ while (this.acceptPunct(","))
1539
+ out.push(this.roleSpec(what));
1540
+ return out;
1541
+ }
1542
+ policy() {
1543
+ const { text, span } = this.nameTok("a policy name");
1544
+ this.expect("ON");
1545
+ const node = { statement: "policy", name: { pieces: [text], span }, table: this.qualifiedName("a table name"), roles: [] };
1546
+ if (this.accept("AS")) {
1547
+ if (this.accept("PERMISSIVE"))
1548
+ node.permissive = true;
1549
+ else {
1550
+ this.expect("RESTRICTIVE");
1551
+ node.permissive = false;
1552
+ }
1553
+ }
1554
+ if (this.accept("FOR")) {
1555
+ if (!this.accept("ALL", "SELECT", "INSERT", "UPDATE", "DELETE"))
1556
+ this.fail("expected ALL, SELECT, INSERT, UPDATE or DELETE");
1557
+ node.command = this.lastSeen().text.toUpperCase();
1558
+ }
1559
+ if (this.accept("TO"))
1560
+ node.roles = this.roleList();
1561
+ if (this.accept("USING")) {
1562
+ if (!this.isPunct("("))
1563
+ this.fail("expected '('");
1564
+ node.using = this.parenthesized();
1565
+ }
1566
+ if (this.acceptSeq("WITH", "CHECK")) {
1567
+ if (!this.isPunct("("))
1568
+ this.fail("expected '('");
1569
+ node.check = this.parenthesized();
1570
+ }
1571
+ return node;
1572
+ }
1573
+ role() {
1574
+ const { text, span } = this.nameTok("a role name");
1575
+ const node = { statement: "role", name: { pieces: [text], span }, options: [] };
1576
+ this.accept("WITH");
1577
+ const FLAGS = ["SUPERUSER", "NOSUPERUSER", "CREATEDB", "NOCREATEDB", "CREATEROLE", "NOCREATEROLE", "INHERIT", "NOINHERIT", "LOGIN", "NOLOGIN", "REPLICATION", "NOREPLICATION", "BYPASSRLS", "NOBYPASSRLS"];
1578
+ for (;;) {
1579
+ if (this.atEnd())
1580
+ return node;
1581
+ if (kw(this.peek(), ...FLAGS))
1582
+ node.options.push({ option: this.next().text.toUpperCase() });
1583
+ else if (this.acceptSeq("CONNECTION", "LIMIT")) {
1584
+ const from = this.idx();
1585
+ if (this.isOp("-"))
1586
+ this.p++;
1587
+ const t = this.next();
1588
+ if (t.kind !== "number" && t.kind !== "ref")
1589
+ this.fail("expected a number", t);
1590
+ node.options.push({ option: "CONNECTION LIMIT", value: this.span(from, t.kind === "ref" ? [t.part] : []) });
1591
+ }
1592
+ else if (kw(this.peek(), "PASSWORD", "ENCRYPTED", "UNENCRYPTED") || this.is("VALID", "UNTIL")) {
1593
+ this.fail("expected a role option (a password is the environment's secret, never a declaration's: set it where the environment's credentials are kept)");
1594
+ }
1595
+ else if (kw(this.peek(), "IN", "ROLE", "ADMIN", "USER", "SYSID")) {
1596
+ this.fail("expected a role option (who is a member of which role is the environment's: grant memberships where the roles are provisioned)");
1597
+ }
1598
+ else
1599
+ return this.fail("expected a role option");
1600
+ }
1601
+ }
1602
+ /** `GRANT|REVOKE privileges ON target TO|FROM roles`, after GRANT or REVOKE; for default privileges, `on` is a plural kind and there are no objects. */
1603
+ grant(revoke, defaults) {
1604
+ const grantOptionFor = revoke && this.acceptSeq("GRANT", "OPTION", "FOR");
1605
+ const privileges = [];
1606
+ if (this.accept("ALL")) {
1607
+ this.accept("PRIVILEGES");
1608
+ const columns = this.isPunct("(") ? this.nameList() : [];
1609
+ privileges.push({ privilege: "ALL", columns });
1610
+ }
1611
+ else {
1612
+ const KNOWN = ["SELECT", "INSERT", "UPDATE", "DELETE", "TRUNCATE", "REFERENCES", "TRIGGER", "MAINTAIN", "USAGE", "CREATE", "EXECUTE"];
1613
+ do {
1614
+ const t = this.next();
1615
+ if (!kw(t, ...KNOWN)) {
1616
+ if (t.kind === "ident" || t.kind === "qident" || t.kind === "ref") {
1617
+ this.fail("expected a privilege (a role granted to a role is membership, which is the environment's: grant it where the roles are provisioned)", t);
1618
+ }
1619
+ this.fail("expected a privilege", t);
1620
+ }
1621
+ privileges.push({ privilege: t.text.toUpperCase(), columns: this.isPunct("(") ? this.nameList() : [] });
1622
+ } while (this.acceptPunct(","));
1623
+ }
1624
+ this.expect("ON");
1625
+ const node = { statement: "grant", revoke, grantOptionFor, privileges, on: "TABLE", objects: [], grantees: [], withGrantOption: false, defaults };
1626
+ if (defaults) {
1627
+ if (!this.accept("TABLES", "SEQUENCES", "FUNCTIONS", "ROUTINES", "TYPES", "SCHEMAS"))
1628
+ this.fail("expected TABLES, SEQUENCES, FUNCTIONS, ROUTINES, TYPES or SCHEMAS");
1629
+ node.on = this.lastSeen().text.toUpperCase();
1630
+ }
1631
+ else {
1632
+ if (this.accept("ALL")) {
1633
+ this.fail("expected an object (ALL ... IN SCHEMA grants on what exists when it runs; grant on each declared object, and use ALTER DEFAULT PRIVILEGES for what is created later)", this.lastSeen());
1634
+ }
1635
+ if (kw(this.peek(), "DATABASE", "DOMAIN", "TYPE", "LANGUAGE", "LARGE", "TABLESPACE", "FOREIGN", "PARAMETER")) {
1636
+ this.fail("expected TABLE, SEQUENCE, SCHEMA, FUNCTION, PROCEDURE or ROUTINE (privileges on this kind of object are not declared)");
1637
+ }
1638
+ if (this.accept("TABLE", "SEQUENCE", "SCHEMA", "FUNCTION", "PROCEDURE", "ROUTINE"))
1639
+ node.on = this.lastSeen().text.toUpperCase();
1640
+ const routine = node.on === "FUNCTION" || node.on === "PROCEDURE" || node.on === "ROUTINE";
1641
+ do {
1642
+ const name = this.qualifiedName(node.on === "SCHEMA" ? "a schema name" : "an object name");
1643
+ node.objects.push(routine && this.isPunct("(") ? { name, args: this.parenthesized() } : { name });
1644
+ } while (this.acceptPunct(","));
1645
+ }
1646
+ if (revoke)
1647
+ this.expect("FROM");
1648
+ else
1649
+ this.expect("TO");
1650
+ this.accept("GROUP");
1651
+ node.grantees = this.roleList();
1652
+ if (!revoke && this.acceptSeq("WITH", "GRANT", "OPTION"))
1653
+ node.withGrantOption = true;
1654
+ if (this.acceptSeq("GRANTED", "BY"))
1655
+ this.fail("expected the end of the statement (GRANTED BY names another grantor; chant grants as the role it runs as)", this.lastSeen());
1656
+ if (revoke && this.accept("CASCADE", "RESTRICT")) {
1657
+ if (this.lastSeen().text.toUpperCase() === "CASCADE")
1658
+ this.fail("expected the end of the statement (CASCADE revokes what others granted from it; revoke each grant)", this.lastSeen());
1659
+ }
1660
+ return node;
1661
+ }
1662
+ defaultPrivileges() {
1663
+ let forRoles;
1664
+ let inSchemas;
1665
+ for (;;) {
1666
+ if (this.accept("FOR")) {
1667
+ this.expect("ROLE", "USER");
1668
+ forRoles = this.roleList();
1669
+ }
1670
+ else if (this.acceptSeq("IN", "SCHEMA")) {
1671
+ inSchemas = [];
1672
+ do
1673
+ inSchemas.push((({ text, span }) => ({ name: text, span }))(this.nameTok("a schema name")));
1674
+ while (this.acceptPunct(","));
1675
+ }
1676
+ else
1677
+ break;
1678
+ }
1679
+ let node;
1680
+ if (this.accept("GRANT"))
1681
+ node = this.grant(false, true);
1682
+ else {
1683
+ this.expect("REVOKE");
1684
+ node = this.grant(true, true);
1685
+ }
1686
+ return { ...node, ...(forRoles ? { forRoles } : {}), ...(inSchemas ? { inSchemas } : {}) };
1687
+ }
1688
+ rowSecurity() {
1689
+ this.accept("ONLY");
1690
+ const table = this.qualifiedName("a table name");
1691
+ let action;
1692
+ if (this.accept("ENABLE"))
1693
+ action = "ENABLE";
1694
+ else if (this.accept("DISABLE"))
1695
+ action = "DISABLE";
1696
+ else if (this.accept("FORCE"))
1697
+ action = "FORCE";
1698
+ else if (this.acceptSeq("NO", "FORCE"))
1699
+ action = "NO FORCE";
1700
+ else
1701
+ return this.fail("expected ENABLE, DISABLE, FORCE or NO FORCE ROW LEVEL SECURITY (a table's other changes are its CREATE TABLE's)");
1702
+ this.expect("ROW");
1703
+ this.expect("LEVEL");
1704
+ this.expect("SECURITY");
1705
+ return { statement: "rowSecurity", table, action };
1706
+ }
1707
+ comment() {
1708
+ this.expect("ON");
1709
+ let objectType;
1710
+ let on;
1711
+ let target;
1712
+ let args;
1713
+ if (this.accept("CONSTRAINT")) {
1714
+ objectType = "CONSTRAINT";
1715
+ target = this.qualifiedName("a constraint name");
1716
+ this.expect("ON");
1717
+ if (this.accept("DOMAIN"))
1718
+ objectType = "DOMAIN CONSTRAINT";
1719
+ on = this.qualifiedName("a table name");
1720
+ }
1721
+ else if (this.accept("TRIGGER", "POLICY")) {
1722
+ objectType = this.lastSeen().text.toUpperCase();
1723
+ target = this.qualifiedName(`a ${objectType.toLowerCase()} name`);
1724
+ this.expect("ON");
1725
+ on = this.qualifiedName("a table name");
1726
+ }
1727
+ else if (this.accept("ROLE")) {
1728
+ objectType = "ROLE";
1729
+ target = this.qualifiedName("a role name");
1730
+ }
1731
+ else if (this.acceptSeq("EVENT", "TRIGGER")) {
1732
+ objectType = "EVENT TRIGGER";
1733
+ target = this.qualifiedName("an event trigger name");
1734
+ }
1735
+ else if (this.accept("CAST")) {
1736
+ objectType = "CAST";
1737
+ const from = this.idx();
1738
+ this.expectPunct("(");
1739
+ const source = this.typeName();
1740
+ this.expect("AS");
1741
+ const castTarget = this.typeName();
1742
+ this.expectPunct(")");
1743
+ target = { pieces: [], span: this.span(from, [...source.refs, ...castTarget.refs]) };
1744
+ this.expect("IS");
1745
+ const node = { statement: "comment", objectType, target, cast: { source, target: castTarget } };
1746
+ if (!this.accept("NULL"))
1747
+ node.text = this.commentText();
1748
+ return node;
1749
+ }
1750
+ else if (this.accept("AGGREGATE", "COLLATION")) {
1751
+ objectType = this.lastSeen().text.toUpperCase();
1752
+ target = this.qualifiedName(`an ${objectType.toLowerCase()} name`);
1753
+ if (objectType === "AGGREGATE") {
1754
+ if (!this.isPunct("("))
1755
+ this.fail("expected '(' and the aggregate's argument types");
1756
+ args = this.parenthesized();
1757
+ }
1758
+ }
1759
+ else {
1760
+ if (this.acceptSeq("MATERIALIZED", "VIEW"))
1761
+ objectType = "MATERIALIZED VIEW";
1762
+ else if (this.accept("TABLE", "COLUMN", "INDEX", "VIEW", "SEQUENCE", "TYPE", "DOMAIN", "SCHEMA", "EXTENSION", "FUNCTION", "PROCEDURE")) {
1763
+ objectType = this.tokens[this.idx(-1)].text.toUpperCase();
1764
+ }
1765
+ else
1766
+ return this.fail("expected TABLE, COLUMN, INDEX, VIEW, MATERIALIZED VIEW, SEQUENCE, TYPE, DOMAIN, SCHEMA, EXTENSION, FUNCTION, PROCEDURE, AGGREGATE, COLLATION, CAST, TRIGGER, EVENT TRIGGER, POLICY, ROLE or CONSTRAINT");
1767
+ target = this.qualifiedName("an object name");
1768
+ if ((objectType === "FUNCTION" || objectType === "PROCEDURE") && this.isPunct("("))
1769
+ args = this.parenthesized();
1770
+ }
1771
+ this.expect("IS");
1772
+ const node = { statement: "comment", objectType, target, on, ...(args ? { args } : {}) };
1773
+ if (!this.accept("NULL"))
1774
+ node.text = this.commentText();
1775
+ return node;
1776
+ }
1777
+ /** A comment's text: a string, or an interpolation. */
1778
+ commentText() {
1779
+ const t = this.peek();
1780
+ if (t?.kind !== "string" && t?.kind !== "ref")
1781
+ this.fail("expected a string or NULL");
1782
+ const from = this.idx();
1783
+ this.p++;
1784
+ return this.span(from, t.kind === "ref" ? [t.part] : []);
1785
+ }
1786
+ /** Statements separated by `;`, to the end. */
1787
+ parseMany() {
1788
+ const out = [];
1789
+ while (this.peek() !== undefined) {
1790
+ if (this.acceptPunct(";"))
1791
+ continue;
1792
+ const before = this.unknownClauses.length;
1793
+ const node = this.parse();
1794
+ if (this.unknownClauses.length > before)
1795
+ node.unknown = this.unknownClauses.slice(before);
1796
+ out.push(node);
1797
+ if (this.peek() !== undefined && !this.isPunct(";"))
1798
+ this.fail(`unexpected token after the ${describe(out[out.length - 1])}`);
1799
+ }
1800
+ if (out.length === 0)
1801
+ this.fail("expected a statement");
1802
+ return out;
1803
+ }
1804
+ }
1805
+ const describe = (n) => n.statement === "comment"
1806
+ ? "COMMENT ON"
1807
+ : n.statement === "enum"
1808
+ ? "type definition"
1809
+ : n.statement === "grant"
1810
+ ? n.defaults
1811
+ ? "ALTER DEFAULT PRIVILEGES"
1812
+ : n.revoke
1813
+ ? "REVOKE"
1814
+ : "GRANT"
1815
+ : n.statement === "rowSecurity"
1816
+ ? "ALTER TABLE"
1817
+ : `${n.statement} definition`;
1818
+ /**
1819
+ * Parse a template: one CREATE, optionally followed by COMMENT ON statements,
1820
+ * separated by `;`. Throws {@link SqlSyntaxError} located in the template.
1821
+ */
1822
+ export function parseStatements(tokens) {
1823
+ return new PgParser(tokens).parseMany();
1824
+ }