neon 3.0.0 → 3.1.0

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 (198) hide show
  1. package/README.md +54 -0
  2. package/dist/_shared/auth_selection.js +76 -79
  3. package/dist/_shared/credentials.js +166 -187
  4. package/dist/_shared/env-core/env.js +354 -517
  5. package/dist/_shared/env-core/reuse-secrets.js +159 -203
  6. package/dist/_shared/paths.js +129 -126
  7. package/dist/_shared/profiles.js +192 -242
  8. package/dist/_shared/secure_file.js +36 -38
  9. package/dist/_virtual/_rolldown/runtime.js +13 -0
  10. package/dist/analytics.js +163 -207
  11. package/dist/api.js +815 -758
  12. package/dist/auth.js +121 -141
  13. package/dist/auth_context.js +39 -53
  14. package/dist/cli.js +4 -7
  15. package/dist/commands/api.js +220 -250
  16. package/dist/commands/api_keys.js +251 -314
  17. package/dist/commands/auth.js +283 -328
  18. package/dist/commands/bootstrap.js +372 -437
  19. package/dist/commands/branches.js +304 -455
  20. package/dist/commands/bucket.js +374 -514
  21. package/dist/commands/checkout.js +213 -298
  22. package/dist/commands/config.js +573 -690
  23. package/dist/commands/connection_string.js +137 -165
  24. package/dist/commands/data_api.js +238 -260
  25. package/dist/commands/databases.js +67 -76
  26. package/dist/commands/deploy.js +31 -25
  27. package/dist/commands/dev.js +639 -719
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -355
  31. package/dist/commands/index.js +70 -65
  32. package/dist/commands/init.js +84 -119
  33. package/dist/commands/inspect.js +55 -55
  34. package/dist/commands/ip_allow.js +88 -120
  35. package/dist/commands/link.js +874 -1019
  36. package/dist/commands/logs.js +291 -0
  37. package/dist/commands/neon_auth.js +725 -933
  38. package/dist/commands/operations.js +34 -25
  39. package/dist/commands/orgs.js +28 -18
  40. package/dist/commands/profile.js +614 -845
  41. package/dist/commands/projects.js +313 -373
  42. package/dist/commands/psql.js +60 -58
  43. package/dist/commands/roles.js +55 -58
  44. package/dist/commands/schema_diff.js +87 -131
  45. package/dist/commands/set_context.js +34 -26
  46. package/dist/commands/snapshots.js +288 -413
  47. package/dist/commands/status.js +41 -37
  48. package/dist/commands/user.js +21 -10
  49. package/dist/commands/vpc_endpoints.js +85 -113
  50. package/dist/config.js +7 -6
  51. package/dist/config_format.js +50 -66
  52. package/dist/config_template.js +128 -157
  53. package/dist/context.js +183 -235
  54. package/dist/current_branch_fast_path.js +40 -49
  55. package/dist/dev/env.js +313 -394
  56. package/dist/dev/functions.js +54 -68
  57. package/dist/dev/inputs.js +46 -58
  58. package/dist/dev/runtime.js +135 -164
  59. package/dist/dev/websocket.js +766 -959
  60. package/dist/env.js +27 -33
  61. package/dist/env_file.js +118 -132
  62. package/dist/env_services.js +36 -38
  63. package/dist/errors.js +57 -68
  64. package/dist/functions_api.js +45 -43
  65. package/dist/help.js +189 -140
  66. package/dist/index.js +182 -257
  67. package/dist/init/agents.js +137 -118
  68. package/dist/init/auth.js +58 -68
  69. package/dist/init/bootstrap.js +325 -396
  70. package/dist/init/build_config.js +4 -2
  71. package/dist/init/detect_agent.js +56 -101
  72. package/dist/init/editors.js +35 -52
  73. package/dist/init/enrich_output.js +51 -66
  74. package/dist/init/extension.js +134 -171
  75. package/dist/init/inspect.js +179 -266
  76. package/dist/init/interactive.js +510 -622
  77. package/dist/init/neonctl.js +117 -168
  78. package/dist/init/orchestrate.js +157 -173
  79. package/dist/init/phases/auth.js +188 -202
  80. package/dist/init/phases/cleanup.js +23 -23
  81. package/dist/init/phases/db.js +251 -277
  82. package/dist/init/phases/getting_started.js +213 -223
  83. package/dist/init/phases/mcp.js +174 -224
  84. package/dist/init/phases/migrations.js +247 -248
  85. package/dist/init/phases/neon_auth.js +114 -133
  86. package/dist/init/phases/setup.js +546 -703
  87. package/dist/init/phases/skills.js +75 -86
  88. package/dist/init/phases/status.js +72 -67
  89. package/dist/init/resolve_context.js +102 -99
  90. package/dist/init/route_command.js +91 -98
  91. package/dist/init/skills.js +174 -218
  92. package/dist/init/vsix.js +77 -99
  93. package/dist/log.js +17 -16
  94. package/dist/neon_services.js +104 -129
  95. package/dist/parameters.gen.js +481 -471
  96. package/dist/pkg.js +17 -19
  97. package/dist/profile_keys.js +44 -47
  98. package/dist/psql/cli.js +44 -47
  99. package/dist/psql/command/cmd_cond.js +231 -406
  100. package/dist/psql/command/cmd_connect.js +557 -764
  101. package/dist/psql/command/cmd_copy.js +727 -983
  102. package/dist/psql/command/cmd_describe.js +1499 -1688
  103. package/dist/psql/command/cmd_format.js +733 -905
  104. package/dist/psql/command/cmd_io.js +1293 -2082
  105. package/dist/psql/command/cmd_lo.js +297 -359
  106. package/dist/psql/command/cmd_meta.js +727 -878
  107. package/dist/psql/command/cmd_misc.js +138 -172
  108. package/dist/psql/command/cmd_pipeline.js +547 -1099
  109. package/dist/psql/command/cmd_restrict.js +119 -155
  110. package/dist/psql/command/cmd_show.js +529 -688
  111. package/dist/psql/command/dispatch.js +261 -325
  112. package/dist/psql/command/inputQueue.js +35 -33
  113. package/dist/psql/command/shared.js +49 -63
  114. package/dist/psql/complete/filenames.js +90 -133
  115. package/dist/psql/complete/index.js +59 -97
  116. package/dist/psql/complete/matcher.js +236 -300
  117. package/dist/psql/complete/psqlVars.js +218 -223
  118. package/dist/psql/complete/queries.js +159 -177
  119. package/dist/psql/complete/rules.js +1493 -2299
  120. package/dist/psql/core/common.js +762 -1180
  121. package/dist/psql/core/help.js +456 -546
  122. package/dist/psql/core/mainloop.js +692 -1302
  123. package/dist/psql/core/prompt.js +391 -408
  124. package/dist/psql/core/settings.js +429 -644
  125. package/dist/psql/core/sqlHelp.js +480 -554
  126. package/dist/psql/core/startup.js +626 -815
  127. package/dist/psql/core/syncVars.js +67 -110
  128. package/dist/psql/core/variables.js +156 -278
  129. package/dist/psql/describe/formatters.js +884 -1285
  130. package/dist/psql/describe/processNamePattern.js +173 -260
  131. package/dist/psql/describe/queries.js +1368 -2403
  132. package/dist/psql/describe/versionGate.js +32 -41
  133. package/dist/psql/index.js +1414 -1927
  134. package/dist/psql/io/history.js +232 -271
  135. package/dist/psql/io/input.js +103 -108
  136. package/dist/psql/io/lineEditor/buffer.js +238 -319
  137. package/dist/psql/io/lineEditor/complete.js +135 -213
  138. package/dist/psql/io/lineEditor/filename.js +139 -148
  139. package/dist/psql/io/lineEditor/index.js +653 -870
  140. package/dist/psql/io/lineEditor/keymap.js +544 -702
  141. package/dist/psql/io/lineEditor/vt100.js +294 -341
  142. package/dist/psql/io/pgpass.js +158 -187
  143. package/dist/psql/io/pgservice.js +146 -183
  144. package/dist/psql/io/psqlrc.js +328 -403
  145. package/dist/psql/print/aligned.js +1020 -1683
  146. package/dist/psql/print/asciidoc.js +180 -214
  147. package/dist/psql/print/crosstab.js +281 -442
  148. package/dist/psql/print/csv.js +48 -70
  149. package/dist/psql/print/html.js +195 -226
  150. package/dist/psql/print/json.js +75 -88
  151. package/dist/psql/print/latex.js +291 -364
  152. package/dist/psql/print/pager.js +171 -242
  153. package/dist/psql/print/troff.js +194 -226
  154. package/dist/psql/print/unaligned.js +69 -95
  155. package/dist/psql/print/units.js +167 -169
  156. package/dist/psql/scanner/slash.js +428 -483
  157. package/dist/psql/scanner/sql.js +445 -889
  158. package/dist/psql/scanner/stringutils.js +309 -379
  159. package/dist/psql/types/index.js +2 -7
  160. package/dist/psql/types/scanner.js +25 -22
  161. package/dist/psql/wire/connection.js +2042 -2803
  162. package/dist/psql/wire/copy.js +84 -100
  163. package/dist/psql/wire/notify.js +39 -59
  164. package/dist/psql/wire/pipeline.js +305 -518
  165. package/dist/psql/wire/protocol.js +349 -417
  166. package/dist/psql/wire/sasl.js +180 -265
  167. package/dist/psql/wire/tls.js +400 -561
  168. package/dist/storage_api.js +115 -129
  169. package/dist/test_utils/fixtures.js +94 -113
  170. package/dist/test_utils/oauth_server.js +10 -7
  171. package/dist/test_utils/project_dir.js +33 -0
  172. package/dist/utils/ai_gateway_notice.js +131 -162
  173. package/dist/utils/api_enums.js +21 -28
  174. package/dist/utils/auth.js +10 -4
  175. package/dist/utils/branch_notice.js +20 -19
  176. package/dist/utils/branch_picker.js +83 -89
  177. package/dist/utils/cli_name.js +15 -12
  178. package/dist/utils/compute_units.js +20 -27
  179. package/dist/utils/config_diff.js +127 -158
  180. package/dist/utils/enrichers.js +95 -148
  181. package/dist/utils/esbuild.js +130 -189
  182. package/dist/utils/flags.js +35 -47
  183. package/dist/utils/formats.js +8 -15
  184. package/dist/utils/git_diff.js +69 -80
  185. package/dist/utils/inspect_db.js +101 -143
  186. package/dist/utils/inspect_queries.js +179 -142
  187. package/dist/utils/middlewares.js +37 -44
  188. package/dist/utils/openapi.js +87 -99
  189. package/dist/utils/package_manager.js +312 -110
  190. package/dist/utils/point_in_time.js +49 -53
  191. package/dist/utils/psql.js +89 -106
  192. package/dist/utils/service_picker.js +55 -58
  193. package/dist/utils/string.js +5 -5
  194. package/dist/utils/ui.js +38 -55
  195. package/dist/utils/write_sync.js +26 -35
  196. package/dist/utils/zip.js +4 -3
  197. package/dist/writer.js +67 -87
  198. package/package.json +7 -5
@@ -1,2424 +1,1618 @@
1
- /**
2
- * Tab-completion rule body.
3
- *
4
- * Port (selective — see "Coverage" below) of psql's `psql_completion()`
5
- * from `src/bin/psql/tab-complete.in.c`. Given the tokenized "previous
6
- * words" and the in-progress current word, we walk a chain of
7
- * Matches/TailMatches/HeadMatches guards and return:
8
- *
9
- * - a STATIC list (for keyword / enum-value completion), filtered by the
10
- * current word's prefix, OR
11
- * - a CATALOG query (for table/view/role/schema/etc.) executed against
12
- * the live Connection.
13
- *
14
- * Coverage (the parts of upstream we ship):
15
- *
16
- * - Backslash command name completion: `\` → list of commands.
17
- * - Backslash arg completion for the high-traffic commands: `\c[onnect]`,
18
- * `\dt`/`\d`/`\dv`/`\dm`/`\di`/`\ds`, `\df`, `\dn`, `\du`/`\dg`,
19
- * `\dx`, `\dL`, `\dT`, `\do` (operators), `\dC` (casts), `\encoding`,
20
- * `\pset`, `\set`.
21
- * - Top-level SQL keyword set (SELECT/INSERT/UPDATE/DELETE/etc).
22
- * - Mid-statement object completion in the most common contexts:
23
- * FROM → tables/views/matviews/foreign tables.
24
- * INTO (INSERT) → tables.
25
- * JOIN → tables/views/matviews.
26
- * UPDATE → tables.
27
- * DELETE FROM → tables.
28
- * ALTER TABLE → tables, then sub-action (ADD/DROP/…),
29
- * then sub-action continuation (COLUMN/CONSTRAINT/…).
30
- * ALTER VIEW → views + sub-actions.
31
- * ALTER MATERIALIZED VIEW → mat-views + sub-actions.
32
- * ALTER INDEX → indexes + sub-actions.
33
- * ALTER SEQUENCE → sequences + sub-actions.
34
- * ALTER FUNCTION / PROCEDURE / ROUTINE → functions + sub-actions.
35
- * ALTER TYPE → types + sub-actions.
36
- * ALTER ROLE/USER → roles + sub-actions.
37
- * ALTER DATABASE → databases + sub-actions.
38
- * ALTER SCHEMA → schemas + sub-actions.
39
- * ALTER EXTENSION → extensions + sub-actions.
40
- * ALTER POLICY → ON <table>, rename/owner.
41
- * ALTER PUBLICATION / SUBSCRIPTION → sub-actions.
42
- * DROP TABLE → tables.
43
- * DROP VIEW → views.
44
- * DROP INDEX → indexes.
45
- * DROP MATERIALIZED VIEW → mat views.
46
- * DROP SEQUENCE → sequences.
47
- * DROP TYPE → types.
48
- * DROP SCHEMA → schemas.
49
- * DROP EXTENSION → extensions.
50
- * DROP ROLE / USER → roles.
51
- * DROP DATABASE → databases.
52
- * DROP FUNCTION → functions.
53
- * CREATE INDEX → CONCURRENTLY / IF NOT EXISTS / ON / USING (access methods).
54
- * GRANT / REVOKE … ON … → tables.
55
- * TRUNCATE [TABLE] → tables.
56
- * LOCK TABLE → tables.
57
- * COPY → tables.
58
- * ANALYZE / VACUUM → tables.
59
- * REINDEX → indexes/tables/databases (limited).
60
- * SET <guc> → list of GUC names from pg_settings.
61
- * SET ROLE → roles.
62
- * SET SCHEMA → schemas.
63
- * SHOW <guc> → list of GUC names from pg_settings.
64
- * RESET <guc> → list of GUC names from pg_settings.
65
- *
66
- * - Window-function clauses: `OVER (` → PARTITION BY / ORDER BY / RANGE /
67
- * ROWS / GROUPS.
68
- * - Generic post-FROM/JOIN tail keywords: JOIN, WHERE, GROUP BY, ORDER BY,
69
- * LIMIT, OFFSET, UNION, INTERSECT, EXCEPT, etc.
70
- * - WHERE-expression continuations: AND / OR / IS / IN / NOT / BETWEEN.
71
- * - `\set`/`\unset`: completion of psql variable names.
72
- * - Variable expansion `:NAME` completion (inside any line).
73
- *
74
- * Upstream coverage notes (psql `tab-complete.in.c`):
75
- * - We ported the ALTER-OBJECT arms around lines 2050-2700 (sub-actions
76
- * for TABLE/VIEW/MV/INDEX/SEQUENCE/FUNCTION/TYPE/ROLE/DB/SCHEMA/
77
- * EXTENSION/POLICY/PUBLICATION/SUBSCRIPTION) but elided the deep
78
- * option-value continuations (e.g. ALTER TABLE … ADD CONSTRAINT …
79
- * CHECK (…)) and partition-bound clauses.
80
- * - We ported the post-FROM / JOIN tail-keyword set from lines ~4500-4700.
81
- * - We ported the SET/SHOW/RESET GUC lookup from lines ~5500-5700, using
82
- * a live pg_settings query rather than a static list.
83
- * - We ported the CREATE INDEX block at ~3000-3100.
84
- * - Skipped: ALTER-system fine-grained options, COMMENT ON full grammar,
85
- * CREATE STATISTICS, CREATE EVENT TRIGGER bodies, FDW/USER MAPPING
86
- * argument grammar, and most GRANT/REVOKE class continuations beyond
87
- * the table object form.
88
- *
89
- * What's intentionally still stubbed:
90
- *
91
- * - psql `\h` SQL help index — exists in psql proper, would need the
92
- * help index data.
93
- * - Column-name completion after `SELECT … FROM t WHERE` (we don't carry
94
- * a parsed alias→relation map; upstream parses the FROM clause).
95
- *
96
- * The shape mirrors the C source closely enough that adding new rules is
97
- * mechanical: drop a new `if (TailMatches(...))` arm in the right region.
98
- */
1
+ import { HeadMatches, TailMatches, tokenize } from "./matcher.js";
99
2
  import { completeFilenames, isCopyFromOrTo } from "./filenames.js";
100
- import { HeadMatches, MatchAny, TailMatches, tokenize } from "./matcher.js";
101
- import { ENCODINGS, PSET_OPTIONS, psetValuesFor, SPECIAL_VARIABLES, variableValuesFor, } from "./psqlVars.js";
102
- import { Query_for_constraint_of_table, Query_for_constraint_of_table_in_schema, Query_for_list_of_casts, Query_for_list_of_databases, Query_for_list_of_datatypes, Query_for_list_of_enum_values_quoted, Query_for_list_of_extensions, Query_for_list_of_functions, Query_for_list_of_index_access_methods, Query_for_list_of_indexes, Query_for_list_of_languages, Query_for_list_of_matviews, Query_for_list_of_operators, Query_for_list_of_publications, Query_for_list_of_relations_in_schema, Query_for_list_of_roles, Query_for_list_of_schemas, Query_for_list_of_sequences, Query_for_list_of_set_vars, Query_for_list_of_subscriptions, Query_for_list_of_tables, Query_for_list_of_tables_for_constraint, Query_for_list_of_tables_for_constraint_in_schema, Query_for_list_of_tables_views, Query_for_list_of_tablespaces, Query_for_list_of_timezone_names_quoted_in, Query_for_list_of_timezone_names_quoted_out, Query_for_list_of_types, Query_for_list_of_views, Query_for_values_of_enum_GUC, runCatalogQuery, } from "./queries.js";
3
+ import { ENCODINGS, PSET_OPTIONS, SPECIAL_VARIABLES, psetValuesFor, variableValuesFor } from "./psqlVars.js";
4
+ import { Query_for_constraint_of_table, Query_for_constraint_of_table_in_schema, Query_for_list_of_casts, Query_for_list_of_databases, Query_for_list_of_datatypes, Query_for_list_of_enum_values_quoted, Query_for_list_of_extensions, Query_for_list_of_functions, Query_for_list_of_index_access_methods, Query_for_list_of_indexes, Query_for_list_of_languages, Query_for_list_of_matviews, Query_for_list_of_operators, Query_for_list_of_publications, Query_for_list_of_relations_in_schema, Query_for_list_of_roles, Query_for_list_of_schemas, Query_for_list_of_sequences, Query_for_list_of_set_vars, Query_for_list_of_subscriptions, Query_for_list_of_tables, Query_for_list_of_tables_for_constraint, Query_for_list_of_tables_for_constraint_in_schema, Query_for_list_of_tables_views, Query_for_list_of_tablespaces, Query_for_list_of_timezone_names_quoted_in, Query_for_list_of_timezone_names_quoted_out, Query_for_list_of_types, Query_for_list_of_views, Query_for_values_of_enum_GUC, runCatalogQuery } from "./queries.js";
5
+ //#region src/psql/complete/rules.ts
103
6
  /** Backslash command names psql tab-completes (mirrors backslash_commands[]). */
104
- export const BACKSLASH_COMMANDS = [
105
- "\\a",
106
- "\\bind",
107
- "\\bind_named",
108
- "\\c",
109
- "\\C",
110
- "\\cd",
111
- "\\close_prepared",
112
- "\\conninfo",
113
- "\\connect",
114
- "\\copy",
115
- "\\copyright",
116
- "\\crosstabview",
117
- "\\d",
118
- "\\dA",
119
- "\\dAc",
120
- "\\dAf",
121
- "\\dAo",
122
- "\\dAp",
123
- "\\da",
124
- "\\db",
125
- "\\dC",
126
- "\\dc",
127
- "\\dconfig",
128
- "\\dD",
129
- "\\dd",
130
- "\\ddp",
131
- "\\dE",
132
- "\\des",
133
- "\\det",
134
- "\\deu",
135
- "\\dew",
136
- "\\df",
137
- "\\dF",
138
- "\\dFd",
139
- "\\dFp",
140
- "\\dFt",
141
- "\\dg",
142
- "\\di",
143
- "\\dl",
144
- "\\dL",
145
- "\\dm",
146
- "\\dn",
147
- "\\do",
148
- "\\dO",
149
- "\\dp",
150
- "\\dP",
151
- "\\dPi",
152
- "\\dPt",
153
- "\\drds",
154
- "\\drg",
155
- "\\dRs",
156
- "\\dRp",
157
- "\\ds",
158
- "\\dt",
159
- "\\dT",
160
- "\\dv",
161
- "\\du",
162
- "\\dx",
163
- "\\dX",
164
- "\\dy",
165
- "\\echo",
166
- "\\edit",
167
- "\\ef",
168
- "\\elif",
169
- "\\else",
170
- "\\encoding",
171
- "\\endif",
172
- "\\endpipeline",
173
- "\\errverbose",
174
- "\\ev",
175
- "\\f",
176
- "\\flush",
177
- "\\flushrequest",
178
- "\\g",
179
- "\\gdesc",
180
- "\\getenv",
181
- "\\getresults",
182
- "\\gexec",
183
- "\\gset",
184
- "\\gx",
185
- "\\help",
186
- "\\html",
187
- "\\if",
188
- "\\include",
189
- "\\include_relative",
190
- "\\ir",
191
- "\\l",
192
- "\\list",
193
- "\\lo_export",
194
- "\\lo_import",
195
- "\\lo_list",
196
- "\\lo_unlink",
197
- "\\o",
198
- "\\out",
199
- "\\parse",
200
- "\\password",
201
- "\\print",
202
- "\\prompt",
203
- "\\pset",
204
- "\\q",
205
- "\\qecho",
206
- "\\quit",
207
- "\\reset",
208
- "\\restrict",
209
- "\\s",
210
- "\\sendpipeline",
211
- "\\set",
212
- "\\setenv",
213
- "\\sf",
214
- "\\startpipeline",
215
- "\\sv",
216
- "\\syncpipeline",
217
- "\\t",
218
- "\\T",
219
- "\\timing",
220
- "\\unrestrict",
221
- "\\unset",
222
- "\\w",
223
- "\\warn",
224
- "\\watch",
225
- "\\write",
226
- "\\x",
227
- "\\z",
228
- "\\!",
229
- "\\?",
7
+ const BACKSLASH_COMMANDS = [
8
+ "\\a",
9
+ "\\bind",
10
+ "\\bind_named",
11
+ "\\c",
12
+ "\\C",
13
+ "\\cd",
14
+ "\\close_prepared",
15
+ "\\conninfo",
16
+ "\\connect",
17
+ "\\copy",
18
+ "\\copyright",
19
+ "\\crosstabview",
20
+ "\\d",
21
+ "\\dA",
22
+ "\\dAc",
23
+ "\\dAf",
24
+ "\\dAo",
25
+ "\\dAp",
26
+ "\\da",
27
+ "\\db",
28
+ "\\dC",
29
+ "\\dc",
30
+ "\\dconfig",
31
+ "\\dD",
32
+ "\\dd",
33
+ "\\ddp",
34
+ "\\dE",
35
+ "\\des",
36
+ "\\det",
37
+ "\\deu",
38
+ "\\dew",
39
+ "\\df",
40
+ "\\dF",
41
+ "\\dFd",
42
+ "\\dFp",
43
+ "\\dFt",
44
+ "\\dg",
45
+ "\\di",
46
+ "\\dl",
47
+ "\\dL",
48
+ "\\dm",
49
+ "\\dn",
50
+ "\\do",
51
+ "\\dO",
52
+ "\\dp",
53
+ "\\dP",
54
+ "\\dPi",
55
+ "\\dPt",
56
+ "\\drds",
57
+ "\\drg",
58
+ "\\dRs",
59
+ "\\dRp",
60
+ "\\ds",
61
+ "\\dt",
62
+ "\\dT",
63
+ "\\dv",
64
+ "\\du",
65
+ "\\dx",
66
+ "\\dX",
67
+ "\\dy",
68
+ "\\echo",
69
+ "\\edit",
70
+ "\\ef",
71
+ "\\elif",
72
+ "\\else",
73
+ "\\encoding",
74
+ "\\endif",
75
+ "\\endpipeline",
76
+ "\\errverbose",
77
+ "\\ev",
78
+ "\\f",
79
+ "\\flush",
80
+ "\\flushrequest",
81
+ "\\g",
82
+ "\\gdesc",
83
+ "\\getenv",
84
+ "\\getresults",
85
+ "\\gexec",
86
+ "\\gset",
87
+ "\\gx",
88
+ "\\help",
89
+ "\\html",
90
+ "\\if",
91
+ "\\include",
92
+ "\\include_relative",
93
+ "\\ir",
94
+ "\\l",
95
+ "\\list",
96
+ "\\lo_export",
97
+ "\\lo_import",
98
+ "\\lo_list",
99
+ "\\lo_unlink",
100
+ "\\o",
101
+ "\\out",
102
+ "\\parse",
103
+ "\\password",
104
+ "\\print",
105
+ "\\prompt",
106
+ "\\pset",
107
+ "\\q",
108
+ "\\qecho",
109
+ "\\quit",
110
+ "\\reset",
111
+ "\\restrict",
112
+ "\\s",
113
+ "\\sendpipeline",
114
+ "\\set",
115
+ "\\setenv",
116
+ "\\sf",
117
+ "\\startpipeline",
118
+ "\\sv",
119
+ "\\syncpipeline",
120
+ "\\t",
121
+ "\\T",
122
+ "\\timing",
123
+ "\\unrestrict",
124
+ "\\unset",
125
+ "\\w",
126
+ "\\warn",
127
+ "\\watch",
128
+ "\\write",
129
+ "\\x",
130
+ "\\z",
131
+ "\\!",
132
+ "\\?"
230
133
  ];
231
134
  /** Top-level SQL statement keywords. */
232
- export const SQL_TOP_KEYWORDS = [
233
- "ABORT",
234
- "ALTER",
235
- "ANALYZE",
236
- "BEGIN",
237
- "CALL",
238
- "CHECKPOINT",
239
- "CLOSE",
240
- "CLUSTER",
241
- "COMMENT",
242
- "COMMIT",
243
- "COPY",
244
- "CREATE",
245
- "DEALLOCATE",
246
- "DECLARE",
247
- "DELETE FROM",
248
- "DISCARD",
249
- "DO",
250
- "DROP",
251
- "END",
252
- "EXECUTE",
253
- "EXPLAIN",
254
- "FETCH",
255
- "GRANT",
256
- "IMPORT",
257
- "INSERT INTO",
258
- "LISTEN",
259
- "LOAD",
260
- "LOCK",
261
- "MERGE",
262
- "MOVE",
263
- "NOTIFY",
264
- "PREPARE",
265
- "REASSIGN",
266
- "REFRESH MATERIALIZED VIEW",
267
- "REINDEX",
268
- "RELEASE",
269
- "RESET",
270
- "REVOKE",
271
- "ROLLBACK",
272
- "SAVEPOINT",
273
- "SECURITY LABEL",
274
- "SELECT",
275
- "SET",
276
- "SHOW",
277
- "START TRANSACTION",
278
- "TABLE",
279
- "TRUNCATE",
280
- "UNLISTEN",
281
- "UPDATE",
282
- "VACUUM",
283
- "VALUES",
284
- "WITH",
135
+ const SQL_TOP_KEYWORDS = [
136
+ "ABORT",
137
+ "ALTER",
138
+ "ANALYZE",
139
+ "BEGIN",
140
+ "CALL",
141
+ "CHECKPOINT",
142
+ "CLOSE",
143
+ "CLUSTER",
144
+ "COMMENT",
145
+ "COMMIT",
146
+ "COPY",
147
+ "CREATE",
148
+ "DEALLOCATE",
149
+ "DECLARE",
150
+ "DELETE FROM",
151
+ "DISCARD",
152
+ "DO",
153
+ "DROP",
154
+ "END",
155
+ "EXECUTE",
156
+ "EXPLAIN",
157
+ "FETCH",
158
+ "GRANT",
159
+ "IMPORT",
160
+ "INSERT INTO",
161
+ "LISTEN",
162
+ "LOAD",
163
+ "LOCK",
164
+ "MERGE",
165
+ "MOVE",
166
+ "NOTIFY",
167
+ "PREPARE",
168
+ "REASSIGN",
169
+ "REFRESH MATERIALIZED VIEW",
170
+ "REINDEX",
171
+ "RELEASE",
172
+ "RESET",
173
+ "REVOKE",
174
+ "ROLLBACK",
175
+ "SAVEPOINT",
176
+ "SECURITY LABEL",
177
+ "SELECT",
178
+ "SET",
179
+ "SHOW",
180
+ "START TRANSACTION",
181
+ "TABLE",
182
+ "TRUNCATE",
183
+ "UNLISTEN",
184
+ "UPDATE",
185
+ "VACUUM",
186
+ "VALUES",
187
+ "WITH"
285
188
  ];
286
189
  /** Keywords accepted after CREATE. */
287
- export const CREATE_OBJECTS = [
288
- "ACCESS METHOD",
289
- "AGGREGATE",
290
- "CAST",
291
- "COLLATION",
292
- "CONVERSION",
293
- "DATABASE",
294
- "DEFAULT PRIVILEGES",
295
- "DOMAIN",
296
- "EVENT TRIGGER",
297
- "EXTENSION",
298
- "FOREIGN DATA WRAPPER",
299
- "FOREIGN TABLE",
300
- "FUNCTION",
301
- "GLOBAL",
302
- "GROUP",
303
- "INDEX",
304
- "LANGUAGE",
305
- "LOCAL",
306
- "MATERIALIZED VIEW",
307
- "OPERATOR",
308
- "OR REPLACE",
309
- "POLICY",
310
- "PROCEDURE",
311
- "PUBLICATION",
312
- "ROLE",
313
- "RULE",
314
- "SCHEMA",
315
- "SEQUENCE",
316
- "SERVER",
317
- "STATISTICS",
318
- "SUBSCRIPTION",
319
- "TABLE",
320
- "TABLESPACE",
321
- "TEMP",
322
- "TEMPORARY",
323
- "TEXT SEARCH",
324
- "TRANSFORM",
325
- "TRIGGER",
326
- "TYPE",
327
- "UNIQUE",
328
- "UNLOGGED",
329
- "USER",
330
- "VIEW",
190
+ const CREATE_OBJECTS = [
191
+ "ACCESS METHOD",
192
+ "AGGREGATE",
193
+ "CAST",
194
+ "COLLATION",
195
+ "CONVERSION",
196
+ "DATABASE",
197
+ "DEFAULT PRIVILEGES",
198
+ "DOMAIN",
199
+ "EVENT TRIGGER",
200
+ "EXTENSION",
201
+ "FOREIGN DATA WRAPPER",
202
+ "FOREIGN TABLE",
203
+ "FUNCTION",
204
+ "GLOBAL",
205
+ "GROUP",
206
+ "INDEX",
207
+ "LANGUAGE",
208
+ "LOCAL",
209
+ "MATERIALIZED VIEW",
210
+ "OPERATOR",
211
+ "OR REPLACE",
212
+ "POLICY",
213
+ "PROCEDURE",
214
+ "PUBLICATION",
215
+ "ROLE",
216
+ "RULE",
217
+ "SCHEMA",
218
+ "SEQUENCE",
219
+ "SERVER",
220
+ "STATISTICS",
221
+ "SUBSCRIPTION",
222
+ "TABLE",
223
+ "TABLESPACE",
224
+ "TEMP",
225
+ "TEMPORARY",
226
+ "TEXT SEARCH",
227
+ "TRANSFORM",
228
+ "TRIGGER",
229
+ "TYPE",
230
+ "UNIQUE",
231
+ "UNLOGGED",
232
+ "USER",
233
+ "VIEW"
331
234
  ];
332
235
  /** Keywords accepted after DROP. */
333
- export const DROP_OBJECTS = [
334
- "ACCESS METHOD",
335
- "AGGREGATE",
336
- "CAST",
337
- "COLLATION",
338
- "CONVERSION",
339
- "DATABASE",
340
- "DOMAIN",
341
- "EVENT TRIGGER",
342
- "EXTENSION",
343
- "FOREIGN DATA WRAPPER",
344
- "FOREIGN TABLE",
345
- "FUNCTION",
346
- "GROUP",
347
- "INDEX",
348
- "LANGUAGE",
349
- "MATERIALIZED VIEW",
350
- "OPERATOR",
351
- "OWNED",
352
- "POLICY",
353
- "PROCEDURE",
354
- "PUBLICATION",
355
- "ROLE",
356
- "RULE",
357
- "SCHEMA",
358
- "SEQUENCE",
359
- "SERVER",
360
- "STATISTICS",
361
- "SUBSCRIPTION",
362
- "TABLE",
363
- "TABLESPACE",
364
- "TEXT SEARCH",
365
- "TRANSFORM",
366
- "TRIGGER",
367
- "TYPE",
368
- "USER",
369
- "VIEW",
236
+ const DROP_OBJECTS = [
237
+ "ACCESS METHOD",
238
+ "AGGREGATE",
239
+ "CAST",
240
+ "COLLATION",
241
+ "CONVERSION",
242
+ "DATABASE",
243
+ "DOMAIN",
244
+ "EVENT TRIGGER",
245
+ "EXTENSION",
246
+ "FOREIGN DATA WRAPPER",
247
+ "FOREIGN TABLE",
248
+ "FUNCTION",
249
+ "GROUP",
250
+ "INDEX",
251
+ "LANGUAGE",
252
+ "MATERIALIZED VIEW",
253
+ "OPERATOR",
254
+ "OWNED",
255
+ "POLICY",
256
+ "PROCEDURE",
257
+ "PUBLICATION",
258
+ "ROLE",
259
+ "RULE",
260
+ "SCHEMA",
261
+ "SEQUENCE",
262
+ "SERVER",
263
+ "STATISTICS",
264
+ "SUBSCRIPTION",
265
+ "TABLE",
266
+ "TABLESPACE",
267
+ "TEXT SEARCH",
268
+ "TRANSFORM",
269
+ "TRIGGER",
270
+ "TYPE",
271
+ "USER",
272
+ "VIEW"
370
273
  ];
371
274
  /** Keywords accepted after ALTER. */
372
- export const ALTER_OBJECTS = [
373
- "AGGREGATE",
374
- "COLLATION",
375
- "CONVERSION",
376
- "DATABASE",
377
- "DEFAULT PRIVILEGES",
378
- "DOMAIN",
379
- "EVENT TRIGGER",
380
- "EXTENSION",
381
- "FOREIGN DATA WRAPPER",
382
- "FOREIGN TABLE",
383
- "FUNCTION",
384
- "GROUP",
385
- "INDEX",
386
- "LANGUAGE",
387
- "LARGE OBJECT",
388
- "MATERIALIZED VIEW",
389
- "OPERATOR",
390
- "POLICY",
391
- "PROCEDURE",
392
- "PUBLICATION",
393
- "ROLE",
394
- "RULE",
395
- "SCHEMA",
396
- "SEQUENCE",
397
- "SERVER",
398
- "STATISTICS",
399
- "SUBSCRIPTION",
400
- "SYSTEM",
401
- "TABLE",
402
- "TABLESPACE",
403
- "TEXT SEARCH",
404
- "TRIGGER",
405
- "TYPE",
406
- "USER",
407
- "VIEW",
275
+ const ALTER_OBJECTS = [
276
+ "AGGREGATE",
277
+ "COLLATION",
278
+ "CONVERSION",
279
+ "DATABASE",
280
+ "DEFAULT PRIVILEGES",
281
+ "DOMAIN",
282
+ "EVENT TRIGGER",
283
+ "EXTENSION",
284
+ "FOREIGN DATA WRAPPER",
285
+ "FOREIGN TABLE",
286
+ "FUNCTION",
287
+ "GROUP",
288
+ "INDEX",
289
+ "LANGUAGE",
290
+ "LARGE OBJECT",
291
+ "MATERIALIZED VIEW",
292
+ "OPERATOR",
293
+ "POLICY",
294
+ "PROCEDURE",
295
+ "PUBLICATION",
296
+ "ROLE",
297
+ "RULE",
298
+ "SCHEMA",
299
+ "SEQUENCE",
300
+ "SERVER",
301
+ "STATISTICS",
302
+ "SUBSCRIPTION",
303
+ "SYSTEM",
304
+ "TABLE",
305
+ "TABLESPACE",
306
+ "TEXT SEARCH",
307
+ "TRIGGER",
308
+ "TYPE",
309
+ "USER",
310
+ "VIEW"
408
311
  ];
409
312
  /** Sub-actions for ALTER TABLE. */
410
- export const ALTER_TABLE_ACTIONS = [
411
- "ADD",
412
- "ALTER",
413
- "ATTACH PARTITION",
414
- "CLUSTER ON",
415
- "DETACH PARTITION",
416
- "DISABLE",
417
- "DROP",
418
- "ENABLE",
419
- "INHERIT",
420
- "NO INHERIT",
421
- "OF",
422
- "NOT OF",
423
- "OWNER TO",
424
- "RENAME",
425
- "REPLICA IDENTITY",
426
- "RESET",
427
- "SET",
428
- "VALIDATE CONSTRAINT",
313
+ const ALTER_TABLE_ACTIONS = [
314
+ "ADD",
315
+ "ALTER",
316
+ "ATTACH PARTITION",
317
+ "CLUSTER ON",
318
+ "DETACH PARTITION",
319
+ "DISABLE",
320
+ "DROP",
321
+ "ENABLE",
322
+ "INHERIT",
323
+ "NO INHERIT",
324
+ "OF",
325
+ "NOT OF",
326
+ "OWNER TO",
327
+ "RENAME",
328
+ "REPLICA IDENTITY",
329
+ "RESET",
330
+ "SET",
331
+ "VALIDATE CONSTRAINT"
429
332
  ];
430
333
  /** Continuation after `ALTER TABLE x ADD`. */
431
- export const ALTER_TABLE_ADD = [
432
- "COLUMN",
433
- "CONSTRAINT",
434
- "CHECK",
435
- "FOREIGN KEY",
436
- "PRIMARY KEY",
437
- "UNIQUE",
438
- "EXCLUDE",
334
+ const ALTER_TABLE_ADD = [
335
+ "COLUMN",
336
+ "CONSTRAINT",
337
+ "CHECK",
338
+ "FOREIGN KEY",
339
+ "PRIMARY KEY",
340
+ "UNIQUE",
341
+ "EXCLUDE"
439
342
  ];
440
343
  /** Continuation after `ALTER TABLE x ALTER [COLUMN] y`. */
441
- export const ALTER_TABLE_ALTER_COLUMN = [
442
- "ADD GENERATED",
443
- "DROP DEFAULT",
444
- "DROP EXPRESSION",
445
- "DROP IDENTITY",
446
- "DROP NOT NULL",
447
- "RESET",
448
- "RESTART",
449
- "SET",
450
- "SET DATA TYPE",
451
- "SET DEFAULT",
452
- "SET EXPRESSION",
453
- "SET GENERATED",
454
- "SET NOT NULL",
455
- "SET STATISTICS",
456
- "SET STORAGE",
457
- "TYPE",
344
+ const ALTER_TABLE_ALTER_COLUMN = [
345
+ "ADD GENERATED",
346
+ "DROP DEFAULT",
347
+ "DROP EXPRESSION",
348
+ "DROP IDENTITY",
349
+ "DROP NOT NULL",
350
+ "RESET",
351
+ "RESTART",
352
+ "SET",
353
+ "SET DATA TYPE",
354
+ "SET DEFAULT",
355
+ "SET EXPRESSION",
356
+ "SET GENERATED",
357
+ "SET NOT NULL",
358
+ "SET STATISTICS",
359
+ "SET STORAGE",
360
+ "TYPE"
458
361
  ];
459
362
  /** Continuation after `ALTER TABLE x DROP`. */
460
- export const ALTER_TABLE_DROP = [
461
- "COLUMN",
462
- "CONSTRAINT",
463
- "IF EXISTS",
363
+ const ALTER_TABLE_DROP = [
364
+ "COLUMN",
365
+ "CONSTRAINT",
366
+ "IF EXISTS"
464
367
  ];
465
368
  /** Continuation after `ALTER TABLE x RENAME`. */
466
- export const ALTER_TABLE_RENAME = [
467
- "COLUMN",
468
- "CONSTRAINT",
469
- "TO",
369
+ const ALTER_TABLE_RENAME = [
370
+ "COLUMN",
371
+ "CONSTRAINT",
372
+ "TO"
470
373
  ];
471
374
  /** Continuation after `ALTER TABLE x SET`. */
472
- export const ALTER_TABLE_SET = [
473
- "(",
474
- "LOGGED",
475
- "SCHEMA",
476
- "TABLESPACE",
477
- "UNLOGGED",
478
- "WITHOUT CLUSTER",
479
- "WITHOUT OIDS",
375
+ const ALTER_TABLE_SET = [
376
+ "(",
377
+ "LOGGED",
378
+ "SCHEMA",
379
+ "TABLESPACE",
380
+ "UNLOGGED",
381
+ "WITHOUT CLUSTER",
382
+ "WITHOUT OIDS"
480
383
  ];
481
384
  /** Continuation after `ALTER TABLE x ENABLE`. */
482
- export const ALTER_TABLE_ENABLE = [
483
- "ALWAYS",
484
- "REPLICA",
485
- "ROW LEVEL SECURITY",
486
- "RULE",
487
- "TRIGGER",
385
+ const ALTER_TABLE_ENABLE = [
386
+ "ALWAYS",
387
+ "REPLICA",
388
+ "ROW LEVEL SECURITY",
389
+ "RULE",
390
+ "TRIGGER"
488
391
  ];
489
392
  /** Continuation after `ALTER TABLE x DISABLE`. */
490
- export const ALTER_TABLE_DISABLE = [
491
- "ROW LEVEL SECURITY",
492
- "RULE",
493
- "TRIGGER",
393
+ const ALTER_TABLE_DISABLE = [
394
+ "ROW LEVEL SECURITY",
395
+ "RULE",
396
+ "TRIGGER"
494
397
  ];
495
398
  /** Continuation after `ALTER TABLE x REPLICA IDENTITY`. */
496
- export const ALTER_TABLE_REPLICA_IDENTITY = [
497
- "DEFAULT",
498
- "FULL",
499
- "NOTHING",
500
- "USING INDEX",
399
+ const ALTER_TABLE_REPLICA_IDENTITY = [
400
+ "DEFAULT",
401
+ "FULL",
402
+ "NOTHING",
403
+ "USING INDEX"
501
404
  ];
502
405
  /** Sub-actions for ALTER VIEW. */
503
- export const ALTER_VIEW_ACTIONS = [
504
- "ALTER",
505
- "OWNER TO",
506
- "RENAME",
507
- "RESET",
508
- "SET",
406
+ const ALTER_VIEW_ACTIONS = [
407
+ "ALTER",
408
+ "OWNER TO",
409
+ "RENAME",
410
+ "RESET",
411
+ "SET"
509
412
  ];
510
413
  /** Sub-actions for ALTER MATERIALIZED VIEW. */
511
- export const ALTER_MATVIEW_ACTIONS = [
512
- "ALTER",
513
- "CLUSTER ON",
514
- "DEPENDS ON EXTENSION",
515
- "NO DEPENDS ON EXTENSION",
516
- "OWNER TO",
517
- "RENAME",
518
- "RESET",
519
- "SET",
414
+ const ALTER_MATVIEW_ACTIONS = [
415
+ "ALTER",
416
+ "CLUSTER ON",
417
+ "DEPENDS ON EXTENSION",
418
+ "NO DEPENDS ON EXTENSION",
419
+ "OWNER TO",
420
+ "RENAME",
421
+ "RESET",
422
+ "SET"
520
423
  ];
521
424
  /** Sub-actions for ALTER INDEX. */
522
- export const ALTER_INDEX_ACTIONS = [
523
- "ALTER COLUMN",
524
- "ATTACH PARTITION",
525
- "DEPENDS ON EXTENSION",
526
- "NO DEPENDS ON EXTENSION",
527
- "OWNER TO",
528
- "RENAME",
529
- "RESET",
530
- "SET",
425
+ const ALTER_INDEX_ACTIONS = [
426
+ "ALTER COLUMN",
427
+ "ATTACH PARTITION",
428
+ "DEPENDS ON EXTENSION",
429
+ "NO DEPENDS ON EXTENSION",
430
+ "OWNER TO",
431
+ "RENAME",
432
+ "RESET",
433
+ "SET"
531
434
  ];
532
435
  /** Sub-actions for ALTER SEQUENCE. */
533
- export const ALTER_SEQUENCE_ACTIONS = [
534
- "AS",
535
- "CACHE",
536
- "CYCLE",
537
- "INCREMENT BY",
538
- "MAXVALUE",
539
- "MINVALUE",
540
- "NO CYCLE",
541
- "NO MAXVALUE",
542
- "NO MINVALUE",
543
- "OWNED BY",
544
- "OWNER TO",
545
- "RENAME TO",
546
- "RESTART",
547
- "SET SCHEMA",
548
- "START WITH",
436
+ const ALTER_SEQUENCE_ACTIONS = [
437
+ "AS",
438
+ "CACHE",
439
+ "CYCLE",
440
+ "INCREMENT BY",
441
+ "MAXVALUE",
442
+ "MINVALUE",
443
+ "NO CYCLE",
444
+ "NO MAXVALUE",
445
+ "NO MINVALUE",
446
+ "OWNED BY",
447
+ "OWNER TO",
448
+ "RENAME TO",
449
+ "RESTART",
450
+ "SET SCHEMA",
451
+ "START WITH"
549
452
  ];
550
453
  /** Sub-actions for ALTER FUNCTION / PROCEDURE / ROUTINE. */
551
- export const ALTER_FUNCTION_ACTIONS = [
552
- "CALLED ON NULL INPUT",
553
- "COST",
554
- "DEPENDS ON EXTENSION",
555
- "IMMUTABLE",
556
- "LEAKPROOF",
557
- "NO DEPENDS ON EXTENSION",
558
- "NOT LEAKPROOF",
559
- "OWNER TO",
560
- "PARALLEL",
561
- "RENAME TO",
562
- "RESET",
563
- "RETURNS NULL ON NULL INPUT",
564
- "ROWS",
565
- "SECURITY DEFINER",
566
- "SECURITY INVOKER",
567
- "SET",
568
- "SET SCHEMA",
569
- "STABLE",
570
- "STRICT",
571
- "SUPPORT",
572
- "VOLATILE",
454
+ const ALTER_FUNCTION_ACTIONS = [
455
+ "CALLED ON NULL INPUT",
456
+ "COST",
457
+ "DEPENDS ON EXTENSION",
458
+ "IMMUTABLE",
459
+ "LEAKPROOF",
460
+ "NO DEPENDS ON EXTENSION",
461
+ "NOT LEAKPROOF",
462
+ "OWNER TO",
463
+ "PARALLEL",
464
+ "RENAME TO",
465
+ "RESET",
466
+ "RETURNS NULL ON NULL INPUT",
467
+ "ROWS",
468
+ "SECURITY DEFINER",
469
+ "SECURITY INVOKER",
470
+ "SET",
471
+ "SET SCHEMA",
472
+ "STABLE",
473
+ "STRICT",
474
+ "SUPPORT",
475
+ "VOLATILE"
573
476
  ];
574
477
  /** Sub-actions for ALTER TYPE. */
575
- export const ALTER_TYPE_ACTIONS = [
576
- "ADD ATTRIBUTE",
577
- "ADD VALUE",
578
- "ALTER ATTRIBUTE",
579
- "DROP ATTRIBUTE",
580
- "OWNER TO",
581
- "RENAME",
582
- "RENAME ATTRIBUTE",
583
- "RENAME VALUE",
584
- "SET SCHEMA",
585
- "SET",
478
+ const ALTER_TYPE_ACTIONS = [
479
+ "ADD ATTRIBUTE",
480
+ "ADD VALUE",
481
+ "ALTER ATTRIBUTE",
482
+ "DROP ATTRIBUTE",
483
+ "OWNER TO",
484
+ "RENAME",
485
+ "RENAME ATTRIBUTE",
486
+ "RENAME VALUE",
487
+ "SET SCHEMA",
488
+ "SET"
586
489
  ];
587
490
  /** Sub-actions for ALTER ROLE / USER. */
588
- export const ALTER_ROLE_ACTIONS = [
589
- "BYPASSRLS",
590
- "CONNECTION LIMIT",
591
- "CREATEDB",
592
- "CREATEROLE",
593
- "ENCRYPTED PASSWORD",
594
- "IN DATABASE",
595
- "INHERIT",
596
- "LOGIN",
597
- "NOBYPASSRLS",
598
- "NOCREATEDB",
599
- "NOCREATEROLE",
600
- "NOINHERIT",
601
- "NOLOGIN",
602
- "NOREPLICATION",
603
- "NOSUPERUSER",
604
- "PASSWORD",
605
- "RENAME TO",
606
- "REPLICATION",
607
- "RESET",
608
- "SET",
609
- "SUPERUSER",
610
- "VALID UNTIL",
611
- "WITH",
491
+ const ALTER_ROLE_ACTIONS = [
492
+ "BYPASSRLS",
493
+ "CONNECTION LIMIT",
494
+ "CREATEDB",
495
+ "CREATEROLE",
496
+ "ENCRYPTED PASSWORD",
497
+ "IN DATABASE",
498
+ "INHERIT",
499
+ "LOGIN",
500
+ "NOBYPASSRLS",
501
+ "NOCREATEDB",
502
+ "NOCREATEROLE",
503
+ "NOINHERIT",
504
+ "NOLOGIN",
505
+ "NOREPLICATION",
506
+ "NOSUPERUSER",
507
+ "PASSWORD",
508
+ "RENAME TO",
509
+ "REPLICATION",
510
+ "RESET",
511
+ "SET",
512
+ "SUPERUSER",
513
+ "VALID UNTIL",
514
+ "WITH"
612
515
  ];
613
516
  /** Sub-actions for ALTER DATABASE. */
614
- export const ALTER_DATABASE_ACTIONS = [
615
- "ALLOW_CONNECTIONS",
616
- "CONNECTION LIMIT",
617
- "IS_TEMPLATE",
618
- "OWNER TO",
619
- "REFRESH COLLATION VERSION",
620
- "RENAME TO",
621
- "RESET",
622
- "SET",
623
- "SET TABLESPACE",
624
- "WITH",
517
+ const ALTER_DATABASE_ACTIONS = [
518
+ "ALLOW_CONNECTIONS",
519
+ "CONNECTION LIMIT",
520
+ "IS_TEMPLATE",
521
+ "OWNER TO",
522
+ "REFRESH COLLATION VERSION",
523
+ "RENAME TO",
524
+ "RESET",
525
+ "SET",
526
+ "SET TABLESPACE",
527
+ "WITH"
625
528
  ];
626
529
  /** Sub-actions for ALTER SCHEMA. */
627
- export const ALTER_SCHEMA_ACTIONS = [
628
- "OWNER TO",
629
- "RENAME TO",
630
- ];
530
+ const ALTER_SCHEMA_ACTIONS = ["OWNER TO", "RENAME TO"];
631
531
  /** Sub-actions for ALTER EXTENSION. */
632
- export const ALTER_EXTENSION_ACTIONS = [
633
- "ADD",
634
- "DROP",
635
- "SET SCHEMA",
636
- "UPDATE",
532
+ const ALTER_EXTENSION_ACTIONS = [
533
+ "ADD",
534
+ "DROP",
535
+ "SET SCHEMA",
536
+ "UPDATE"
637
537
  ];
638
538
  /** Sub-actions for ALTER POLICY. */
639
- export const ALTER_POLICY_ACTIONS = ["ON", "RENAME TO"];
539
+ const ALTER_POLICY_ACTIONS = ["ON", "RENAME TO"];
640
540
  /** Sub-actions for ALTER PUBLICATION. */
641
- export const ALTER_PUBLICATION_ACTIONS = [
642
- "ADD",
643
- "DROP",
644
- "OWNER TO",
645
- "RENAME TO",
646
- "SET",
541
+ const ALTER_PUBLICATION_ACTIONS = [
542
+ "ADD",
543
+ "DROP",
544
+ "OWNER TO",
545
+ "RENAME TO",
546
+ "SET"
647
547
  ];
648
548
  /** Sub-actions for ALTER SUBSCRIPTION. */
649
- export const ALTER_SUBSCRIPTION_ACTIONS = [
650
- "ADD PUBLICATION",
651
- "CONNECTION",
652
- "DISABLE",
653
- "DROP PUBLICATION",
654
- "ENABLE",
655
- "OWNER TO",
656
- "REFRESH PUBLICATION",
657
- "RENAME TO",
658
- "SET",
659
- "SET PUBLICATION",
660
- "SKIP",
549
+ const ALTER_SUBSCRIPTION_ACTIONS = [
550
+ "ADD PUBLICATION",
551
+ "CONNECTION",
552
+ "DISABLE",
553
+ "DROP PUBLICATION",
554
+ "ENABLE",
555
+ "OWNER TO",
556
+ "REFRESH PUBLICATION",
557
+ "RENAME TO",
558
+ "SET",
559
+ "SET PUBLICATION",
560
+ "SKIP"
661
561
  ];
662
562
  /** CREATE INDEX top-level options. */
663
- export const CREATE_INDEX_OPTIONS = [
664
- "CONCURRENTLY",
665
- "IF NOT EXISTS",
666
- "ON",
667
- "UNIQUE",
563
+ const CREATE_INDEX_OPTIONS = [
564
+ "CONCURRENTLY",
565
+ "IF NOT EXISTS",
566
+ "ON",
567
+ "UNIQUE"
668
568
  ];
669
569
  /** Window frame clauses after `OVER (`. */
670
- export const WINDOW_FRAME_KEYWORDS = [
671
- "GROUPS",
672
- "ORDER BY",
673
- "PARTITION BY",
674
- "RANGE",
675
- "ROWS",
570
+ const WINDOW_FRAME_KEYWORDS = [
571
+ "GROUPS",
572
+ "ORDER BY",
573
+ "PARTITION BY",
574
+ "RANGE",
575
+ "ROWS"
676
576
  ];
677
577
  /** Tail keywords that follow a `FROM <table>` clause in a query. */
678
- export const POST_FROM_KEYWORDS = [
679
- "AS",
680
- "CROSS JOIN",
681
- "EXCEPT",
682
- "FETCH",
683
- "FOR",
684
- "FULL JOIN",
685
- "FULL OUTER JOIN",
686
- "GROUP BY",
687
- "HAVING",
688
- "INNER JOIN",
689
- "INTERSECT",
690
- "JOIN",
691
- "LATERAL",
692
- "LEFT JOIN",
693
- "LEFT OUTER JOIN",
694
- "LIMIT",
695
- "NATURAL JOIN",
696
- "OFFSET",
697
- "ON",
698
- "ORDER BY",
699
- "RIGHT JOIN",
700
- "RIGHT OUTER JOIN",
701
- "TABLESAMPLE",
702
- "UNION",
703
- "USING",
704
- "WHERE",
705
- "WINDOW",
578
+ const POST_FROM_KEYWORDS = [
579
+ "AS",
580
+ "CROSS JOIN",
581
+ "EXCEPT",
582
+ "FETCH",
583
+ "FOR",
584
+ "FULL JOIN",
585
+ "FULL OUTER JOIN",
586
+ "GROUP BY",
587
+ "HAVING",
588
+ "INNER JOIN",
589
+ "INTERSECT",
590
+ "JOIN",
591
+ "LATERAL",
592
+ "LEFT JOIN",
593
+ "LEFT OUTER JOIN",
594
+ "LIMIT",
595
+ "NATURAL JOIN",
596
+ "OFFSET",
597
+ "ON",
598
+ "ORDER BY",
599
+ "RIGHT JOIN",
600
+ "RIGHT OUTER JOIN",
601
+ "TABLESAMPLE",
602
+ "UNION",
603
+ "USING",
604
+ "WHERE",
605
+ "WINDOW"
706
606
  ];
707
607
  /** Continuations within a WHERE expression. */
708
- export const WHERE_CONTINUATIONS = [
709
- "AND",
710
- "BETWEEN",
711
- "IN",
712
- "IS",
713
- "LIKE",
714
- "NOT",
715
- "OR",
608
+ const WHERE_CONTINUATIONS = [
609
+ "AND",
610
+ "BETWEEN",
611
+ "IN",
612
+ "IS",
613
+ "LIKE",
614
+ "NOT",
615
+ "OR"
716
616
  ];
717
617
  /** Boolean-style values used with `\set` for AUTOCOMMIT etc. (extends ON_OFF). */
718
- export const DATESTYLE_VALUES = [
719
- "GERMAN",
720
- "ISO",
721
- "POSTGRES",
722
- "SQL",
618
+ const DATESTYLE_VALUES = [
619
+ "GERMAN",
620
+ "ISO",
621
+ "POSTGRES",
622
+ "SQL"
723
623
  ];
724
624
  /** GRANT / REVOKE privileges. */
725
- export const PRIVILEGE_KEYWORDS = [
726
- "ALL",
727
- "CREATE",
728
- "CONNECT",
729
- "DELETE",
730
- "EXECUTE",
731
- "INSERT",
732
- "REFERENCES",
733
- "SELECT",
734
- "TEMPORARY",
735
- "TRIGGER",
736
- "TRUNCATE",
737
- "UPDATE",
738
- "USAGE",
625
+ const PRIVILEGE_KEYWORDS = [
626
+ "ALL",
627
+ "CREATE",
628
+ "CONNECT",
629
+ "DELETE",
630
+ "EXECUTE",
631
+ "INSERT",
632
+ "REFERENCES",
633
+ "SELECT",
634
+ "TEMPORARY",
635
+ "TRIGGER",
636
+ "TRUNCATE",
637
+ "UPDATE",
638
+ "USAGE"
739
639
  ];
740
640
  /** Common transaction/savepoint keywords. */
741
- export const TRANSACTION_KEYWORDS = [
742
- "ISOLATION LEVEL",
743
- "READ ONLY",
744
- "READ WRITE",
745
- "TRANSACTION",
641
+ const TRANSACTION_KEYWORDS = [
642
+ "ISOLATION LEVEL",
643
+ "READ ONLY",
644
+ "READ WRITE",
645
+ "TRANSACTION"
746
646
  ];
747
647
  /**
748
- * Built-in scalar type keywords that psql tab-completion mixes in
749
- * wherever a type name is expected. Mirrors upstream's
750
- * `Keywords_for_list_of_datatypes` (tab-complete.in.c). The multi-word
751
- * names disabled under `#ifdef NOT_USED` upstream are intentionally
752
- * omitted here too — tab completion can't disambiguate across word
753
- * boundaries.
754
- */
755
- export const BUILTIN_DATATYPE_KEYWORDS = [
756
- "bigint",
757
- "boolean",
758
- "character",
759
- "double precision",
760
- "integer",
761
- "real",
762
- "smallint",
648
+ * Built-in scalar type keywords that psql tab-completion mixes in
649
+ * wherever a type name is expected. Mirrors upstream's
650
+ * `Keywords_for_list_of_datatypes` (tab-complete.in.c). The multi-word
651
+ * names disabled under `#ifdef NOT_USED` upstream are intentionally
652
+ * omitted here too — tab completion can't disambiguate across word
653
+ * boundaries.
654
+ */
655
+ const BUILTIN_DATATYPE_KEYWORDS = [
656
+ "bigint",
657
+ "boolean",
658
+ "character",
659
+ "double precision",
660
+ "integer",
661
+ "real",
662
+ "smallint"
763
663
  ];
764
664
  /**
765
- * COPY ... FROM ... WITH ( ... ) option keywords. Mirrors upstream's
766
- * `Copy_from_options` macro = `Copy_common_options` + the FROM-specific
767
- * extras (DEFAULT, FORCE_NOT_NULL, FORCE_NULL, FREEZE, LOG_VERBOSITY,
768
- * ON_ERROR, REJECT_LIMIT).
769
- */
770
- export const COPY_FROM_OPTIONS = [
771
- "DELIMITER",
772
- "ENCODING",
773
- "ESCAPE",
774
- "FORMAT",
775
- "HEADER",
776
- "NULL",
777
- "QUOTE",
778
- "DEFAULT",
779
- "FORCE_NOT_NULL",
780
- "FORCE_NULL",
781
- "FREEZE",
782
- "LOG_VERBOSITY",
783
- "ON_ERROR",
784
- "REJECT_LIMIT",
665
+ * COPY ... FROM ... WITH ( ... ) option keywords. Mirrors upstream's
666
+ * `Copy_from_options` macro = `Copy_common_options` + the FROM-specific
667
+ * extras (DEFAULT, FORCE_NOT_NULL, FORCE_NULL, FREEZE, LOG_VERBOSITY,
668
+ * ON_ERROR, REJECT_LIMIT).
669
+ */
670
+ const COPY_FROM_OPTIONS = [
671
+ "DELIMITER",
672
+ "ENCODING",
673
+ "ESCAPE",
674
+ "FORMAT",
675
+ "HEADER",
676
+ "NULL",
677
+ "QUOTE",
678
+ "DEFAULT",
679
+ "FORCE_NOT_NULL",
680
+ "FORCE_NULL",
681
+ "FREEZE",
682
+ "LOG_VERBOSITY",
683
+ "ON_ERROR",
684
+ "REJECT_LIMIT"
785
685
  ];
786
686
  /**
787
- * COPY ... TO ... WITH ( ... ) option keywords. Mirrors upstream's
788
- * `Copy_to_options` macro = `Copy_common_options` + `FORCE_QUOTE`.
789
- */
790
- export const COPY_TO_OPTIONS = [
791
- "DELIMITER",
792
- "ENCODING",
793
- "ESCAPE",
794
- "FORMAT",
795
- "HEADER",
796
- "NULL",
797
- "QUOTE",
798
- "FORCE_QUOTE",
687
+ * COPY ... TO ... WITH ( ... ) option keywords. Mirrors upstream's
688
+ * `Copy_to_options` macro = `Copy_common_options` + `FORCE_QUOTE`.
689
+ */
690
+ const COPY_TO_OPTIONS = [
691
+ "DELIMITER",
692
+ "ENCODING",
693
+ "ESCAPE",
694
+ "FORMAT",
695
+ "HEADER",
696
+ "NULL",
697
+ "QUOTE",
698
+ "FORCE_QUOTE"
799
699
  ];
800
700
  /**
801
- * Apply a case-insensitive prefix filter. Empty `prefix` returns the whole
802
- * list. The match honours `compCase` — uppercase the candidates when the
803
- * user is typing in uppercase, etc.
804
- */
701
+ * Apply a case-insensitive prefix filter. Empty `prefix` returns the whole
702
+ * list. The match honours `compCase` — uppercase the candidates when the
703
+ * user is typing in uppercase, etc.
704
+ */
805
705
  const filterAndCase = (candidates, prefix, settings) => {
806
- const lowPrefix = prefix.toLowerCase();
807
- const result = [];
808
- for (const c of candidates) {
809
- if (c.toLowerCase().startsWith(lowPrefix)) {
810
- result.push(applyCase(c, prefix, settings));
811
- }
812
- }
813
- return result;
706
+ const lowPrefix = prefix.toLowerCase();
707
+ const result = [];
708
+ for (const c of candidates) if (c.toLowerCase().startsWith(lowPrefix)) result.push(applyCase(c, prefix, settings));
709
+ return result;
814
710
  };
815
711
  /**
816
- * Render a candidate with the case psql would use, based on `COMP_KEYWORD_CASE`:
817
- *
818
- * - lower → always lowercase, regardless of input.
819
- * - upper → always uppercase, regardless of input.
820
- * - preserve-lower → lowercase by default; uppercase if the user typed
821
- * a fragment containing an UPPERCASE letter.
822
- * - preserve-upper → (default) uppercase by default; lowercase if the
823
- * user typed a fragment containing a lowercase letter.
824
- *
825
- * Per psql docs: "preserve-upper, the default, returns the keyword in upper
826
- * case unless the partial word entered is in lower case". The dichotomy is
827
- * really "did the user type ANY lowercase letter" (for preserve-upper) and
828
- * "did the user type ANY uppercase letter" (for preserve-lower) — matching
829
- * upstream's `pg_str_endswith` / `pg_str_islower` heuristics.
830
- */
712
+ * Render a candidate with the case psql would use, based on `COMP_KEYWORD_CASE`:
713
+ *
714
+ * - lower → always lowercase, regardless of input.
715
+ * - upper → always uppercase, regardless of input.
716
+ * - preserve-lower → lowercase by default; uppercase if the user typed
717
+ * a fragment containing an UPPERCASE letter.
718
+ * - preserve-upper → (default) uppercase by default; lowercase if the
719
+ * user typed a fragment containing a lowercase letter.
720
+ *
721
+ * Per psql docs: "preserve-upper, the default, returns the keyword in upper
722
+ * case unless the partial word entered is in lower case". The dichotomy is
723
+ * really "did the user type ANY lowercase letter" (for preserve-upper) and
724
+ * "did the user type ANY uppercase letter" (for preserve-lower) — matching
725
+ * upstream's `pg_str_endswith` / `pg_str_islower` heuristics.
726
+ */
831
727
  const applyCase = (candidate, typed, settings) => {
832
- // Identifiers (already quoted, starts with ", or already lowercase) are
833
- // never re-cased.
834
- if (candidate.startsWith('"') || candidate.startsWith("'"))
835
- return candidate;
836
- // Catalog query results are always quoted-or-lowercase by virtue of
837
- // quote_ident; pass them through unchanged.
838
- if (containsNonKeywordChar(candidate))
839
- return candidate;
840
- const mode = settings.compCase;
841
- // Mirror upstream `pg_strdup_keyword_case` (tab-complete.c): the case
842
- // decision keys off the FIRST character of the user's input, not the
843
- // presence of any case anywhere. Empty input (`first` = 0) is treated as
844
- // neither lowercase nor alpha, so:
845
- // - preserve-upper → UPPERCASE (default mode; matches vanilla psql 18
846
- // for the `set <name> <TAB><TAB>` → `TO` case, upstream test
847
- // 010_tab_completion.pl line 366).
848
- // - preserve-lower → lowercase.
849
- // The same rule applies when the first char is a non-letter (digit, `_`,
850
- // punctuation) — those preserve the mode's default direction.
851
- const first = typed[0] ?? "";
852
- const firstIsLower = /[a-z]/.test(first);
853
- const firstIsAlpha = /[A-Za-z]/.test(first);
854
- const lowerCaseIt = mode === "lower" ||
855
- ((mode === "preserve-lower" || mode === "preserve-upper") &&
856
- firstIsLower) ||
857
- (mode === "preserve-lower" && !firstIsAlpha);
858
- if (mode === "upper")
859
- return candidate.toUpperCase();
860
- return lowerCaseIt ? candidate.toLowerCase() : candidate.toUpperCase();
728
+ if (candidate.startsWith("\"") || candidate.startsWith("'")) return candidate;
729
+ if (containsNonKeywordChar(candidate)) return candidate;
730
+ const mode = settings.compCase;
731
+ const first = typed[0] ?? "";
732
+ const firstIsLower = /[a-z]/.test(first);
733
+ const firstIsAlpha = /[A-Za-z]/.test(first);
734
+ const lowerCaseIt = mode === "lower" || (mode === "preserve-lower" || mode === "preserve-upper") && firstIsLower || mode === "preserve-lower" && !firstIsAlpha;
735
+ if (mode === "upper") return candidate.toUpperCase();
736
+ return lowerCaseIt ? candidate.toLowerCase() : candidate.toUpperCase();
861
737
  };
862
738
  const containsNonKeywordChar = (s) => /[^A-Za-z0-9 ]/.test(s);
863
739
  /**
864
- * Split a candidate like `pg_catalog.tab_` into [schema, prefix]. Returns
865
- * `null` if there's no schema qualifier. `schemaWasQuoted` records whether
866
- * the user wrote the schema in `"..."` form so the caller can decide whether
867
- * to fold its case in the rendered output.
868
- */
740
+ * Split a candidate like `pg_catalog.tab_` into [schema, prefix]. Returns
741
+ * `null` if there's no schema qualifier. `schemaWasQuoted` records whether
742
+ * the user wrote the schema in `"..."` form so the caller can decide whether
743
+ * to fold its case in the rendered output.
744
+ */
869
745
  const splitSchemaPrefix = (word) => {
870
- const dot = word.indexOf(".");
871
- if (dot < 0)
872
- return null;
873
- // Reject if anything after the dot looks like another dot (we only handle
874
- // schema.relation, not catalog.schema.relation).
875
- const after = word.slice(dot + 1);
876
- if (after.includes("."))
877
- return null;
878
- // Strip optional quoting on the schema.
879
- let schema = word.slice(0, dot);
880
- let schemaWasQuoted = false;
881
- if (schema.startsWith('"') && schema.endsWith('"')) {
882
- schemaWasQuoted = true;
883
- schema = schema.slice(1, -1).replace(/""/g, '"');
884
- }
885
- return { schema, prefix: after, schemaWasQuoted };
746
+ const dot = word.indexOf(".");
747
+ if (dot < 0) return null;
748
+ const after = word.slice(dot + 1);
749
+ if (after.includes(".")) return null;
750
+ let schema = word.slice(0, dot);
751
+ let schemaWasQuoted = false;
752
+ if (schema.startsWith("\"") && schema.endsWith("\"")) {
753
+ schemaWasQuoted = true;
754
+ schema = schema.slice(1, -1).replace(/""/g, "\"");
755
+ }
756
+ return {
757
+ schema,
758
+ prefix: after,
759
+ schemaWasQuoted
760
+ };
886
761
  };
887
762
  /**
888
- * Parse a table reference (the `<ref>` slot in
889
- * `ALTER TABLE <ref> DROP CONSTRAINT y`) from the already-tokenized
890
- * `prevWords` slice between `ALTER TABLE` and the action keyword.
891
- *
892
- * The scanner can produce the reference as 1 or 2 tokens depending on
893
- * quoting:
894
- *
895
- * - `tab1` → `["tab1"]` (bare, case-folded)
896
- * - `"tab1"` → `["\"tab1\""]` (quoted, exact-case)
897
- * - `public.tab1` → `["public.tab1"]` (single token, dotted)
898
- * - `public."tab1"` → `["public.\"tab1\""]` (single token, dotted+quoted)
899
- *
900
- * Returns the parsed parts with case-folding applied to UNQUOTED
901
- * identifiers (matching `pg_strcasecmp` semantics) so the caller can
902
- * pass them straight to a `WHERE relname = $N` catalog query.
903
- *
904
- * Returns `null` when the tokens don't look like a valid reference.
905
- */
763
+ * Parse a table reference (the `<ref>` slot in
764
+ * `ALTER TABLE <ref> DROP CONSTRAINT y`) from the already-tokenized
765
+ * `prevWords` slice between `ALTER TABLE` and the action keyword.
766
+ *
767
+ * The scanner can produce the reference as 1 or 2 tokens depending on
768
+ * quoting:
769
+ *
770
+ * - `tab1` → `["tab1"]` (bare, case-folded)
771
+ * - `"tab1"` → `["\"tab1\""]` (quoted, exact-case)
772
+ * - `public.tab1` → `["public.tab1"]` (single token, dotted)
773
+ * - `public."tab1"` → `["public.\"tab1\""]` (single token, dotted+quoted)
774
+ *
775
+ * Returns the parsed parts with case-folding applied to UNQUOTED
776
+ * identifiers (matching `pg_strcasecmp` semantics) so the caller can
777
+ * pass them straight to a `WHERE relname = $N` catalog query.
778
+ *
779
+ * Returns `null` when the tokens don't look like a valid reference.
780
+ */
906
781
  const parseTableRef = (refTokens) => {
907
- const stripQuote = (s) => {
908
- if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
909
- return { v: s.slice(1, -1).replace(/""/g, '"'), quoted: true };
910
- }
911
- return { v: s, quoted: false };
912
- };
913
- // Two-token form: `public.` + `"tab1"` (our scanner ends the first
914
- // token on the dot when the relation half is quoted).
915
- if (refTokens.length === 2) {
916
- const first = refTokens[0];
917
- if (!first.endsWith("."))
918
- return null;
919
- const s = stripQuote(first.slice(0, -1));
920
- const t = stripQuote(refTokens[1]);
921
- if (t.v.length === 0)
922
- return null;
923
- return {
924
- schema: s.quoted ? s.v : s.v.toLowerCase(),
925
- table: t.quoted ? t.v : t.v.toLowerCase(),
926
- };
927
- }
928
- if (refTokens.length !== 1)
929
- return null;
930
- const tok = refTokens[0];
931
- // Single-token form. Schema-qualified? Find the FIRST dot that isn't
932
- // inside `"..."`.
933
- let inQuote = false;
934
- let dot = -1;
935
- for (let i = 0; i < tok.length; i++) {
936
- const ch = tok[i];
937
- if (ch === '"') {
938
- inQuote = !inQuote;
939
- }
940
- else if (ch === "." && !inQuote) {
941
- dot = i;
942
- break;
943
- }
944
- }
945
- if (dot >= 0) {
946
- const s = stripQuote(tok.slice(0, dot));
947
- const t = stripQuote(tok.slice(dot + 1));
948
- if (t.v.length === 0)
949
- return null;
950
- return {
951
- schema: s.quoted ? s.v : s.v.toLowerCase(),
952
- table: t.quoted ? t.v : t.v.toLowerCase(),
953
- };
954
- }
955
- const t = stripQuote(tok);
956
- if (t.v.length === 0)
957
- return null;
958
- return { schema: null, table: t.quoted ? t.v : t.v.toLowerCase() };
782
+ const stripQuote = (s) => {
783
+ if (s.length >= 2 && s.startsWith("\"") && s.endsWith("\"")) return {
784
+ v: s.slice(1, -1).replace(/""/g, "\""),
785
+ quoted: true
786
+ };
787
+ return {
788
+ v: s,
789
+ quoted: false
790
+ };
791
+ };
792
+ if (refTokens.length === 2) {
793
+ const first = refTokens[0];
794
+ if (!first.endsWith(".")) return null;
795
+ const s = stripQuote(first.slice(0, -1));
796
+ const t = stripQuote(refTokens[1]);
797
+ if (t.v.length === 0) return null;
798
+ return {
799
+ schema: s.quoted ? s.v : s.v.toLowerCase(),
800
+ table: t.quoted ? t.v : t.v.toLowerCase()
801
+ };
802
+ }
803
+ if (refTokens.length !== 1) return null;
804
+ const tok = refTokens[0];
805
+ let inQuote = false;
806
+ let dot = -1;
807
+ for (let i = 0; i < tok.length; i++) {
808
+ const ch = tok[i];
809
+ if (ch === "\"") inQuote = !inQuote;
810
+ else if (ch === "." && !inQuote) {
811
+ dot = i;
812
+ break;
813
+ }
814
+ }
815
+ if (dot >= 0) {
816
+ const s = stripQuote(tok.slice(0, dot));
817
+ const t = stripQuote(tok.slice(dot + 1));
818
+ if (t.v.length === 0) return null;
819
+ return {
820
+ schema: s.quoted ? s.v : s.v.toLowerCase(),
821
+ table: t.quoted ? t.v : t.v.toLowerCase()
822
+ };
823
+ }
824
+ const t = stripQuote(tok);
825
+ if (t.v.length === 0) return null;
826
+ return {
827
+ schema: null,
828
+ table: t.quoted ? t.v : t.v.toLowerCase()
829
+ };
959
830
  };
960
- // ---------------------------------------------------------------------------
961
- // Entry point.
962
- // ---------------------------------------------------------------------------
963
831
  /**
964
- * Top-level rule dispatch.
965
- *
966
- * `prevWords` are the tokens BEFORE the current (in-progress) word; the
967
- * current word's leading characters are what we're filtering candidates
968
- * against.
969
- */
970
- export const findCompletions = async (prevWords, currentWord, ctx) => {
971
- // Re-read the connection on every call so `\c` is picked up immediately.
972
- const conn = ctx.settings.db ?? ctx.conn ?? null;
973
- // ----- Variable expansion (`:NAME`, `:'NAME'`, `:"NAME"`, `:{?NAME}`)
974
- // takes priority over anything else. The interpolation forms are valid
975
- // both inside SQL and inside backslash-command args (`\echo :VERB`),
976
- // so this branch fires regardless of `prevWords`.
977
- if (currentWord.startsWith(":") && !currentWord.startsWith("::")) {
978
- const names = listVarNames(ctx.settings);
979
- const lc = (s) => s.toLowerCase();
980
- // `:{?NAME}` — test-form (psqlscan_test_variable upstream). The
981
- // candidate must close the `}` so the user's literal `:{?VERB`
982
- // expands to `:{?VERBOSITY}` in one Tab.
983
- if (currentWord.startsWith(":{?")) {
984
- const prefix = currentWord.slice(3);
985
- const lp = lc(prefix);
986
- const cands = names
987
- .filter((n) => lc(n).startsWith(lp))
988
- .map((n) => ":{?" + n + "}");
989
- return { candidates: cands };
990
- }
991
- // `:'NAME'` / `:"NAME"` — quoted-substitution forms (psqlscan emits
992
- // a quoted literal / identifier). Close the matching quote so the
993
- // unique-match path appends a trailing space cleanly.
994
- if (currentWord.startsWith(":'")) {
995
- const prefix = currentWord.slice(2);
996
- const lp = lc(prefix);
997
- const cands = names
998
- .filter((n) => lc(n).startsWith(lp))
999
- .map((n) => ":'" + n + "'");
1000
- return { candidates: cands };
1001
- }
1002
- if (currentWord.startsWith(':"')) {
1003
- const prefix = currentWord.slice(2);
1004
- const lp = lc(prefix);
1005
- const cands = names
1006
- .filter((n) => lc(n).startsWith(lp))
1007
- .map((n) => ':"' + n + '"');
1008
- return { candidates: cands };
1009
- }
1010
- // Plain `:NAME` — bare substitution.
1011
- const prefix = currentWord.slice(1);
1012
- const lp = lc(prefix);
1013
- const filt = names
1014
- .filter((n) => lc(n).startsWith(lp))
1015
- .map((n) => ":" + n);
1016
- return { candidates: filt };
1017
- }
1018
- // ----- Backslash-command name completion.
1019
- // Trigger: the user is mid-token and the token starts with '\'.
1020
- if (currentWord.startsWith("\\") && prevWords.length === 0) {
1021
- return {
1022
- candidates: BACKSLASH_COMMANDS.filter((c) => c.toLowerCase().startsWith(currentWord.toLowerCase())),
1023
- };
1024
- }
1025
- // ----- Backslash-command argument completion.
1026
- if (prevWords.length > 0 && prevWords[0].startsWith("\\")) {
1027
- return await backslashArgRules(prevWords, currentWord, ctx, conn);
1028
- }
1029
- // ----- Multi-line SQL: rules that need to see across the line boundary
1030
- // re-tokenize `queryBuf` and consult the COMBINED token sequence. The
1031
- // standard single-line `prevWords` view stays untouched so the bulk of
1032
- // the rule grammar (which is happy to match on the current line alone)
1033
- // isn't disturbed.
1034
- //
1035
- // Upstream's `get_previous_words` pastes `tab_completion_query_buf` in
1036
- // front of `rl_line_buffer` with a `\n` separator and tokenizes the
1037
- // whole thing — see tab-complete.in.c ~line 6670. We mirror that, but
1038
- // gate the cross-line view to specific rules so the existing tail-match
1039
- // arms keep their familiar (line-local) semantics.
1040
- const combined = combinedPrevWords(ctx.queryBuf, prevWords);
1041
- const multiLine = await multiLineSqlRules(combined, prevWords, currentWord, ctx, conn);
1042
- if (multiLine !== null)
1043
- return multiLine;
1044
- // ----- SQL: top-of-statement keyword completion.
1045
- if (prevWords.length === 0) {
1046
- return {
1047
- candidates: filterAndCase(SQL_TOP_KEYWORDS, currentWord, ctx.settings),
1048
- };
1049
- }
1050
- return await sqlRules(prevWords, currentWord, ctx, conn);
832
+ * Top-level rule dispatch.
833
+ *
834
+ * `prevWords` are the tokens BEFORE the current (in-progress) word; the
835
+ * current word's leading characters are what we're filtering candidates
836
+ * against.
837
+ */
838
+ const findCompletions = async (prevWords, currentWord, ctx) => {
839
+ const conn = ctx.settings.db ?? ctx.conn ?? null;
840
+ if (currentWord.startsWith(":") && !currentWord.startsWith("::")) {
841
+ const names = listVarNames(ctx.settings);
842
+ const lc = (s) => s.toLowerCase();
843
+ if (currentWord.startsWith(":{?")) {
844
+ const lp = lc(currentWord.slice(3));
845
+ return { candidates: names.filter((n) => lc(n).startsWith(lp)).map((n) => ":{?" + n + "}") };
846
+ }
847
+ if (currentWord.startsWith(":'")) {
848
+ const lp = lc(currentWord.slice(2));
849
+ return { candidates: names.filter((n) => lc(n).startsWith(lp)).map((n) => ":'" + n + "'") };
850
+ }
851
+ if (currentWord.startsWith(":\"")) {
852
+ const lp = lc(currentWord.slice(2));
853
+ return { candidates: names.filter((n) => lc(n).startsWith(lp)).map((n) => ":\"" + n + "\"") };
854
+ }
855
+ const lp = lc(currentWord.slice(1));
856
+ return { candidates: names.filter((n) => lc(n).startsWith(lp)).map((n) => ":" + n) };
857
+ }
858
+ if (currentWord.startsWith("\\") && prevWords.length === 0) return { candidates: BACKSLASH_COMMANDS.filter((c) => c.toLowerCase().startsWith(currentWord.toLowerCase())) };
859
+ if (prevWords.length > 0 && prevWords[0].startsWith("\\")) return await backslashArgRules(prevWords, currentWord, ctx, conn);
860
+ const combined = combinedPrevWords(ctx.queryBuf, prevWords);
861
+ const multiLine = await multiLineSqlRules(combined, prevWords, currentWord, ctx, conn);
862
+ if (multiLine !== null) return multiLine;
863
+ if (prevWords.length === 0) return { candidates: filterAndCase(SQL_TOP_KEYWORDS, currentWord, ctx.settings) };
864
+ return await sqlRules(prevWords, currentWord, ctx, conn);
1051
865
  };
1052
866
  /**
1053
- * Tokenize `queryBuf` and concatenate with the current line's `prevWords`.
1054
- * The result matches what upstream's `get_previous_words` produces for a
1055
- * multi-line statement: tokens from prior lines first, then tokens from the
1056
- * current line up to the cursor.
1057
- *
1058
- * Tokens cross the line boundary harmlessly because our tokenizer treats
1059
- * `\n` as whitespace (a separator). Newlines INSIDE a `'...'`/`"..."`
1060
- * literal are absorbed by the quoted-string handling and don't split the
1061
- * literal into two tokens.
1062
- */
867
+ * Tokenize `queryBuf` and concatenate with the current line's `prevWords`.
868
+ * The result matches what upstream's `get_previous_words` produces for a
869
+ * multi-line statement: tokens from prior lines first, then tokens from the
870
+ * current line up to the cursor.
871
+ *
872
+ * Tokens cross the line boundary harmlessly because our tokenizer treats
873
+ * `\n` as whitespace (a separator). Newlines INSIDE a `'...'`/`"..."`
874
+ * literal are absorbed by the quoted-string handling and don't split the
875
+ * literal into two tokens.
876
+ */
1063
877
  const combinedPrevWords = (queryBuf, prevWords) => {
1064
- if (queryBuf === undefined || queryBuf.length === 0)
1065
- return prevWords;
1066
- const bufTokens = tokenize(queryBuf).map((t) => t.text);
1067
- return [...bufTokens, ...prevWords];
878
+ if (queryBuf === void 0 || queryBuf.length === 0) return prevWords;
879
+ return [...tokenize(queryBuf).map((t) => t.text), ...prevWords];
1068
880
  };
1069
881
  /**
1070
- * Rules that fire only when the user is in the middle of a multi-line
1071
- * statement (`queryBuf` non-empty / the combined-token view is longer than
1072
- * the current-line view). Today this covers:
1073
- *
1074
- * - `ANALYZE (` opened on a previous line — emit the option list.
1075
- * - `COMMENT ON CONSTRAINT <name> ON <schema>.` continuation —
1076
- * resolve to the table holding that constraint in `<schema>`.
1077
- *
1078
- * Returns the rule's `RuleResult` when an arm matches, or `null` when the
1079
- * combined-token view doesn't add anything (so the caller continues with
1080
- * the line-local rule grammar).
1081
- */
882
+ * Rules that fire only when the user is in the middle of a multi-line
883
+ * statement (`queryBuf` non-empty / the combined-token view is longer than
884
+ * the current-line view). Today this covers:
885
+ *
886
+ * - `ANALYZE (` opened on a previous line — emit the option list.
887
+ * - `COMMENT ON CONSTRAINT <name> ON <schema>.` continuation —
888
+ * resolve to the table holding that constraint in `<schema>`.
889
+ *
890
+ * Returns the rule's `RuleResult` when an arm matches, or `null` when the
891
+ * combined-token view doesn't add anything (so the caller continues with
892
+ * the line-local rule grammar).
893
+ */
1082
894
  const multiLineSqlRules = async (combined, prevWords, currentWord, ctx, conn) => {
1083
- // Skip if there's no cross-line context to add.
1084
- if (combined.length === prevWords.length)
1085
- return null;
1086
- // ----- ANALYZE ( <prefix> — option list inside the parenthesized form.
1087
- // Mirrors upstream:
1088
- //
1089
- // else if (HeadMatches("ANALYZE", "(*") &&
1090
- // !HeadMatches("ANALYZE", "(*)"))
1091
- // {
1092
- // if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
1093
- // COMPLETE_WITH("VERBOSE", "SKIP_LOCKED", "BUFFER_USAGE_LIMIT");
1094
- // else if (TailMatches("VERBOSE|SKIP_LOCKED"))
1095
- // COMPLETE_WITH("ON", "OFF");
1096
- // }
1097
- //
1098
- // Our scanner splits `(` and `,` as their own tokens (the C tokenizer
1099
- // keeps `(verbose` as one word), so `ends_with(prev_wd, '(')` becomes
1100
- // "previous token IS `(`" and similarly for `,`.
1101
- if (HeadMatches(combined, ["ANALYZE"]) &&
1102
- isInsideOpenParen(combined.slice(1))) {
1103
- const lastTok = combined[combined.length - 1];
1104
- if (lastTok === "(" || lastTok === ",") {
1105
- return {
1106
- candidates: filterAndCase(["VERBOSE", "SKIP_LOCKED", "BUFFER_USAGE_LIMIT"], currentWord, ctx.settings),
1107
- };
1108
- }
1109
- // Inside the option list with a partial option name in the current
1110
- // word — filter the option list by the prefix.
1111
- if (currentWord.length > 0 &&
1112
- (lastTok === undefined || /^[A-Za-z_]+$/.test(currentWord))) {
1113
- return {
1114
- candidates: filterAndCase(["VERBOSE", "SKIP_LOCKED", "BUFFER_USAGE_LIMIT"], currentWord, ctx.settings),
1115
- };
1116
- }
1117
- // `VERBOSE` / `SKIP_LOCKED` boolean continuation — ON / OFF.
1118
- if (TailMatches(combined, ["VERBOSE|SKIP_LOCKED"])) {
1119
- return {
1120
- candidates: filterAndCase(["ON", "OFF"], currentWord, ctx.settings),
1121
- };
1122
- }
1123
- }
1124
- // ----- COMMENT ON CONSTRAINT <name> ON <schema>.<prefix> — resolve to the
1125
- // table that has `<name>` as a constraint within `<schema>`. Upstream
1126
- // calls `set_completion_reference(prev2_wd)` (the constraint name) and
1127
- // runs `Query_for_list_of_tables_for_constraint` with the schema match
1128
- // baked into the SchemaQuery shape (tab-complete.in.c ~line 3204).
1129
- //
1130
- // We allow the rule to fire even when the line-local `prevWords` only
1131
- // sees the trailing `ON` — the COMBINED tokens carry the
1132
- // `COMMENT ON CONSTRAINT <name>` prefix from the previous line.
1133
- if (TailMatches(combined, ["COMMENT", "ON", "CONSTRAINT", MatchAny, "ON"])) {
1134
- if (!conn)
1135
- return { candidates: [] };
1136
- // `combined` ends in `ON`. The constraint name is the token just
1137
- // before that trailing `ON`. Indexing from the right avoids hard-coding
1138
- // the queryBuf token count.
1139
- const constraintNameRaw = combined[combined.length - 2];
1140
- const constraintName = stripIdentifierQuote(constraintNameRaw);
1141
- const split = splitSchemaPrefix(currentWord);
1142
- if (split) {
1143
- // Schema-qualified: emit tables in `<schema>` that have the named
1144
- // constraint, with the canonical schema prefix re-attached so the
1145
- // line ends up looking like `public.tab1`.
1146
- const rows = await runCatalogQuery(conn, Query_for_list_of_tables_for_constraint_in_schema, split.prefix, [constraintName, split.schema]);
1147
- const canonicalSchema = split.schemaWasQuoted
1148
- ? split.schema
1149
- : split.schema.toLowerCase();
1150
- return {
1151
- candidates: rows.map((r) => canonicalSchema + "." + r),
1152
- };
1153
- }
1154
- // Unqualified: list tables that have the constraint, plus the schemas
1155
- // they live in (with trailing `.` so the user can drill into a schema).
1156
- const [tables, schemas] = await Promise.all([
1157
- runCatalogQuery(conn, Query_for_list_of_tables_for_constraint, currentWord, [constraintName]),
1158
- runCatalogQuery(conn, Query_for_list_of_schemas, currentWord),
1159
- ]);
1160
- return { candidates: [...tables, ...schemas.map((s) => s + ".")] };
1161
- }
1162
- void conn;
1163
- return null;
895
+ if (combined.length === prevWords.length) return null;
896
+ if (HeadMatches(combined, ["ANALYZE"]) && isInsideOpenParen(combined.slice(1))) {
897
+ const lastTok = combined[combined.length - 1];
898
+ if (lastTok === "(" || lastTok === ",") return { candidates: filterAndCase([
899
+ "VERBOSE",
900
+ "SKIP_LOCKED",
901
+ "BUFFER_USAGE_LIMIT"
902
+ ], currentWord, ctx.settings) };
903
+ if (currentWord.length > 0 && (lastTok === void 0 || /^[A-Za-z_]+$/.test(currentWord))) return { candidates: filterAndCase([
904
+ "VERBOSE",
905
+ "SKIP_LOCKED",
906
+ "BUFFER_USAGE_LIMIT"
907
+ ], currentWord, ctx.settings) };
908
+ if (TailMatches(combined, ["VERBOSE|SKIP_LOCKED"])) return { candidates: filterAndCase(["ON", "OFF"], currentWord, ctx.settings) };
909
+ }
910
+ if (TailMatches(combined, [
911
+ "COMMENT",
912
+ "ON",
913
+ "CONSTRAINT",
914
+ null,
915
+ "ON"
916
+ ])) {
917
+ if (!conn) return { candidates: [] };
918
+ const constraintNameRaw = combined[combined.length - 2];
919
+ const constraintName = stripIdentifierQuote(constraintNameRaw);
920
+ const split = splitSchemaPrefix(currentWord);
921
+ if (split) {
922
+ const rows = await runCatalogQuery(conn, Query_for_list_of_tables_for_constraint_in_schema, split.prefix, [constraintName, split.schema]);
923
+ const canonicalSchema = split.schemaWasQuoted ? split.schema : split.schema.toLowerCase();
924
+ return { candidates: rows.map((r) => canonicalSchema + "." + r) };
925
+ }
926
+ const [tables, schemas] = await Promise.all([runCatalogQuery(conn, Query_for_list_of_tables_for_constraint, currentWord, [constraintName]), runCatalogQuery(conn, Query_for_list_of_schemas, currentWord)]);
927
+ return { candidates: [...tables, ...schemas.map((s) => s + ".")] };
928
+ }
929
+ return null;
1164
930
  };
1165
931
  /**
1166
- * Return true when the tokens (after dropping a leading `ANALYZE` keyword)
1167
- * are inside an unclosed parenthesized form — i.e. there's a `(` somewhere
1168
- * and no matching `)` at the trailing position. Mirrors upstream's
1169
- * `HeadMatches("ANALYZE", "(*") && !HeadMatches("ANALYZE", "(*)")`
1170
- * pattern under our split-punctuation tokenizer.
1171
- */
932
+ * Return true when the tokens (after dropping a leading `ANALYZE` keyword)
933
+ * are inside an unclosed parenthesized form — i.e. there's a `(` somewhere
934
+ * and no matching `)` at the trailing position. Mirrors upstream's
935
+ * `HeadMatches("ANALYZE", "(*") && !HeadMatches("ANALYZE", "(*)")`
936
+ * pattern under our split-punctuation tokenizer.
937
+ */
1172
938
  const isInsideOpenParen = (tokens) => {
1173
- let depth = 0;
1174
- let sawOpen = false;
1175
- for (const t of tokens) {
1176
- if (t === "(") {
1177
- depth++;
1178
- sawOpen = true;
1179
- }
1180
- else if (t === ")") {
1181
- depth--;
1182
- }
1183
- }
1184
- return sawOpen && depth > 0;
939
+ let depth = 0;
940
+ let sawOpen = false;
941
+ for (const t of tokens) if (t === "(") {
942
+ depth++;
943
+ sawOpen = true;
944
+ } else if (t === ")") depth--;
945
+ return sawOpen && depth > 0;
1185
946
  };
1186
947
  /** Strip surrounding `"..."` quoting from a constraint/identifier token. */
1187
948
  const stripIdentifierQuote = (raw) => {
1188
- if (raw === undefined)
1189
- return "";
1190
- if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"')) {
1191
- return raw.slice(1, -1).replace(/""/g, '"');
1192
- }
1193
- return raw;
949
+ if (raw === void 0) return "";
950
+ if (raw.length >= 2 && raw.startsWith("\"") && raw.endsWith("\"")) return raw.slice(1, -1).replace(/""/g, "\"");
951
+ return raw;
1194
952
  };
1195
- // ---------------------------------------------------------------------------
1196
- // Backslash arg rules.
1197
- // ---------------------------------------------------------------------------
1198
953
  const backslashArgRules = async (prevWords, currentWord, ctx, conn) => {
1199
- const cmd = prevWords[0]; // e.g. '\dt'
1200
- // \c [DBNAME], \connect [DBNAME]: complete database names.
1201
- if (cmd === "\\c" || cmd === "\\connect") {
1202
- if (prevWords.length === 1 && conn) {
1203
- const rows = await runCatalogQuery(conn, Query_for_list_of_databases, currentWord);
1204
- return { candidates: rows };
1205
- }
1206
- return { candidates: [] };
1207
- }
1208
- // \dn[+] [schema]
1209
- if (cmd === "\\dn" || cmd === "\\dn+") {
1210
- if (prevWords.length === 1 && conn) {
1211
- const rows = await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord);
1212
- return { candidates: rows };
1213
- }
1214
- return { candidates: [] };
1215
- }
1216
- // \df, \dfa, \dfn, \dfp, \dft, \dfw, \ef, \sf
1217
- if (cmd === "\\df" ||
1218
- cmd === "\\df+" ||
1219
- cmd === "\\dfa" ||
1220
- cmd === "\\dfn" ||
1221
- cmd === "\\dfp" ||
1222
- cmd === "\\dft" ||
1223
- cmd === "\\dfw" ||
1224
- cmd === "\\ef" ||
1225
- cmd === "\\sf" ||
1226
- cmd === "\\sf+") {
1227
- if (prevWords.length === 1 && conn) {
1228
- const rows = await runCatalogQuery(conn, Query_for_list_of_functions, currentWord);
1229
- return { candidates: rows };
1230
- }
1231
- return { candidates: [] };
1232
- }
1233
- // \du, \dg → roles.
1234
- if (cmd === "\\du" ||
1235
- cmd === "\\dg" ||
1236
- cmd === "\\du+" ||
1237
- cmd === "\\dg+") {
1238
- if (prevWords.length === 1 && conn) {
1239
- const rows = await runCatalogQuery(conn, Query_for_list_of_roles, currentWord);
1240
- return { candidates: rows };
1241
- }
1242
- return { candidates: [] };
1243
- }
1244
- // \dx → extensions.
1245
- if (cmd === "\\dx" || cmd === "\\dx+") {
1246
- if (prevWords.length === 1 && conn) {
1247
- const rows = await runCatalogQuery(conn, Query_for_list_of_extensions, currentWord);
1248
- return { candidates: rows };
1249
- }
1250
- return { candidates: [] };
1251
- }
1252
- // \dL → languages.
1253
- if (cmd === "\\dL" || cmd === "\\dL+") {
1254
- if (prevWords.length === 1 && conn) {
1255
- const rows = await runCatalogQuery(conn, Query_for_list_of_languages, currentWord);
1256
- return { candidates: rows };
1257
- }
1258
- return { candidates: [] };
1259
- }
1260
- // \dT → types.
1261
- if (cmd === "\\dT" || cmd === "\\dT+") {
1262
- if (prevWords.length === 1 && conn) {
1263
- const rows = await runCatalogQuery(conn, Query_for_list_of_types, currentWord);
1264
- return { candidates: rows };
1265
- }
1266
- return { candidates: [] };
1267
- }
1268
- // \do → operators.
1269
- if (cmd === "\\do" ||
1270
- cmd === "\\do+" ||
1271
- cmd === "\\doS" ||
1272
- cmd === "\\doS+") {
1273
- if (prevWords.length === 1 && conn) {
1274
- const rows = await runCatalogQuery(conn, Query_for_list_of_operators, currentWord);
1275
- return { candidates: rows };
1276
- }
1277
- return { candidates: [] };
1278
- }
1279
- // \dC → casts (free-form pattern of "src AS tgt").
1280
- if (cmd === "\\dC" || cmd === "\\dC+") {
1281
- if (prevWords.length === 1 && conn) {
1282
- const rows = await runCatalogQuery(conn, Query_for_list_of_casts, currentWord);
1283
- return { candidates: rows };
1284
- }
1285
- return { candidates: [] };
1286
- }
1287
- // \dt / \dtv / \d / \dv / \dm / \di / \ds → relations of various kinds.
1288
- if (cmd === "\\dt" ||
1289
- cmd === "\\dt+" ||
1290
- cmd === "\\dtv" ||
1291
- cmd === "\\d" ||
1292
- cmd === "\\d+" ||
1293
- cmd === "\\dE" ||
1294
- cmd === "\\dE+") {
1295
- if (prevWords.length === 1 && conn) {
1296
- return {
1297
- candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_tables),
1298
- };
1299
- }
1300
- return { candidates: [] };
1301
- }
1302
- if (cmd === "\\dv" || cmd === "\\dv+") {
1303
- if (prevWords.length === 1 && conn) {
1304
- return {
1305
- candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_views),
1306
- };
1307
- }
1308
- return { candidates: [] };
1309
- }
1310
- if (cmd === "\\dm" || cmd === "\\dm+") {
1311
- if (prevWords.length === 1 && conn) {
1312
- return {
1313
- candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_matviews),
1314
- };
1315
- }
1316
- return { candidates: [] };
1317
- }
1318
- if (cmd === "\\di" || cmd === "\\di+") {
1319
- if (prevWords.length === 1 && conn) {
1320
- return {
1321
- candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_indexes),
1322
- };
1323
- }
1324
- return { candidates: [] };
1325
- }
1326
- if (cmd === "\\ds" || cmd === "\\ds+") {
1327
- if (prevWords.length === 1 && conn) {
1328
- return {
1329
- candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_sequences),
1330
- };
1331
- }
1332
- return { candidates: [] };
1333
- }
1334
- // \l[+] / \list → databases.
1335
- if (cmd === "\\l" ||
1336
- cmd === "\\l+" ||
1337
- cmd === "\\list" ||
1338
- cmd === "\\list+") {
1339
- if (prevWords.length === 1 && conn) {
1340
- const rows = await runCatalogQuery(conn, Query_for_list_of_databases, currentWord);
1341
- return { candidates: rows };
1342
- }
1343
- return { candidates: [] };
1344
- }
1345
- // \encoding NAME
1346
- if (cmd === "\\encoding") {
1347
- if (prevWords.length === 1) {
1348
- return { candidates: filterCi(ENCODINGS, currentWord) };
1349
- }
1350
- return { candidates: [] };
1351
- }
1352
- // \pset OPT [value]
1353
- if (cmd === "\\pset") {
1354
- if (prevWords.length === 1) {
1355
- return { candidates: filterCi(PSET_OPTIONS, currentWord) };
1356
- }
1357
- if (prevWords.length === 2) {
1358
- const values = psetValuesFor(prevWords[1].toLowerCase());
1359
- if (values)
1360
- return { candidates: filterCi(values, currentWord) };
1361
- return { candidates: [] };
1362
- }
1363
- return { candidates: [] };
1364
- }
1365
- // \set NAME [VALUE]
1366
- if (cmd === "\\set") {
1367
- if (prevWords.length === 1) {
1368
- return {
1369
- candidates: filterCi(listAllVarNames(ctx.settings), currentWord),
1370
- };
1371
- }
1372
- if (prevWords.length === 2) {
1373
- const values = variableValuesFor(prevWords[1].toUpperCase());
1374
- if (values)
1375
- return { candidates: filterCi(values, currentWord) };
1376
- return { candidates: [] };
1377
- }
1378
- return { candidates: [] };
1379
- }
1380
- // \unset NAME — only existing variables.
1381
- if (cmd === "\\unset") {
1382
- if (prevWords.length === 1) {
1383
- return {
1384
- candidates: filterCi(listVarNames(ctx.settings), currentWord),
1385
- };
1386
- }
1387
- return { candidates: [] };
1388
- }
1389
- // \echo / \warn / \qecho — variable expansion only (handled above).
1390
- // \prompt — variable name argument is the 1st positional.
1391
- if (cmd === "\\prompt") {
1392
- if (prevWords.length === 2) {
1393
- return {
1394
- candidates: filterCi(listAllVarNames(ctx.settings), currentWord),
1395
- };
1396
- }
1397
- return { candidates: [] };
1398
- }
1399
- // \lo_import / \lo_export → filesystem-driven filename completion.
1400
- // psql parses the path as a backslash-argument literal — no SQL string
1401
- // quoting required, so we use the unquoted candidate form.
1402
- if (cmd === "\\lo_import" || cmd === "\\lo_export") {
1403
- if (prevWords.length === 1) {
1404
- return { candidates: completeFilenames(currentWord, "none") };
1405
- }
1406
- return { candidates: [] };
1407
- }
1408
- // \copy <table> [FROM|TO] <path> — once the FROM/TO keyword has been
1409
- // typed, the next token is a filename (backslash-context, so bare paths
1410
- // are fine).
1411
- if (cmd === "\\copy") {
1412
- // First arg: a table name.
1413
- if (prevWords.length === 1 && conn) {
1414
- return {
1415
- candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_tables),
1416
- };
1417
- }
1418
- // Second arg: the FROM/TO keyword.
1419
- if (prevWords.length === 2) {
1420
- return {
1421
- candidates: filterAndCase(["FROM", "TO"], currentWord, ctx.settings),
1422
- };
1423
- }
1424
- // Third arg (after FROM/TO): the filename.
1425
- if (prevWords.length >= 3) {
1426
- const last = prevWords[prevWords.length - 1].toUpperCase();
1427
- if (last === "FROM" || last === "TO") {
1428
- return { candidates: completeFilenames(currentWord, "none") };
1429
- }
1430
- }
1431
- return { candidates: [] };
1432
- }
1433
- // \drds, \drg → roles (for the first arg).
1434
- if (cmd === "\\drds" || cmd === "\\drg") {
1435
- if (prevWords.length === 1 && conn) {
1436
- const rows = await runCatalogQuery(conn, Query_for_list_of_roles, currentWord);
1437
- return { candidates: rows };
1438
- }
1439
- return { candidates: [] };
1440
- }
1441
- // Anything else: no completion.
1442
- return { candidates: [] };
954
+ const cmd = prevWords[0];
955
+ if (cmd === "\\c" || cmd === "\\connect") {
956
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord) };
957
+ return { candidates: [] };
958
+ }
959
+ if (cmd === "\\dn" || cmd === "\\dn+") {
960
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord) };
961
+ return { candidates: [] };
962
+ }
963
+ if (cmd === "\\df" || cmd === "\\df+" || cmd === "\\dfa" || cmd === "\\dfn" || cmd === "\\dfp" || cmd === "\\dft" || cmd === "\\dfw" || cmd === "\\ef" || cmd === "\\sf" || cmd === "\\sf+") {
964
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord) };
965
+ return { candidates: [] };
966
+ }
967
+ if (cmd === "\\du" || cmd === "\\dg" || cmd === "\\du+" || cmd === "\\dg+") {
968
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
969
+ return { candidates: [] };
970
+ }
971
+ if (cmd === "\\dx" || cmd === "\\dx+") {
972
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_extensions, currentWord) };
973
+ return { candidates: [] };
974
+ }
975
+ if (cmd === "\\dL" || cmd === "\\dL+") {
976
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_languages, currentWord) };
977
+ return { candidates: [] };
978
+ }
979
+ if (cmd === "\\dT" || cmd === "\\dT+") {
980
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_types, currentWord) };
981
+ return { candidates: [] };
982
+ }
983
+ if (cmd === "\\do" || cmd === "\\do+" || cmd === "\\doS" || cmd === "\\doS+") {
984
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_operators, currentWord) };
985
+ return { candidates: [] };
986
+ }
987
+ if (cmd === "\\dC" || cmd === "\\dC+") {
988
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_casts, currentWord) };
989
+ return { candidates: [] };
990
+ }
991
+ if (cmd === "\\dt" || cmd === "\\dt+" || cmd === "\\dtv" || cmd === "\\d" || cmd === "\\d+" || cmd === "\\dE" || cmd === "\\dE+") {
992
+ if (prevWords.length === 1 && conn) return { candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_tables) };
993
+ return { candidates: [] };
994
+ }
995
+ if (cmd === "\\dv" || cmd === "\\dv+") {
996
+ if (prevWords.length === 1 && conn) return { candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_views) };
997
+ return { candidates: [] };
998
+ }
999
+ if (cmd === "\\dm" || cmd === "\\dm+") {
1000
+ if (prevWords.length === 1 && conn) return { candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_matviews) };
1001
+ return { candidates: [] };
1002
+ }
1003
+ if (cmd === "\\di" || cmd === "\\di+") {
1004
+ if (prevWords.length === 1 && conn) return { candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_indexes) };
1005
+ return { candidates: [] };
1006
+ }
1007
+ if (cmd === "\\ds" || cmd === "\\ds+") {
1008
+ if (prevWords.length === 1 && conn) return { candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_sequences) };
1009
+ return { candidates: [] };
1010
+ }
1011
+ if (cmd === "\\l" || cmd === "\\l+" || cmd === "\\list" || cmd === "\\list+") {
1012
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord) };
1013
+ return { candidates: [] };
1014
+ }
1015
+ if (cmd === "\\encoding") {
1016
+ if (prevWords.length === 1) return { candidates: filterCi(ENCODINGS, currentWord) };
1017
+ return { candidates: [] };
1018
+ }
1019
+ if (cmd === "\\pset") {
1020
+ if (prevWords.length === 1) return { candidates: filterCi(PSET_OPTIONS, currentWord) };
1021
+ if (prevWords.length === 2) {
1022
+ const values = psetValuesFor(prevWords[1].toLowerCase());
1023
+ if (values) return { candidates: filterCi(values, currentWord) };
1024
+ return { candidates: [] };
1025
+ }
1026
+ return { candidates: [] };
1027
+ }
1028
+ if (cmd === "\\set") {
1029
+ if (prevWords.length === 1) return { candidates: filterCi(listAllVarNames(ctx.settings), currentWord) };
1030
+ if (prevWords.length === 2) {
1031
+ const values = variableValuesFor(prevWords[1].toUpperCase());
1032
+ if (values) return { candidates: filterCi(values, currentWord) };
1033
+ return { candidates: [] };
1034
+ }
1035
+ return { candidates: [] };
1036
+ }
1037
+ if (cmd === "\\unset") {
1038
+ if (prevWords.length === 1) return { candidates: filterCi(listVarNames(ctx.settings), currentWord) };
1039
+ return { candidates: [] };
1040
+ }
1041
+ if (cmd === "\\prompt") {
1042
+ if (prevWords.length === 2) return { candidates: filterCi(listAllVarNames(ctx.settings), currentWord) };
1043
+ return { candidates: [] };
1044
+ }
1045
+ if (cmd === "\\lo_import" || cmd === "\\lo_export") {
1046
+ if (prevWords.length === 1) return { candidates: completeFilenames(currentWord, "none") };
1047
+ return { candidates: [] };
1048
+ }
1049
+ if (cmd === "\\copy") {
1050
+ if (prevWords.length === 1 && conn) return { candidates: await completeSchemaOrRelations(conn, currentWord, Query_for_list_of_tables) };
1051
+ if (prevWords.length === 2) return { candidates: filterAndCase(["FROM", "TO"], currentWord, ctx.settings) };
1052
+ if (prevWords.length >= 3) {
1053
+ const last = prevWords[prevWords.length - 1].toUpperCase();
1054
+ if (last === "FROM" || last === "TO") return { candidates: completeFilenames(currentWord, "none") };
1055
+ }
1056
+ return { candidates: [] };
1057
+ }
1058
+ if (cmd === "\\drds" || cmd === "\\drg") {
1059
+ if (prevWords.length === 1 && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
1060
+ return { candidates: [] };
1061
+ }
1062
+ return { candidates: [] };
1443
1063
  };
1444
- // ---------------------------------------------------------------------------
1445
- // SQL rules.
1446
- // ---------------------------------------------------------------------------
1447
1064
  const sqlRules = async (prevWords, currentWord, ctx, conn) => {
1448
- // Convenience: most rules want to fall back to a "tables" lookup. We
1449
- // factor that into a single helper.
1450
- const completeTables = async (query = Query_for_list_of_tables) => {
1451
- if (!conn)
1452
- return { candidates: [] };
1453
- return {
1454
- candidates: await completeSchemaOrRelations(conn, currentWord, query),
1455
- };
1456
- };
1457
- // COPY <table> [FROM|TO] <'path'> — filename completion in the
1458
- // string-literal context. MUST come before the generic `FROM <prefix>`
1459
- // rule below, which would otherwise treat the path as a table name.
1460
- if (isCopyFromOrTo(prevWords)) {
1461
- return { candidates: completeFilenames(currentWord, "sql") };
1462
- }
1463
- // FROM <prefix>: tables/views/matviews. EXCLUDE `REVOKE … FROM <role>`,
1464
- // which must complete role names — otherwise this generic rule shadows the
1465
- // REVOKE-roles arm below, making it dead code (review item #26).
1466
- if (TailMatches(prevWords, ["FROM"]) &&
1467
- !HeadMatches(prevWords, ["REVOKE"])) {
1468
- return completeTables(Query_for_list_of_tables_views);
1469
- }
1470
- // After FROM x, suggest JOIN/WHERE/etc — handled below.
1471
- // UPDATE <prefix>: tables.
1472
- if (TailMatches(prevWords, ["UPDATE"]))
1473
- return completeTables();
1474
- // DELETE FROM <prefix>: tables.
1475
- if (TailMatches(prevWords, ["DELETE", "FROM"]))
1476
- return completeTables();
1477
- // INSERT INTO <prefix>: tables. The tokenizer might give us
1478
- // ['INSERT'] [INTO] or ['INSERT', 'INTO'] depending on whether the user
1479
- // typed an extra space.
1480
- if (TailMatches(prevWords, ["INTO"]))
1481
- return completeTables();
1482
- // JOIN <prefix>: tables. Most common: SELECT … FROM x JOIN <prefix>.
1483
- if (TailMatches(prevWords, ["JOIN"])) {
1484
- return completeTables(Query_for_list_of_tables_views);
1485
- }
1486
- // After JOIN x, suggest ON or USING.
1487
- if (TailMatches(prevWords, ["JOIN", MatchAny])) {
1488
- return {
1489
- candidates: filterAndCase(["ON", "USING"], currentWord, ctx.settings),
1490
- };
1491
- }
1492
- // After `FROM table_name <TAB>` (or `FROM table AS alias <TAB>`) — offer
1493
- // the post-FROM tail keywords. Only fire when the statement starts with
1494
- // SELECT/INSERT/UPDATE/DELETE (i.e. a SELECT-list-friendly context), so
1495
- // we don't trample more specific rules like `ALTER TABLE x` or
1496
- // `INSERT INTO x` which look "FROM-like" structurally.
1497
- const inSelectContext = HeadMatches(prevWords, ["SELECT"]) ||
1498
- HeadMatches(prevWords, ["INSERT"]) ||
1499
- HeadMatches(prevWords, ["UPDATE"]) ||
1500
- HeadMatches(prevWords, ["DELETE"]) ||
1501
- HeadMatches(prevWords, ["WITH"]) ||
1502
- HeadMatches(prevWords, ["EXPLAIN"]);
1503
- if (inSelectContext &&
1504
- (TailMatches(prevWords, ["FROM", MatchAny]) ||
1505
- TailMatches(prevWords, ["FROM", MatchAny, MatchAny]))) {
1506
- // `FROM x` or `FROM x alias` → post-FROM continuations.
1507
- return {
1508
- candidates: filterAndCase(POST_FROM_KEYWORDS, currentWord, ctx.settings),
1509
- };
1510
- }
1511
- // Window-function frame clause: `OVER (` then `<TAB>`. The tokenizer
1512
- // emits the `(` as its own token, so prevWords ends with '('.
1513
- if (TailMatches(prevWords, ["OVER", "("])) {
1514
- return {
1515
- candidates: filterAndCase(WINDOW_FRAME_KEYWORDS, currentWord, ctx.settings),
1516
- };
1517
- }
1518
- // `OVER <name>` (named window reference) is free-form; no completion.
1519
- // After a WHERE clause has been started (any `WHERE … <expr> <TAB>`),
1520
- // suggest boolean continuations. We trigger on the simple case
1521
- // `WHERE <ident>` and `WHERE … AND/OR <ident>`.
1522
- if (inSelectContext &&
1523
- TailMatches(prevWords, ["WHERE", MatchAny]) &&
1524
- !TailMatches(prevWords, ["WHERE"])) {
1525
- return {
1526
- candidates: filterAndCase(WHERE_CONTINUATIONS, currentWord, ctx.settings),
1527
- };
1528
- }
1529
- // ---- ALTER TABLE block ----
1530
- // Deep ALTER TABLE sub-action continuations must be checked BEFORE the
1531
- // 3-token fallback `ALTER TABLE x` (which lists generic sub-actions).
1532
- // The deeper rules use HeadMatches so they survive an arbitrary trailing
1533
- // option list like `ALTER TABLE foo ADD CONSTRAINT bar CHECK (...)`.
1534
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1535
- TailMatches(prevWords, ["ADD"])) {
1536
- return {
1537
- candidates: filterAndCase(ALTER_TABLE_ADD, currentWord, ctx.settings),
1538
- };
1539
- }
1540
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1541
- TailMatches(prevWords, ["DROP"])) {
1542
- return {
1543
- candidates: filterAndCase(ALTER_TABLE_DROP, currentWord, ctx.settings),
1544
- };
1545
- }
1546
- // `ALTER TABLE <ref> DROP CONSTRAINT <prefix>` — constraint names on
1547
- // the referenced table. Mirrors upstream tab-complete.in.c ~line 1280
1548
- // (`COMPLETE_WITH_QUERY(Query_for_constraint_of_table)`).
1549
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1550
- TailMatches(prevWords, ["DROP", "CONSTRAINT"])) {
1551
- if (!conn)
1552
- return { candidates: [] };
1553
- const refTokens = prevWords.slice(2, prevWords.length - 2);
1554
- const ref = parseTableRef(refTokens);
1555
- if (!ref)
1556
- return { candidates: [] };
1557
- const cands = ref.schema === null
1558
- ? await runCatalogQuery(conn, Query_for_constraint_of_table, currentWord, [ref.table])
1559
- : await runCatalogQuery(conn, Query_for_constraint_of_table_in_schema, currentWord, [ref.schema, ref.table]);
1560
- return { candidates: cands };
1561
- }
1562
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1563
- TailMatches(prevWords, ["RENAME"])) {
1564
- return {
1565
- candidates: filterAndCase(ALTER_TABLE_RENAME, currentWord, ctx.settings),
1566
- };
1567
- }
1568
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1569
- (TailMatches(prevWords, ["ALTER"]) ||
1570
- TailMatches(prevWords, ["ALTER", "COLUMN", MatchAny]))) {
1571
- return {
1572
- candidates: filterAndCase(ALTER_TABLE_ALTER_COLUMN, currentWord, ctx.settings),
1573
- };
1574
- }
1575
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1576
- TailMatches(prevWords, ["SET"])) {
1577
- return {
1578
- candidates: filterAndCase(ALTER_TABLE_SET, currentWord, ctx.settings),
1579
- };
1580
- }
1581
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1582
- TailMatches(prevWords, ["ENABLE"])) {
1583
- return {
1584
- candidates: filterAndCase(ALTER_TABLE_ENABLE, currentWord, ctx.settings),
1585
- };
1586
- }
1587
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1588
- TailMatches(prevWords, ["DISABLE"])) {
1589
- return {
1590
- candidates: filterAndCase(ALTER_TABLE_DISABLE, currentWord, ctx.settings),
1591
- };
1592
- }
1593
- if (HeadMatches(prevWords, ["ALTER", "TABLE"]) &&
1594
- TailMatches(prevWords, ["REPLICA", "IDENTITY"])) {
1595
- return {
1596
- candidates: filterAndCase(ALTER_TABLE_REPLICA_IDENTITY, currentWord, ctx.settings),
1597
- };
1598
- }
1599
- // ALTER TABLE x — sub-actions (must come AFTER the deep continuations above).
1600
- if (TailMatches(prevWords, ["ALTER", "TABLE", MatchAny])) {
1601
- return {
1602
- candidates: filterAndCase(ALTER_TABLE_ACTIONS, currentWord, ctx.settings),
1603
- };
1604
- }
1605
- // ALTER TABLE — table name.
1606
- if (TailMatches(prevWords, ["ALTER", "TABLE"]))
1607
- return completeTables();
1608
- // ---- ALTER VIEW / MATERIALIZED VIEW ----
1609
- if (TailMatches(prevWords, ["ALTER", "VIEW", MatchAny])) {
1610
- return {
1611
- candidates: filterAndCase(ALTER_VIEW_ACTIONS, currentWord, ctx.settings),
1612
- };
1613
- }
1614
- if (TailMatches(prevWords, ["ALTER", "VIEW"])) {
1615
- return completeTables(Query_for_list_of_views);
1616
- }
1617
- if (TailMatches(prevWords, ["ALTER", "MATERIALIZED", "VIEW", MatchAny])) {
1618
- return {
1619
- candidates: filterAndCase(ALTER_MATVIEW_ACTIONS, currentWord, ctx.settings),
1620
- };
1621
- }
1622
- if (TailMatches(prevWords, ["ALTER", "MATERIALIZED", "VIEW"])) {
1623
- return completeTables(Query_for_list_of_matviews);
1624
- }
1625
- // ---- ALTER INDEX ----
1626
- if (TailMatches(prevWords, ["ALTER", "INDEX", MatchAny])) {
1627
- return {
1628
- candidates: filterAndCase(ALTER_INDEX_ACTIONS, currentWord, ctx.settings),
1629
- };
1630
- }
1631
- if (TailMatches(prevWords, ["ALTER", "INDEX"])) {
1632
- return completeTables(Query_for_list_of_indexes);
1633
- }
1634
- // ---- ALTER SEQUENCE ----
1635
- if (TailMatches(prevWords, ["ALTER", "SEQUENCE", MatchAny])) {
1636
- return {
1637
- candidates: filterAndCase(ALTER_SEQUENCE_ACTIONS, currentWord, ctx.settings),
1638
- };
1639
- }
1640
- if (TailMatches(prevWords, ["ALTER", "SEQUENCE"])) {
1641
- return completeTables(Query_for_list_of_sequences);
1642
- }
1643
- // ---- ALTER FUNCTION / PROCEDURE / ROUTINE ----
1644
- if (TailMatches(prevWords, [
1645
- "ALTER",
1646
- "FUNCTION|PROCEDURE|ROUTINE",
1647
- MatchAny,
1648
- ])) {
1649
- return {
1650
- candidates: filterAndCase(ALTER_FUNCTION_ACTIONS, currentWord, ctx.settings),
1651
- };
1652
- }
1653
- if (TailMatches(prevWords, ["ALTER", "FUNCTION|PROCEDURE|ROUTINE"])) {
1654
- if (!conn)
1655
- return { candidates: [] };
1656
- return {
1657
- candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord),
1658
- };
1659
- }
1660
- // ---- ALTER TYPE ----
1661
- // `ALTER TYPE <enum> RENAME VALUE 'X<TAB>` — enum labels of the named
1662
- // type, wrapped in single quotes since the user is mid-string-literal.
1663
- // Mirrors upstream tab-complete.in.c ~line 1480.
1664
- if (TailMatches(prevWords, [
1665
- "ALTER",
1666
- "TYPE",
1667
- MatchAny,
1668
- "RENAME",
1669
- "VALUE",
1670
- ]) &&
1671
- currentWord.startsWith("'")) {
1672
- if (!conn)
1673
- return { candidates: [] };
1674
- const typeName = prevWords[prevWords.length - 3].toLowerCase();
1675
- // Strip the leading quote so the LIKE pattern matches `bar`/`BLACK`
1676
- // (enumlabel column) rather than `'bar`/`'BLACK`.
1677
- const labelPrefix = currentWord.slice(1);
1678
- const cands = await runCatalogQuery(conn, Query_for_list_of_enum_values_quoted, labelPrefix, [typeName]);
1679
- return { candidates: cands };
1680
- }
1681
- if (TailMatches(prevWords, ["ALTER", "TYPE", MatchAny])) {
1682
- return {
1683
- candidates: filterAndCase(ALTER_TYPE_ACTIONS, currentWord, ctx.settings),
1684
- };
1685
- }
1686
- if (TailMatches(prevWords, ["ALTER", "TYPE"])) {
1687
- if (!conn)
1688
- return { candidates: [] };
1689
- return {
1690
- candidates: await runCatalogQuery(conn, Query_for_list_of_types, currentWord),
1691
- };
1692
- }
1693
- // ---- ALTER ROLE / USER / GROUP ----
1694
- if (TailMatches(prevWords, ["ALTER", "ROLE|USER|GROUP", MatchAny])) {
1695
- return {
1696
- candidates: filterAndCase(ALTER_ROLE_ACTIONS, currentWord, ctx.settings),
1697
- };
1698
- }
1699
- if (TailMatches(prevWords, ["ALTER", "ROLE|USER|GROUP"])) {
1700
- if (!conn)
1701
- return { candidates: [] };
1702
- return {
1703
- candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord),
1704
- };
1705
- }
1706
- // ---- ALTER DATABASE ----
1707
- if (TailMatches(prevWords, ["ALTER", "DATABASE", MatchAny])) {
1708
- return {
1709
- candidates: filterAndCase(ALTER_DATABASE_ACTIONS, currentWord, ctx.settings),
1710
- };
1711
- }
1712
- if (TailMatches(prevWords, ["ALTER", "DATABASE"])) {
1713
- if (!conn)
1714
- return { candidates: [] };
1715
- return {
1716
- candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord),
1717
- };
1718
- }
1719
- // ---- ALTER SCHEMA ----
1720
- if (TailMatches(prevWords, ["ALTER", "SCHEMA", MatchAny])) {
1721
- return {
1722
- candidates: filterAndCase(ALTER_SCHEMA_ACTIONS, currentWord, ctx.settings),
1723
- };
1724
- }
1725
- if (TailMatches(prevWords, ["ALTER", "SCHEMA"])) {
1726
- if (!conn)
1727
- return { candidates: [] };
1728
- return {
1729
- candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord),
1730
- };
1731
- }
1732
- // ---- ALTER EXTENSION ----
1733
- if (TailMatches(prevWords, ["ALTER", "EXTENSION", MatchAny])) {
1734
- return {
1735
- candidates: filterAndCase(ALTER_EXTENSION_ACTIONS, currentWord, ctx.settings),
1736
- };
1737
- }
1738
- if (TailMatches(prevWords, ["ALTER", "EXTENSION"])) {
1739
- if (!conn)
1740
- return { candidates: [] };
1741
- return {
1742
- candidates: await runCatalogQuery(conn, Query_for_list_of_extensions, currentWord),
1743
- };
1744
- }
1745
- // ---- ALTER POLICY <name> ON <table> ----
1746
- if (TailMatches(prevWords, ["ALTER", "POLICY", MatchAny])) {
1747
- return {
1748
- candidates: filterAndCase(ALTER_POLICY_ACTIONS, currentWord, ctx.settings),
1749
- };
1750
- }
1751
- if (HeadMatches(prevWords, ["ALTER", "POLICY"]) &&
1752
- TailMatches(prevWords, ["ON"])) {
1753
- return completeTables();
1754
- }
1755
- if (TailMatches(prevWords, ["ALTER", "POLICY"])) {
1756
- // Free-form policy name.
1757
- return { candidates: [] };
1758
- }
1759
- // ---- ALTER PUBLICATION ----
1760
- if (TailMatches(prevWords, ["ALTER", "PUBLICATION", MatchAny])) {
1761
- return {
1762
- candidates: filterAndCase(ALTER_PUBLICATION_ACTIONS, currentWord, ctx.settings),
1763
- };
1764
- }
1765
- if (TailMatches(prevWords, ["ALTER", "PUBLICATION"])) {
1766
- if (!conn)
1767
- return { candidates: [] };
1768
- return {
1769
- candidates: await runCatalogQuery(conn, Query_for_list_of_publications, currentWord),
1770
- };
1771
- }
1772
- // ---- ALTER SUBSCRIPTION ----
1773
- if (TailMatches(prevWords, ["ALTER", "SUBSCRIPTION", MatchAny])) {
1774
- return {
1775
- candidates: filterAndCase(ALTER_SUBSCRIPTION_ACTIONS, currentWord, ctx.settings),
1776
- };
1777
- }
1778
- if (TailMatches(prevWords, ["ALTER", "SUBSCRIPTION"])) {
1779
- if (!conn)
1780
- return { candidates: [] };
1781
- return {
1782
- candidates: await runCatalogQuery(conn, Query_for_list_of_subscriptions, currentWord),
1783
- };
1784
- }
1785
- // Bare ALTER — sub-object keywords.
1786
- if (TailMatches(prevWords, ["ALTER"])) {
1787
- return {
1788
- candidates: filterAndCase(ALTER_OBJECTS, currentWord, ctx.settings),
1789
- };
1790
- }
1791
- // DROP TABLE, DROP VIEW, DROP INDEX, ...
1792
- if (TailMatches(prevWords, ["DROP", "TABLE"]))
1793
- return completeTables();
1794
- if (TailMatches(prevWords, ["DROP", "VIEW"])) {
1795
- return completeTables(Query_for_list_of_views);
1796
- }
1797
- if (TailMatches(prevWords, ["DROP", "MATERIALIZED", "VIEW"])) {
1798
- return completeTables(Query_for_list_of_matviews);
1799
- }
1800
- if (TailMatches(prevWords, ["DROP", "INDEX"])) {
1801
- return completeTables(Query_for_list_of_indexes);
1802
- }
1803
- if (TailMatches(prevWords, ["DROP", "SEQUENCE"])) {
1804
- return completeTables(Query_for_list_of_sequences);
1805
- }
1806
- if (TailMatches(prevWords, ["DROP", "TYPE"])) {
1807
- // Built-in scalar type keywords mirror upstream's
1808
- // `Keywords_for_list_of_datatypes` attached to the SchemaQuery; psql
1809
- // mixes them with user-defined types so `DROP TYPE big<TAB>` resolves
1810
- // to `bigint` even without a matching catalog row.
1811
- const keywords = filterAndCase(BUILTIN_DATATYPE_KEYWORDS, currentWord, ctx.settings);
1812
- if (!conn)
1813
- return { candidates: keywords };
1814
- const types = await runCatalogQuery(conn, Query_for_list_of_types, currentWord);
1815
- return { candidates: [...keywords, ...types] };
1816
- }
1817
- if (TailMatches(prevWords, ["DROP", "SCHEMA"])) {
1818
- if (!conn)
1819
- return { candidates: [] };
1820
- return {
1821
- candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord),
1822
- };
1823
- }
1824
- if (TailMatches(prevWords, ["DROP", "EXTENSION"])) {
1825
- if (!conn)
1826
- return { candidates: [] };
1827
- return {
1828
- candidates: await runCatalogQuery(conn, Query_for_list_of_extensions, currentWord),
1829
- };
1830
- }
1831
- if (TailMatches(prevWords, ["DROP", "ROLE|USER|GROUP"])) {
1832
- if (!conn)
1833
- return { candidates: [] };
1834
- return {
1835
- candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord),
1836
- };
1837
- }
1838
- if (TailMatches(prevWords, ["DROP", "DATABASE"])) {
1839
- if (!conn)
1840
- return { candidates: [] };
1841
- return {
1842
- candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord),
1843
- };
1844
- }
1845
- if (TailMatches(prevWords, ["DROP", "FUNCTION"])) {
1846
- if (!conn)
1847
- return { candidates: [] };
1848
- return {
1849
- candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord),
1850
- };
1851
- }
1852
- if (TailMatches(prevWords, ["DROP", "LANGUAGE"])) {
1853
- if (!conn)
1854
- return { candidates: [] };
1855
- return {
1856
- candidates: await runCatalogQuery(conn, Query_for_list_of_languages, currentWord),
1857
- };
1858
- }
1859
- if (TailMatches(prevWords, ["DROP", "TABLESPACE"])) {
1860
- if (!conn)
1861
- return { candidates: [] };
1862
- return {
1863
- candidates: await runCatalogQuery(conn, Query_for_list_of_tablespaces, currentWord),
1864
- };
1865
- }
1866
- if (TailMatches(prevWords, ["DROP", "PUBLICATION"])) {
1867
- // Mirrors upstream's `words_after_create` PUBLICATION entry
1868
- // (VersionedQuery on `Query_for_list_of_publications`). Two-step
1869
- // completion: `DROP PUBLIC<TAB>` first resolves to `PUBLICATION`
1870
- // via the static DROP_OBJECTS list (handled below by the bare
1871
- // `DROP` arm), then `DROP PUBLICATION <TAB>` lists publications.
1872
- if (!conn)
1873
- return { candidates: [] };
1874
- return {
1875
- candidates: await runCatalogQuery(conn, Query_for_list_of_publications, currentWord),
1876
- };
1877
- }
1878
- if (TailMatches(prevWords, ["DROP", "SUBSCRIPTION"])) {
1879
- // Same shape as DROP PUBLICATION — paired here for parity with the
1880
- // ALTER block above.
1881
- if (!conn)
1882
- return { candidates: [] };
1883
- return {
1884
- candidates: await runCatalogQuery(conn, Query_for_list_of_subscriptions, currentWord),
1885
- };
1886
- }
1887
- // Bare DROP — sub-object keywords.
1888
- if (TailMatches(prevWords, ["DROP"])) {
1889
- return {
1890
- candidates: filterAndCase(DROP_OBJECTS, currentWord, ctx.settings),
1891
- };
1892
- }
1893
- // ---- CREATE INDEX deep handling (specific arms BEFORE generic CREATE). ----
1894
- // CREATE INDEX <TAB> → CONCURRENTLY, IF NOT EXISTS, ON (no name = use ON
1895
- // directly), or a free-form index name.
1896
- if (TailMatches(prevWords, ["CREATE", "INDEX"])) {
1897
- return {
1898
- candidates: filterAndCase(CREATE_INDEX_OPTIONS, currentWord, ctx.settings),
1899
- };
1900
- }
1901
- if (TailMatches(prevWords, ["CREATE", "UNIQUE", "INDEX"])) {
1902
- return {
1903
- candidates: filterAndCase(CREATE_INDEX_OPTIONS, currentWord, ctx.settings),
1904
- };
1905
- }
1906
- // CREATE INDEX <name> <TAB> → ON.
1907
- if (TailMatches(prevWords, ["CREATE", "INDEX", MatchAny]) ||
1908
- TailMatches(prevWords, ["CREATE", "UNIQUE", "INDEX", MatchAny])) {
1909
- return { candidates: filterAndCase(["ON"], currentWord, ctx.settings) };
1910
- }
1911
- // CREATE INDEX ... ON <TAB> → tables.
1912
- if (TailMatches(prevWords, ["CREATE", "INDEX", MatchAny, "ON"]) ||
1913
- TailMatches(prevWords, ["CREATE", "INDEX", "ON"]) ||
1914
- TailMatches(prevWords, ["CREATE", "UNIQUE", "INDEX", MatchAny, "ON"]) ||
1915
- TailMatches(prevWords, ["CREATE", "UNIQUE", "INDEX", "ON"])) {
1916
- return completeTables();
1917
- }
1918
- // CREATE INDEX ... ON <table> <TAB> → USING / (.
1919
- if ((HeadMatches(prevWords, ["CREATE", "INDEX"]) ||
1920
- HeadMatches(prevWords, ["CREATE", "UNIQUE", "INDEX"])) &&
1921
- TailMatches(prevWords, ["ON", MatchAny])) {
1922
- return {
1923
- candidates: filterAndCase(["USING", "("], currentWord, ctx.settings),
1924
- };
1925
- }
1926
- // CREATE INDEX ... USING <TAB> → access methods.
1927
- if ((HeadMatches(prevWords, ["CREATE", "INDEX"]) ||
1928
- HeadMatches(prevWords, ["CREATE", "UNIQUE", "INDEX"])) &&
1929
- TailMatches(prevWords, ["USING"])) {
1930
- if (!conn) {
1931
- // Even without a connection, offer the built-in AMs as a fallback.
1932
- return {
1933
- candidates: filterAndCase(["btree", "hash", "gist", "gin", "spgist", "brin"], currentWord, ctx.settings),
1934
- };
1935
- }
1936
- return {
1937
- candidates: await runCatalogQuery(conn, Query_for_list_of_index_access_methods, currentWord),
1938
- };
1939
- }
1940
- // CREATE ... — first sub-object keyword.
1941
- if (TailMatches(prevWords, ["CREATE"])) {
1942
- return {
1943
- candidates: filterAndCase(CREATE_OBJECTS, currentWord, ctx.settings),
1944
- };
1945
- }
1946
- if (TailMatches(prevWords, ["CREATE", "TABLE"])) {
1947
- // Upstream's `words_after_create` fallback (tab-complete.in.c
1948
- // ~lines 2062-2082) runs `Query_for_list_of_tables` when the
1949
- // word immediately before the cursor is `TABLE`. The intent is to
1950
- // surface existing table names as a HINT — the user can pick a
1951
- // similar name as a starting point. The completion is non-binding;
1952
- // psql doesn't actually insert one of these on a single Tab if
1953
- // the prefix is ambiguous, but the listing on Tab-Tab matches
1954
- // upstream's `qr/mytab123 +mytab246/`.
1955
- return completeTables();
1956
- }
1957
- // Inside CREATE TABLE column-list parens: `<col_name> <TAB>` → types.
1958
- if (HeadMatches(prevWords, ["CREATE", "TABLE"]) &&
1959
- TailMatches(prevWords, ["(|,", MatchAny])) {
1960
- if (!conn)
1961
- return { candidates: [] };
1962
- return {
1963
- candidates: await runCatalogQuery(conn, Query_for_list_of_datatypes, currentWord),
1964
- };
1965
- }
1966
- if (TailMatches(prevWords, ["CREATE", "OR", "REPLACE"])) {
1967
- return {
1968
- candidates: filterAndCase([
1969
- "FUNCTION",
1970
- "PROCEDURE",
1971
- "VIEW",
1972
- "TRIGGER",
1973
- "AGGREGATE",
1974
- "TRANSFORM",
1975
- ], currentWord, ctx.settings),
1976
- };
1977
- }
1978
- // TRUNCATE [TABLE] x
1979
- if (TailMatches(prevWords, ["TRUNCATE"])) {
1980
- return {
1981
- candidates: [
1982
- ...filterAndCase(["TABLE", "ONLY"], currentWord, ctx.settings),
1983
- ...(await tableCandidates(conn, currentWord)),
1984
- ],
1985
- };
1986
- }
1987
- if (TailMatches(prevWords, ["TRUNCATE", "TABLE"]))
1988
- return completeTables();
1989
- if (TailMatches(prevWords, ["TRUNCATE", "ONLY"]))
1990
- return completeTables();
1991
- // COPY ... FROM <sth> WITH ( — option keywords inside the WITH
1992
- // parenthesised list. The tokenizer splits `(` and `,` into their
1993
- // own tokens so the tail looks like `[... WITH (]` for the first
1994
- // option and `[..., WITH, (, opt, ,]` for subsequent ones. Two
1995
- // arms: one for FROM (which adds DEFAULT, FORCE_NOT_NULL, etc.)
1996
- // and one for TO (which adds FORCE_QUOTE only). Mirrors upstream
1997
- // tab-complete.in.c ~3309-3315 (`Copy_from_options` /
1998
- // `Copy_to_options`). The `HeadMatches` guard restricts the rule
1999
- // to a real COPY statement so the generic `(`/`,` pattern doesn't
2000
- // fire inside CREATE TABLE / SELECT lists.
2001
- if (HeadMatches(prevWords, ["COPY|\\copy"]) &&
2002
- HeadMatches(prevWords, [MatchAny, MatchAny, "FROM"]) &&
2003
- (TailMatches(prevWords, ["("]) || TailMatches(prevWords, [","]))) {
2004
- return {
2005
- candidates: filterAndCase(COPY_FROM_OPTIONS, currentWord, ctx.settings),
2006
- };
2007
- }
2008
- if (HeadMatches(prevWords, ["COPY|\\copy"]) &&
2009
- HeadMatches(prevWords, [MatchAny, MatchAny, "TO"]) &&
2010
- (TailMatches(prevWords, ["("]) || TailMatches(prevWords, [","]))) {
2011
- return {
2012
- candidates: filterAndCase(COPY_TO_OPTIONS, currentWord, ctx.settings),
2013
- };
2014
- }
2015
- // COPY x → tables. (The `COPY x FROM/TO <path>` filename completion is
2016
- // handled by the early `isCopyFromOrTo` check at the top of this
2017
- // function so it wins over the generic `FROM <prefix>` table rule.)
2018
- if (TailMatches(prevWords, ["COPY"]))
2019
- return completeTables();
2020
- if (TailMatches(prevWords, ["COPY", MatchAny])) {
2021
- return {
2022
- candidates: filterAndCase(["FROM", "TO"], currentWord, ctx.settings),
2023
- };
2024
- }
2025
- // ANALYZE x → tables (optional VERBOSE first).
2026
- if (TailMatches(prevWords, ["ANALYZE"]) ||
2027
- TailMatches(prevWords, ["ANALYZE", "VERBOSE"])) {
2028
- return completeTables();
2029
- }
2030
- // VACUUM x → tables.
2031
- if (TailMatches(prevWords, ["VACUUM"]) ||
2032
- TailMatches(prevWords, ["VACUUM", "VERBOSE"])) {
2033
- return completeTables();
2034
- }
2035
- if (TailMatches(prevWords, ["VACUUM", "FULL"]))
2036
- return completeTables();
2037
- if (TailMatches(prevWords, ["VACUUM", "ANALYZE"]))
2038
- return completeTables();
2039
- // REINDEX [TABLE|INDEX|SCHEMA|DATABASE] x
2040
- if (TailMatches(prevWords, ["REINDEX"])) {
2041
- return {
2042
- candidates: filterAndCase([
2043
- "TABLE",
2044
- "INDEX",
2045
- "SCHEMA",
2046
- "DATABASE",
2047
- "SYSTEM",
2048
- "CONCURRENTLY",
2049
- ], currentWord, ctx.settings),
2050
- };
2051
- }
2052
- if (TailMatches(prevWords, ["REINDEX", "TABLE"]))
2053
- return completeTables();
2054
- if (TailMatches(prevWords, ["REINDEX", "INDEX"])) {
2055
- return completeTables(Query_for_list_of_indexes);
2056
- }
2057
- if (TailMatches(prevWords, ["REINDEX", "SCHEMA"])) {
2058
- if (!conn)
2059
- return { candidates: [] };
2060
- return {
2061
- candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord),
2062
- };
2063
- }
2064
- if (TailMatches(prevWords, ["REINDEX", "DATABASE"])) {
2065
- if (!conn)
2066
- return { candidates: [] };
2067
- return {
2068
- candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord),
2069
- };
2070
- }
2071
- // GRANT … ON … TO …
2072
- if (TailMatches(prevWords, ["GRANT"]) ||
2073
- TailMatches(prevWords, ["REVOKE"])) {
2074
- return {
2075
- candidates: filterAndCase(PRIVILEGE_KEYWORDS, currentWord, ctx.settings),
2076
- };
2077
- }
2078
- if (TailMatches(prevWords, ["ON"])) {
2079
- // Could be either GRANT/REVOKE … ON or CREATE INDEX … ON. We try
2080
- // tables; if the prior keyword set is CREATE INDEX the rule above
2081
- // already short-circuited.
2082
- return completeTables();
2083
- }
2084
- if (TailMatches(prevWords, ["TO"])) {
2085
- if (HeadMatches(prevWords, ["GRANT"]) && conn) {
2086
- return {
2087
- candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord),
2088
- };
2089
- }
2090
- }
2091
- if (TailMatches(prevWords, ["FROM"]) &&
2092
- HeadMatches(prevWords, ["REVOKE"])) {
2093
- if (!conn)
2094
- return { candidates: [] };
2095
- return {
2096
- candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord),
2097
- };
2098
- }
2099
- // LOCK [TABLE] x
2100
- if (TailMatches(prevWords, ["LOCK"])) {
2101
- return {
2102
- candidates: [
2103
- ...filterAndCase(["TABLE"], currentWord, ctx.settings),
2104
- ...(await tableCandidates(conn, currentWord)),
2105
- ],
2106
- };
2107
- }
2108
- if (TailMatches(prevWords, ["LOCK", "TABLE"]))
2109
- return completeTables();
2110
- // SET search_path, SET ROLE, SET SCHEMA, etc.
2111
- if (TailMatches(prevWords, ["SET", "ROLE"])) {
2112
- if (!conn)
2113
- return { candidates: [] };
2114
- return {
2115
- candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord),
2116
- };
2117
- }
2118
- if (TailMatches(prevWords, ["SET", "SCHEMA"])) {
2119
- if (!conn)
2120
- return { candidates: [] };
2121
- return {
2122
- candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord),
2123
- };
2124
- }
2125
- if (TailMatches(prevWords, ["SET", "SESSION"])) {
2126
- return {
2127
- candidates: filterAndCase(["AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"], currentWord, ctx.settings),
2128
- };
2129
- }
2130
- if (TailMatches(prevWords, ["SET", "TRANSACTION"])) {
2131
- return {
2132
- candidates: filterAndCase(["ISOLATION LEVEL", "READ ONLY", "READ WRITE", "DEFERRABLE"], currentWord, ctx.settings),
2133
- };
2134
- }
2135
- // `SET <name> TO|= <TAB>` — for DateStyle we can suggest the formats.
2136
- if (TailMatches(prevWords, ["SET", "DateStyle", "TO|="]) ||
2137
- TailMatches(prevWords, ["SET", "DATESTYLE", "TO|="]) ||
2138
- TailMatches(prevWords, ["SET", "datestyle", "TO|="])) {
2139
- return {
2140
- candidates: filterAndCase(DATESTYLE_VALUES, currentWord, ctx.settings),
2141
- };
2142
- }
2143
- // `SET timezone TO <prefix>` — names from pg_timezone_names. Two
2144
- // variants depending on whether the user has opened a single quote:
2145
- // - `SET timezone TO am<TAB>` → user typed an unquoted prefix; we
2146
- // respond with `'America/'`-style quoted candidates so the next
2147
- // keystroke continues inside the string literal. The
2148
- // `Query_for_list_of_timezone_names_quoted_out` template matches
2149
- // LIKE against the unquoted name and returns `quote_literal(name)`.
2150
- // - `SET timezone TO 'America/New_<TAB>` → user is mid-literal;
2151
- // `Query_for_list_of_timezone_names_quoted_in` matches the LIKE
2152
- // pattern against the quoted form so the partial `'America/New_`
2153
- // resolves correctly.
2154
- // Mirrors upstream tab-complete.in.c ~line 4530.
2155
- if (TailMatches(prevWords, ["SET", "timezone", "TO|="]) ||
2156
- TailMatches(prevWords, ["SET", "TIMEZONE", "TO|="]) ||
2157
- TailMatches(prevWords, ["SET", "TimeZone", "TO|="])) {
2158
- if (!conn)
2159
- return { candidates: [] };
2160
- const query = currentWord.startsWith("'")
2161
- ? Query_for_list_of_timezone_names_quoted_in
2162
- : Query_for_list_of_timezone_names_quoted_out;
2163
- const cands = await runCatalogQuery(conn, query, currentWord);
2164
- return { candidates: cands };
2165
- }
2166
- // `SET <name> TO|= <prefix>` for any enum-typed GUC — emit the legal
2167
- // values from `pg_settings.enumvals`. Mirrors upstream `tab-complete.in.c`
2168
- // `Query_for_values_of_enum_GUC`. Enum-typed GUCs (intervalstyle,
2169
- // bytea_output, synchronous_commit, plpgsql.variable_conflict, …) get
2170
- // value completion; non-enum GUCs return an empty rowset (no candidates)
2171
- // — matching upstream, which also emits nothing for unknown GUCs once a
2172
- // value position is reached.
2173
- if (TailMatches(prevWords, ["SET", MatchAny, "TO|="])) {
2174
- if (!conn)
2175
- return { candidates: [] };
2176
- // Walk back to the GUC name (the word before TO/=).
2177
- const gucName = prevWords[prevWords.length - 2];
2178
- const cands = await runCatalogQuery(conn, Query_for_values_of_enum_GUC, currentWord, [gucName]);
2179
- return { candidates: cands };
2180
- }
2181
- // `SET <name> <TAB>` (no operator yet) → TO.
2182
- // Upstream tab-complete.in.c uses `COMPLETE_WITH("TO")` here even
2183
- // though `SET <name> = <value>` is valid syntax — the goal is a
2184
- // single unique completion so `set foo<tab><tab>` resolves to
2185
- // `set foo TO ` rather than listing two near-synonymous separators.
2186
- // Verified against vanilla psql 18 + upstream test line 366.
2187
- if (TailMatches(prevWords, ["SET", MatchAny])) {
2188
- return {
2189
- candidates: filterAndCase(["TO"], currentWord, ctx.settings),
2190
- };
2191
- }
2192
- // Bare SET <TAB>: GUC name OR top-level SET sub-keywords (ROLE, SCHEMA, …).
2193
- if (TailMatches(prevWords, ["SET"])) {
2194
- const staticKw = filterAndCase([
2195
- "CONSTRAINTS",
2196
- "LOCAL",
2197
- "ROLE",
2198
- "SCHEMA",
2199
- "SESSION",
2200
- "TIME ZONE",
2201
- "TRANSACTION",
2202
- ], currentWord, ctx.settings);
2203
- if (!conn)
2204
- return { candidates: staticKw };
2205
- const guc = await runCatalogQuery(conn, Query_for_list_of_set_vars, currentWord);
2206
- return { candidates: [...staticKw, ...guc] };
2207
- }
2208
- // SHOW <TAB> — ALL keyword plus the live GUC list.
2209
- if (TailMatches(prevWords, ["SHOW"])) {
2210
- const staticKw = filterAndCase(["ALL"], currentWord, ctx.settings);
2211
- if (!conn) {
2212
- return {
2213
- candidates: [
2214
- ...staticKw,
2215
- ...filterAndCase([
2216
- "search_path",
2217
- "role",
2218
- "session_authorization",
2219
- "transaction_isolation",
2220
- "client_encoding",
2221
- "server_encoding",
2222
- "server_version",
2223
- "timezone",
2224
- ], currentWord, ctx.settings),
2225
- ],
2226
- };
2227
- }
2228
- const guc = await runCatalogQuery(conn, Query_for_list_of_set_vars, currentWord);
2229
- return { candidates: [...staticKw, ...guc] };
2230
- }
2231
- // RESET <TAB> — ALL or the live GUC list.
2232
- if (TailMatches(prevWords, ["RESET"])) {
2233
- const staticKw = filterAndCase(["ALL", "SESSION AUTHORIZATION", "ROLE"], currentWord, ctx.settings);
2234
- if (!conn)
2235
- return { candidates: staticKw };
2236
- const guc = await runCatalogQuery(conn, Query_for_list_of_set_vars, currentWord);
2237
- return { candidates: [...staticKw, ...guc] };
2238
- }
2239
- // START / BEGIN [TRANSACTION] …
2240
- if (TailMatches(prevWords, ["BEGIN"]) ||
2241
- TailMatches(prevWords, ["START"])) {
2242
- return {
2243
- candidates: filterAndCase(TRANSACTION_KEYWORDS, currentWord, ctx.settings),
2244
- };
2245
- }
2246
- // COMMIT / ROLLBACK / RELEASE / SAVEPOINT - small completions
2247
- if (TailMatches(prevWords, ["ROLLBACK"])) {
2248
- return {
2249
- candidates: filterAndCase(["TO SAVEPOINT", "TRANSACTION", "AND CHAIN", "AND NO CHAIN"], currentWord, ctx.settings),
2250
- };
2251
- }
2252
- // EXPLAIN … — pass through to SELECT-/INSERT-/UPDATE-style rules by
2253
- // letting subsequent words drive completion. For the first word after
2254
- // EXPLAIN, offer the options + statement keywords.
2255
- if (TailMatches(prevWords, ["EXPLAIN"])) {
2256
- return {
2257
- candidates: filterAndCase([
2258
- "ANALYZE",
2259
- "VERBOSE",
2260
- "SELECT",
2261
- "INSERT INTO",
2262
- "UPDATE",
2263
- "DELETE FROM",
2264
- "(",
2265
- ], currentWord, ctx.settings),
2266
- };
2267
- }
2268
- // DECLARE … CURSOR FOR …
2269
- if (TailMatches(prevWords, ["DECLARE"])) {
2270
- return { candidates: [] };
2271
- }
2272
- // CALL / DO — no completion beyond keywords (procedure args / language).
2273
- if (TailMatches(prevWords, ["CALL"])) {
2274
- if (!conn)
2275
- return { candidates: [] };
2276
- return {
2277
- candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord),
2278
- };
2279
- }
2280
- // After SELECT — offer FROM / common functions.
2281
- if (TailMatches(prevWords, ["SELECT"])) {
2282
- // The next word can be anything (column list); offer DISTINCT/ALL/* as a
2283
- // small hint set.
2284
- return {
2285
- candidates: filterAndCase([
2286
- "DISTINCT",
2287
- "ALL",
2288
- "*",
2289
- "CURRENT_DATE",
2290
- "CURRENT_TIME",
2291
- "CURRENT_TIMESTAMP",
2292
- "CURRENT_USER",
2293
- "NULL",
2294
- "TRUE",
2295
- "FALSE",
2296
- ], currentWord, ctx.settings),
2297
- };
2298
- }
2299
- // Trailing-keyword fallthrough: WHERE, ORDER BY, GROUP BY, LIMIT etc.
2300
- if (TailMatches(prevWords, ["SELECT", "*"])) {
2301
- return {
2302
- candidates: filterAndCase(["FROM"], currentWord, ctx.settings),
2303
- };
2304
- }
2305
- // No specific rule fired.
2306
- return { candidates: [] };
1065
+ const completeTables = async (query = Query_for_list_of_tables) => {
1066
+ if (!conn) return { candidates: [] };
1067
+ return { candidates: await completeSchemaOrRelations(conn, currentWord, query) };
1068
+ };
1069
+ if (isCopyFromOrTo(prevWords)) return { candidates: completeFilenames(currentWord, "sql") };
1070
+ if (TailMatches(prevWords, ["FROM"]) && !HeadMatches(prevWords, ["REVOKE"])) return completeTables(Query_for_list_of_tables_views);
1071
+ if (TailMatches(prevWords, ["UPDATE"])) return completeTables();
1072
+ if (TailMatches(prevWords, ["DELETE", "FROM"])) return completeTables();
1073
+ if (TailMatches(prevWords, ["INTO"])) return completeTables();
1074
+ if (TailMatches(prevWords, ["JOIN"])) return completeTables(Query_for_list_of_tables_views);
1075
+ if (TailMatches(prevWords, ["JOIN", null])) return { candidates: filterAndCase(["ON", "USING"], currentWord, ctx.settings) };
1076
+ const inSelectContext = HeadMatches(prevWords, ["SELECT"]) || HeadMatches(prevWords, ["INSERT"]) || HeadMatches(prevWords, ["UPDATE"]) || HeadMatches(prevWords, ["DELETE"]) || HeadMatches(prevWords, ["WITH"]) || HeadMatches(prevWords, ["EXPLAIN"]);
1077
+ if (inSelectContext && (TailMatches(prevWords, ["FROM", null]) || TailMatches(prevWords, [
1078
+ "FROM",
1079
+ null,
1080
+ null
1081
+ ]))) return { candidates: filterAndCase(POST_FROM_KEYWORDS, currentWord, ctx.settings) };
1082
+ if (TailMatches(prevWords, ["OVER", "("])) return { candidates: filterAndCase(WINDOW_FRAME_KEYWORDS, currentWord, ctx.settings) };
1083
+ if (inSelectContext && TailMatches(prevWords, ["WHERE", null]) && !TailMatches(prevWords, ["WHERE"])) return { candidates: filterAndCase(WHERE_CONTINUATIONS, currentWord, ctx.settings) };
1084
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["ADD"])) return { candidates: filterAndCase(ALTER_TABLE_ADD, currentWord, ctx.settings) };
1085
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["DROP"])) return { candidates: filterAndCase(ALTER_TABLE_DROP, currentWord, ctx.settings) };
1086
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["DROP", "CONSTRAINT"])) {
1087
+ if (!conn) return { candidates: [] };
1088
+ const refTokens = prevWords.slice(2, prevWords.length - 2);
1089
+ const ref = parseTableRef(refTokens);
1090
+ if (!ref) return { candidates: [] };
1091
+ return { candidates: ref.schema === null ? await runCatalogQuery(conn, Query_for_constraint_of_table, currentWord, [ref.table]) : await runCatalogQuery(conn, Query_for_constraint_of_table_in_schema, currentWord, [ref.schema, ref.table]) };
1092
+ }
1093
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["RENAME"])) return { candidates: filterAndCase(ALTER_TABLE_RENAME, currentWord, ctx.settings) };
1094
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && (TailMatches(prevWords, ["ALTER"]) || TailMatches(prevWords, [
1095
+ "ALTER",
1096
+ "COLUMN",
1097
+ null
1098
+ ]))) return { candidates: filterAndCase(ALTER_TABLE_ALTER_COLUMN, currentWord, ctx.settings) };
1099
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["SET"])) return { candidates: filterAndCase(ALTER_TABLE_SET, currentWord, ctx.settings) };
1100
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["ENABLE"])) return { candidates: filterAndCase(ALTER_TABLE_ENABLE, currentWord, ctx.settings) };
1101
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["DISABLE"])) return { candidates: filterAndCase(ALTER_TABLE_DISABLE, currentWord, ctx.settings) };
1102
+ if (HeadMatches(prevWords, ["ALTER", "TABLE"]) && TailMatches(prevWords, ["REPLICA", "IDENTITY"])) return { candidates: filterAndCase(ALTER_TABLE_REPLICA_IDENTITY, currentWord, ctx.settings) };
1103
+ if (TailMatches(prevWords, [
1104
+ "ALTER",
1105
+ "TABLE",
1106
+ null
1107
+ ])) return { candidates: filterAndCase(ALTER_TABLE_ACTIONS, currentWord, ctx.settings) };
1108
+ if (TailMatches(prevWords, ["ALTER", "TABLE"])) return completeTables();
1109
+ if (TailMatches(prevWords, [
1110
+ "ALTER",
1111
+ "VIEW",
1112
+ null
1113
+ ])) return { candidates: filterAndCase(ALTER_VIEW_ACTIONS, currentWord, ctx.settings) };
1114
+ if (TailMatches(prevWords, ["ALTER", "VIEW"])) return completeTables(Query_for_list_of_views);
1115
+ if (TailMatches(prevWords, [
1116
+ "ALTER",
1117
+ "MATERIALIZED",
1118
+ "VIEW",
1119
+ null
1120
+ ])) return { candidates: filterAndCase(ALTER_MATVIEW_ACTIONS, currentWord, ctx.settings) };
1121
+ if (TailMatches(prevWords, [
1122
+ "ALTER",
1123
+ "MATERIALIZED",
1124
+ "VIEW"
1125
+ ])) return completeTables(Query_for_list_of_matviews);
1126
+ if (TailMatches(prevWords, [
1127
+ "ALTER",
1128
+ "INDEX",
1129
+ null
1130
+ ])) return { candidates: filterAndCase(ALTER_INDEX_ACTIONS, currentWord, ctx.settings) };
1131
+ if (TailMatches(prevWords, ["ALTER", "INDEX"])) return completeTables(Query_for_list_of_indexes);
1132
+ if (TailMatches(prevWords, [
1133
+ "ALTER",
1134
+ "SEQUENCE",
1135
+ null
1136
+ ])) return { candidates: filterAndCase(ALTER_SEQUENCE_ACTIONS, currentWord, ctx.settings) };
1137
+ if (TailMatches(prevWords, ["ALTER", "SEQUENCE"])) return completeTables(Query_for_list_of_sequences);
1138
+ if (TailMatches(prevWords, [
1139
+ "ALTER",
1140
+ "FUNCTION|PROCEDURE|ROUTINE",
1141
+ null
1142
+ ])) return { candidates: filterAndCase(ALTER_FUNCTION_ACTIONS, currentWord, ctx.settings) };
1143
+ if (TailMatches(prevWords, ["ALTER", "FUNCTION|PROCEDURE|ROUTINE"])) {
1144
+ if (!conn) return { candidates: [] };
1145
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord) };
1146
+ }
1147
+ if (TailMatches(prevWords, [
1148
+ "ALTER",
1149
+ "TYPE",
1150
+ null,
1151
+ "RENAME",
1152
+ "VALUE"
1153
+ ]) && currentWord.startsWith("'")) {
1154
+ if (!conn) return { candidates: [] };
1155
+ const typeName = prevWords[prevWords.length - 3].toLowerCase();
1156
+ const labelPrefix = currentWord.slice(1);
1157
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_enum_values_quoted, labelPrefix, [typeName]) };
1158
+ }
1159
+ if (TailMatches(prevWords, [
1160
+ "ALTER",
1161
+ "TYPE",
1162
+ null
1163
+ ])) return { candidates: filterAndCase(ALTER_TYPE_ACTIONS, currentWord, ctx.settings) };
1164
+ if (TailMatches(prevWords, ["ALTER", "TYPE"])) {
1165
+ if (!conn) return { candidates: [] };
1166
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_types, currentWord) };
1167
+ }
1168
+ if (TailMatches(prevWords, [
1169
+ "ALTER",
1170
+ "ROLE|USER|GROUP",
1171
+ null
1172
+ ])) return { candidates: filterAndCase(ALTER_ROLE_ACTIONS, currentWord, ctx.settings) };
1173
+ if (TailMatches(prevWords, ["ALTER", "ROLE|USER|GROUP"])) {
1174
+ if (!conn) return { candidates: [] };
1175
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
1176
+ }
1177
+ if (TailMatches(prevWords, [
1178
+ "ALTER",
1179
+ "DATABASE",
1180
+ null
1181
+ ])) return { candidates: filterAndCase(ALTER_DATABASE_ACTIONS, currentWord, ctx.settings) };
1182
+ if (TailMatches(prevWords, ["ALTER", "DATABASE"])) {
1183
+ if (!conn) return { candidates: [] };
1184
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord) };
1185
+ }
1186
+ if (TailMatches(prevWords, [
1187
+ "ALTER",
1188
+ "SCHEMA",
1189
+ null
1190
+ ])) return { candidates: filterAndCase(ALTER_SCHEMA_ACTIONS, currentWord, ctx.settings) };
1191
+ if (TailMatches(prevWords, ["ALTER", "SCHEMA"])) {
1192
+ if (!conn) return { candidates: [] };
1193
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord) };
1194
+ }
1195
+ if (TailMatches(prevWords, [
1196
+ "ALTER",
1197
+ "EXTENSION",
1198
+ null
1199
+ ])) return { candidates: filterAndCase(ALTER_EXTENSION_ACTIONS, currentWord, ctx.settings) };
1200
+ if (TailMatches(prevWords, ["ALTER", "EXTENSION"])) {
1201
+ if (!conn) return { candidates: [] };
1202
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_extensions, currentWord) };
1203
+ }
1204
+ if (TailMatches(prevWords, [
1205
+ "ALTER",
1206
+ "POLICY",
1207
+ null
1208
+ ])) return { candidates: filterAndCase(ALTER_POLICY_ACTIONS, currentWord, ctx.settings) };
1209
+ if (HeadMatches(prevWords, ["ALTER", "POLICY"]) && TailMatches(prevWords, ["ON"])) return completeTables();
1210
+ if (TailMatches(prevWords, ["ALTER", "POLICY"])) return { candidates: [] };
1211
+ if (TailMatches(prevWords, [
1212
+ "ALTER",
1213
+ "PUBLICATION",
1214
+ null
1215
+ ])) return { candidates: filterAndCase(ALTER_PUBLICATION_ACTIONS, currentWord, ctx.settings) };
1216
+ if (TailMatches(prevWords, ["ALTER", "PUBLICATION"])) {
1217
+ if (!conn) return { candidates: [] };
1218
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_publications, currentWord) };
1219
+ }
1220
+ if (TailMatches(prevWords, [
1221
+ "ALTER",
1222
+ "SUBSCRIPTION",
1223
+ null
1224
+ ])) return { candidates: filterAndCase(ALTER_SUBSCRIPTION_ACTIONS, currentWord, ctx.settings) };
1225
+ if (TailMatches(prevWords, ["ALTER", "SUBSCRIPTION"])) {
1226
+ if (!conn) return { candidates: [] };
1227
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_subscriptions, currentWord) };
1228
+ }
1229
+ if (TailMatches(prevWords, ["ALTER"])) return { candidates: filterAndCase(ALTER_OBJECTS, currentWord, ctx.settings) };
1230
+ if (TailMatches(prevWords, ["DROP", "TABLE"])) return completeTables();
1231
+ if (TailMatches(prevWords, ["DROP", "VIEW"])) return completeTables(Query_for_list_of_views);
1232
+ if (TailMatches(prevWords, [
1233
+ "DROP",
1234
+ "MATERIALIZED",
1235
+ "VIEW"
1236
+ ])) return completeTables(Query_for_list_of_matviews);
1237
+ if (TailMatches(prevWords, ["DROP", "INDEX"])) return completeTables(Query_for_list_of_indexes);
1238
+ if (TailMatches(prevWords, ["DROP", "SEQUENCE"])) return completeTables(Query_for_list_of_sequences);
1239
+ if (TailMatches(prevWords, ["DROP", "TYPE"])) {
1240
+ const keywords = filterAndCase(BUILTIN_DATATYPE_KEYWORDS, currentWord, ctx.settings);
1241
+ if (!conn) return { candidates: keywords };
1242
+ const types = await runCatalogQuery(conn, Query_for_list_of_types, currentWord);
1243
+ return { candidates: [...keywords, ...types] };
1244
+ }
1245
+ if (TailMatches(prevWords, ["DROP", "SCHEMA"])) {
1246
+ if (!conn) return { candidates: [] };
1247
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord) };
1248
+ }
1249
+ if (TailMatches(prevWords, ["DROP", "EXTENSION"])) {
1250
+ if (!conn) return { candidates: [] };
1251
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_extensions, currentWord) };
1252
+ }
1253
+ if (TailMatches(prevWords, ["DROP", "ROLE|USER|GROUP"])) {
1254
+ if (!conn) return { candidates: [] };
1255
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
1256
+ }
1257
+ if (TailMatches(prevWords, ["DROP", "DATABASE"])) {
1258
+ if (!conn) return { candidates: [] };
1259
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord) };
1260
+ }
1261
+ if (TailMatches(prevWords, ["DROP", "FUNCTION"])) {
1262
+ if (!conn) return { candidates: [] };
1263
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord) };
1264
+ }
1265
+ if (TailMatches(prevWords, ["DROP", "LANGUAGE"])) {
1266
+ if (!conn) return { candidates: [] };
1267
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_languages, currentWord) };
1268
+ }
1269
+ if (TailMatches(prevWords, ["DROP", "TABLESPACE"])) {
1270
+ if (!conn) return { candidates: [] };
1271
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_tablespaces, currentWord) };
1272
+ }
1273
+ if (TailMatches(prevWords, ["DROP", "PUBLICATION"])) {
1274
+ if (!conn) return { candidates: [] };
1275
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_publications, currentWord) };
1276
+ }
1277
+ if (TailMatches(prevWords, ["DROP", "SUBSCRIPTION"])) {
1278
+ if (!conn) return { candidates: [] };
1279
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_subscriptions, currentWord) };
1280
+ }
1281
+ if (TailMatches(prevWords, ["DROP"])) return { candidates: filterAndCase(DROP_OBJECTS, currentWord, ctx.settings) };
1282
+ if (TailMatches(prevWords, ["CREATE", "INDEX"])) return { candidates: filterAndCase(CREATE_INDEX_OPTIONS, currentWord, ctx.settings) };
1283
+ if (TailMatches(prevWords, [
1284
+ "CREATE",
1285
+ "UNIQUE",
1286
+ "INDEX"
1287
+ ])) return { candidates: filterAndCase(CREATE_INDEX_OPTIONS, currentWord, ctx.settings) };
1288
+ if (TailMatches(prevWords, [
1289
+ "CREATE",
1290
+ "INDEX",
1291
+ null
1292
+ ]) || TailMatches(prevWords, [
1293
+ "CREATE",
1294
+ "UNIQUE",
1295
+ "INDEX",
1296
+ null
1297
+ ])) return { candidates: filterAndCase(["ON"], currentWord, ctx.settings) };
1298
+ if (TailMatches(prevWords, [
1299
+ "CREATE",
1300
+ "INDEX",
1301
+ null,
1302
+ "ON"
1303
+ ]) || TailMatches(prevWords, [
1304
+ "CREATE",
1305
+ "INDEX",
1306
+ "ON"
1307
+ ]) || TailMatches(prevWords, [
1308
+ "CREATE",
1309
+ "UNIQUE",
1310
+ "INDEX",
1311
+ null,
1312
+ "ON"
1313
+ ]) || TailMatches(prevWords, [
1314
+ "CREATE",
1315
+ "UNIQUE",
1316
+ "INDEX",
1317
+ "ON"
1318
+ ])) return completeTables();
1319
+ if ((HeadMatches(prevWords, ["CREATE", "INDEX"]) || HeadMatches(prevWords, [
1320
+ "CREATE",
1321
+ "UNIQUE",
1322
+ "INDEX"
1323
+ ])) && TailMatches(prevWords, ["ON", null])) return { candidates: filterAndCase(["USING", "("], currentWord, ctx.settings) };
1324
+ if ((HeadMatches(prevWords, ["CREATE", "INDEX"]) || HeadMatches(prevWords, [
1325
+ "CREATE",
1326
+ "UNIQUE",
1327
+ "INDEX"
1328
+ ])) && TailMatches(prevWords, ["USING"])) {
1329
+ if (!conn) return { candidates: filterAndCase([
1330
+ "btree",
1331
+ "hash",
1332
+ "gist",
1333
+ "gin",
1334
+ "spgist",
1335
+ "brin"
1336
+ ], currentWord, ctx.settings) };
1337
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_index_access_methods, currentWord) };
1338
+ }
1339
+ if (TailMatches(prevWords, ["CREATE"])) return { candidates: filterAndCase(CREATE_OBJECTS, currentWord, ctx.settings) };
1340
+ if (TailMatches(prevWords, ["CREATE", "TABLE"])) return completeTables();
1341
+ if (HeadMatches(prevWords, ["CREATE", "TABLE"]) && TailMatches(prevWords, ["(|,", null])) {
1342
+ if (!conn) return { candidates: [] };
1343
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_datatypes, currentWord) };
1344
+ }
1345
+ if (TailMatches(prevWords, [
1346
+ "CREATE",
1347
+ "OR",
1348
+ "REPLACE"
1349
+ ])) return { candidates: filterAndCase([
1350
+ "FUNCTION",
1351
+ "PROCEDURE",
1352
+ "VIEW",
1353
+ "TRIGGER",
1354
+ "AGGREGATE",
1355
+ "TRANSFORM"
1356
+ ], currentWord, ctx.settings) };
1357
+ if (TailMatches(prevWords, ["TRUNCATE"])) return { candidates: [...filterAndCase(["TABLE", "ONLY"], currentWord, ctx.settings), ...await tableCandidates(conn, currentWord)] };
1358
+ if (TailMatches(prevWords, ["TRUNCATE", "TABLE"])) return completeTables();
1359
+ if (TailMatches(prevWords, ["TRUNCATE", "ONLY"])) return completeTables();
1360
+ if (HeadMatches(prevWords, ["COPY|\\copy"]) && HeadMatches(prevWords, [
1361
+ null,
1362
+ null,
1363
+ "FROM"
1364
+ ]) && (TailMatches(prevWords, ["("]) || TailMatches(prevWords, [","]))) return { candidates: filterAndCase(COPY_FROM_OPTIONS, currentWord, ctx.settings) };
1365
+ if (HeadMatches(prevWords, ["COPY|\\copy"]) && HeadMatches(prevWords, [
1366
+ null,
1367
+ null,
1368
+ "TO"
1369
+ ]) && (TailMatches(prevWords, ["("]) || TailMatches(prevWords, [","]))) return { candidates: filterAndCase(COPY_TO_OPTIONS, currentWord, ctx.settings) };
1370
+ if (TailMatches(prevWords, ["COPY"])) return completeTables();
1371
+ if (TailMatches(prevWords, ["COPY", null])) return { candidates: filterAndCase(["FROM", "TO"], currentWord, ctx.settings) };
1372
+ if (TailMatches(prevWords, ["ANALYZE"]) || TailMatches(prevWords, ["ANALYZE", "VERBOSE"])) return completeTables();
1373
+ if (TailMatches(prevWords, ["VACUUM"]) || TailMatches(prevWords, ["VACUUM", "VERBOSE"])) return completeTables();
1374
+ if (TailMatches(prevWords, ["VACUUM", "FULL"])) return completeTables();
1375
+ if (TailMatches(prevWords, ["VACUUM", "ANALYZE"])) return completeTables();
1376
+ if (TailMatches(prevWords, ["REINDEX"])) return { candidates: filterAndCase([
1377
+ "TABLE",
1378
+ "INDEX",
1379
+ "SCHEMA",
1380
+ "DATABASE",
1381
+ "SYSTEM",
1382
+ "CONCURRENTLY"
1383
+ ], currentWord, ctx.settings) };
1384
+ if (TailMatches(prevWords, ["REINDEX", "TABLE"])) return completeTables();
1385
+ if (TailMatches(prevWords, ["REINDEX", "INDEX"])) return completeTables(Query_for_list_of_indexes);
1386
+ if (TailMatches(prevWords, ["REINDEX", "SCHEMA"])) {
1387
+ if (!conn) return { candidates: [] };
1388
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord) };
1389
+ }
1390
+ if (TailMatches(prevWords, ["REINDEX", "DATABASE"])) {
1391
+ if (!conn) return { candidates: [] };
1392
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_databases, currentWord) };
1393
+ }
1394
+ if (TailMatches(prevWords, ["GRANT"]) || TailMatches(prevWords, ["REVOKE"])) return { candidates: filterAndCase(PRIVILEGE_KEYWORDS, currentWord, ctx.settings) };
1395
+ if (TailMatches(prevWords, ["ON"])) return completeTables();
1396
+ if (TailMatches(prevWords, ["TO"])) {
1397
+ if (HeadMatches(prevWords, ["GRANT"]) && conn) return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
1398
+ }
1399
+ if (TailMatches(prevWords, ["FROM"]) && HeadMatches(prevWords, ["REVOKE"])) {
1400
+ if (!conn) return { candidates: [] };
1401
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
1402
+ }
1403
+ if (TailMatches(prevWords, ["LOCK"])) return { candidates: [...filterAndCase(["TABLE"], currentWord, ctx.settings), ...await tableCandidates(conn, currentWord)] };
1404
+ if (TailMatches(prevWords, ["LOCK", "TABLE"])) return completeTables();
1405
+ if (TailMatches(prevWords, ["SET", "ROLE"])) {
1406
+ if (!conn) return { candidates: [] };
1407
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_roles, currentWord) };
1408
+ }
1409
+ if (TailMatches(prevWords, ["SET", "SCHEMA"])) {
1410
+ if (!conn) return { candidates: [] };
1411
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_schemas, currentWord) };
1412
+ }
1413
+ if (TailMatches(prevWords, ["SET", "SESSION"])) return { candidates: filterAndCase(["AUTHORIZATION", "CHARACTERISTICS AS TRANSACTION"], currentWord, ctx.settings) };
1414
+ if (TailMatches(prevWords, ["SET", "TRANSACTION"])) return { candidates: filterAndCase([
1415
+ "ISOLATION LEVEL",
1416
+ "READ ONLY",
1417
+ "READ WRITE",
1418
+ "DEFERRABLE"
1419
+ ], currentWord, ctx.settings) };
1420
+ if (TailMatches(prevWords, [
1421
+ "SET",
1422
+ "DateStyle",
1423
+ "TO|="
1424
+ ]) || TailMatches(prevWords, [
1425
+ "SET",
1426
+ "DATESTYLE",
1427
+ "TO|="
1428
+ ]) || TailMatches(prevWords, [
1429
+ "SET",
1430
+ "datestyle",
1431
+ "TO|="
1432
+ ])) return { candidates: filterAndCase(DATESTYLE_VALUES, currentWord, ctx.settings) };
1433
+ if (TailMatches(prevWords, [
1434
+ "SET",
1435
+ "timezone",
1436
+ "TO|="
1437
+ ]) || TailMatches(prevWords, [
1438
+ "SET",
1439
+ "TIMEZONE",
1440
+ "TO|="
1441
+ ]) || TailMatches(prevWords, [
1442
+ "SET",
1443
+ "TimeZone",
1444
+ "TO|="
1445
+ ])) {
1446
+ if (!conn) return { candidates: [] };
1447
+ const query = currentWord.startsWith("'") ? Query_for_list_of_timezone_names_quoted_in : Query_for_list_of_timezone_names_quoted_out;
1448
+ return { candidates: await runCatalogQuery(conn, query, currentWord) };
1449
+ }
1450
+ if (TailMatches(prevWords, [
1451
+ "SET",
1452
+ null,
1453
+ "TO|="
1454
+ ])) {
1455
+ if (!conn) return { candidates: [] };
1456
+ const gucName = prevWords[prevWords.length - 2];
1457
+ return { candidates: await runCatalogQuery(conn, Query_for_values_of_enum_GUC, currentWord, [gucName]) };
1458
+ }
1459
+ if (TailMatches(prevWords, ["SET", null])) return { candidates: filterAndCase(["TO"], currentWord, ctx.settings) };
1460
+ if (TailMatches(prevWords, ["SET"])) {
1461
+ const staticKw = filterAndCase([
1462
+ "CONSTRAINTS",
1463
+ "LOCAL",
1464
+ "ROLE",
1465
+ "SCHEMA",
1466
+ "SESSION",
1467
+ "TIME ZONE",
1468
+ "TRANSACTION"
1469
+ ], currentWord, ctx.settings);
1470
+ if (!conn) return { candidates: staticKw };
1471
+ const guc = await runCatalogQuery(conn, Query_for_list_of_set_vars, currentWord);
1472
+ return { candidates: [...staticKw, ...guc] };
1473
+ }
1474
+ if (TailMatches(prevWords, ["SHOW"])) {
1475
+ const staticKw = filterAndCase(["ALL"], currentWord, ctx.settings);
1476
+ if (!conn) return { candidates: [...staticKw, ...filterAndCase([
1477
+ "search_path",
1478
+ "role",
1479
+ "session_authorization",
1480
+ "transaction_isolation",
1481
+ "client_encoding",
1482
+ "server_encoding",
1483
+ "server_version",
1484
+ "timezone"
1485
+ ], currentWord, ctx.settings)] };
1486
+ const guc = await runCatalogQuery(conn, Query_for_list_of_set_vars, currentWord);
1487
+ return { candidates: [...staticKw, ...guc] };
1488
+ }
1489
+ if (TailMatches(prevWords, ["RESET"])) {
1490
+ const staticKw = filterAndCase([
1491
+ "ALL",
1492
+ "SESSION AUTHORIZATION",
1493
+ "ROLE"
1494
+ ], currentWord, ctx.settings);
1495
+ if (!conn) return { candidates: staticKw };
1496
+ const guc = await runCatalogQuery(conn, Query_for_list_of_set_vars, currentWord);
1497
+ return { candidates: [...staticKw, ...guc] };
1498
+ }
1499
+ if (TailMatches(prevWords, ["BEGIN"]) || TailMatches(prevWords, ["START"])) return { candidates: filterAndCase(TRANSACTION_KEYWORDS, currentWord, ctx.settings) };
1500
+ if (TailMatches(prevWords, ["ROLLBACK"])) return { candidates: filterAndCase([
1501
+ "TO SAVEPOINT",
1502
+ "TRANSACTION",
1503
+ "AND CHAIN",
1504
+ "AND NO CHAIN"
1505
+ ], currentWord, ctx.settings) };
1506
+ if (TailMatches(prevWords, ["EXPLAIN"])) return { candidates: filterAndCase([
1507
+ "ANALYZE",
1508
+ "VERBOSE",
1509
+ "SELECT",
1510
+ "INSERT INTO",
1511
+ "UPDATE",
1512
+ "DELETE FROM",
1513
+ "("
1514
+ ], currentWord, ctx.settings) };
1515
+ if (TailMatches(prevWords, ["DECLARE"])) return { candidates: [] };
1516
+ if (TailMatches(prevWords, ["CALL"])) {
1517
+ if (!conn) return { candidates: [] };
1518
+ return { candidates: await runCatalogQuery(conn, Query_for_list_of_functions, currentWord) };
1519
+ }
1520
+ if (TailMatches(prevWords, ["SELECT"])) return { candidates: filterAndCase([
1521
+ "DISTINCT",
1522
+ "ALL",
1523
+ "*",
1524
+ "CURRENT_DATE",
1525
+ "CURRENT_TIME",
1526
+ "CURRENT_TIMESTAMP",
1527
+ "CURRENT_USER",
1528
+ "NULL",
1529
+ "TRUE",
1530
+ "FALSE"
1531
+ ], currentWord, ctx.settings) };
1532
+ if (TailMatches(prevWords, ["SELECT", "*"])) return { candidates: filterAndCase(["FROM"], currentWord, ctx.settings) };
1533
+ return { candidates: [] };
2307
1534
  };
2308
- // ---------------------------------------------------------------------------
2309
- // Helpers.
2310
- // ---------------------------------------------------------------------------
2311
1535
  /** Candidates for table-name completion, no schema-qualifier handling. */
2312
1536
  const tableCandidates = async (conn, word) => {
2313
- if (!conn)
2314
- return [];
2315
- return runCatalogQuery(conn, Query_for_list_of_tables, word);
1537
+ if (!conn) return [];
1538
+ return runCatalogQuery(conn, Query_for_list_of_tables, word);
2316
1539
  };
2317
1540
  /**
2318
- * Schema-aware relation completion. If the user typed `schema.x`, fetch
2319
- * relations in `schema` matching `x`. Otherwise fetch unqualified relations
2320
- * AND a list of schemas (so the user can dot through to relations in any
2321
- * schema, mirroring upstream's `complete_from_schema_query`).
2322
- *
2323
- * Schema names are returned with a trailing `.` so the completion engine
2324
- * recognizes them as "in progress" and refrains from appending a space.
2325
- * When a unique candidate is a schema (e.g. `pub<tab>` → `public.`) the
2326
- * user can immediately Tab again to list relations within that schema.
2327
- *
2328
- * Quoted identifier handling: if the user's input starts with `"`, we
2329
- * search the catalog case-sensitively (stripping the opening quote) and
2330
- * return candidates pre-quoted with `"..."` so the line ends up in a
2331
- * valid quoted form — matching upstream readline's behaviour for
2332
- * `select * from "my<tab>` → `"mytab123 "mytab246`.
2333
- */
1541
+ * Schema-aware relation completion. If the user typed `schema.x`, fetch
1542
+ * relations in `schema` matching `x`. Otherwise fetch unqualified relations
1543
+ * AND a list of schemas (so the user can dot through to relations in any
1544
+ * schema, mirroring upstream's `complete_from_schema_query`).
1545
+ *
1546
+ * Schema names are returned with a trailing `.` so the completion engine
1547
+ * recognizes them as "in progress" and refrains from appending a space.
1548
+ * When a unique candidate is a schema (e.g. `pub<tab>` → `public.`) the
1549
+ * user can immediately Tab again to list relations within that schema.
1550
+ *
1551
+ * Quoted identifier handling: if the user's input starts with `"`, we
1552
+ * search the catalog case-sensitively (stripping the opening quote) and
1553
+ * return candidates pre-quoted with `"..."` so the line ends up in a
1554
+ * valid quoted form — matching upstream readline's behaviour for
1555
+ * `select * from "my<tab>` → `"mytab123 "mytab246`.
1556
+ */
2334
1557
  const completeSchemaOrRelations = async (conn, word, query) => {
2335
- if (word.startsWith('"')) {
2336
- return completeQuotedRelations(conn, word, query);
2337
- }
2338
- const split = splitSchemaPrefix(word);
2339
- if (split) {
2340
- const rows = await runCatalogQuery(conn, Query_for_list_of_relations_in_schema, split.prefix, [split.schema]);
2341
- // Re-prefix with the catalog's canonical schema name. Upstream folds
2342
- // unquoted schema identifiers to lowercase in the output (mirroring
2343
- // the way Postgres downcases unquoted names), so `PUBLIC.t<tab>`
2344
- // completes to `public.tab1` instead of preserving `PUBLIC` in the
2345
- // returned candidate.
2346
- const canonicalSchema = split.schemaWasQuoted
2347
- ? split.schema
2348
- : split.schema.toLowerCase();
2349
- return rows.map((r) => canonicalSchema + "." + r);
2350
- }
2351
- // Unqualified: combine relations + schemas (schemas suffixed with `.`).
2352
- const [rels, schemas] = await Promise.all([
2353
- runCatalogQuery(conn, query, word),
2354
- runCatalogQuery(conn, Query_for_list_of_schemas, word),
2355
- ]);
2356
- return [...rels, ...schemas.map((s) => s + ".")];
1558
+ if (word.startsWith("\"")) return completeQuotedRelations(conn, word, query);
1559
+ const split = splitSchemaPrefix(word);
1560
+ if (split) {
1561
+ const rows = await runCatalogQuery(conn, Query_for_list_of_relations_in_schema, split.prefix, [split.schema]);
1562
+ const canonicalSchema = split.schemaWasQuoted ? split.schema : split.schema.toLowerCase();
1563
+ return rows.map((r) => canonicalSchema + "." + r);
1564
+ }
1565
+ const [rels, schemas] = await Promise.all([runCatalogQuery(conn, query, word), runCatalogQuery(conn, Query_for_list_of_schemas, word)]);
1566
+ return [...rels, ...schemas.map((s) => s + ".")];
2357
1567
  };
2358
1568
  /**
2359
- * Search the catalog for relations matching a user-supplied prefix that
2360
- * begins with `"`. We strip the opening quote, search the RAW relname
2361
- * case-sensitively (so `"mi` → `mixedName` rather than `mytab123`), and
2362
- * emit results wrapped in `"..."` with a closing quote so the editor's
2363
- * unique-completion handler appends the trailing space outside the
2364
- * quoted region (e.g. `"mixedName" `).
2365
- */
1569
+ * Search the catalog for relations matching a user-supplied prefix that
1570
+ * begins with `"`. We strip the opening quote, search the RAW relname
1571
+ * case-sensitively (so `"mi` → `mixedName` rather than `mytab123`), and
1572
+ * emit results wrapped in `"..."` with a closing quote so the editor's
1573
+ * unique-completion handler appends the trailing space outside the
1574
+ * quoted region (e.g. `"mixedName" `).
1575
+ */
2366
1576
  const completeQuotedRelations = async (conn, word, query) => {
2367
- // Strip leading `"` (and any trailing `"` the user pre-typed).
2368
- const inside = word.slice(1).replace(/"$/, "");
2369
- // Use a case-sensitive raw-relname LIKE — the inside-the-quote portion
2370
- // is taken verbatim.
2371
- const sql = caseSensitiveRelnameVariant(query);
2372
- if (!sql)
2373
- return [];
2374
- const rows = await runCatalogQuery(conn, sql, inside);
2375
- return rows.map((name) => '"' + name + '"');
1577
+ const inside = word.slice(1).replace(/"$/, "");
1578
+ const sql = caseSensitiveRelnameVariant(query);
1579
+ if (!sql) return [];
1580
+ return (await runCatalogQuery(conn, sql, inside)).map((name) => "\"" + name + "\"");
2376
1581
  };
2377
1582
  /**
2378
- * Build a case-sensitive variant of a relation-list query for quoted
2379
- * input. The standard queries use `ILIKE` on `quote_ident(c.relname)`
2380
- * (which obscures the original case for identifiers like `mixedName`);
2381
- * we swap that for `LIKE` on the raw `c.relname` and return the raw
2382
- * relname so we control the quoting.
2383
- *
2384
- * Returns null if the query isn't a recognised relation query
2385
- * (defensive — keeps the quoted-completion code path bounded).
2386
- */
1583
+ * Build a case-sensitive variant of a relation-list query for quoted
1584
+ * input. The standard queries use `ILIKE` on `quote_ident(c.relname)`
1585
+ * (which obscures the original case for identifiers like `mixedName`);
1586
+ * we swap that for `LIKE` on the raw `c.relname` and return the raw
1587
+ * relname so we control the quoting.
1588
+ *
1589
+ * Returns null if the query isn't a recognised relation query
1590
+ * (defensive — keeps the quoted-completion code path bounded).
1591
+ */
2387
1592
  const caseSensitiveRelnameVariant = (sql) => {
2388
- // We rewrite the SELECT/WHERE clauses on the standard relation queries.
2389
- // The only shape we need to support is `c.relname ILIKE $1` against
2390
- // `pg_catalog.pg_class c`, with various `c.relkind IN (...)` filters.
2391
- if (!sql.includes("pg_catalog.pg_class c"))
2392
- return null;
2393
- if (!sql.includes("c.relname ILIKE $1"))
2394
- return null;
2395
- return sql
2396
- .replace("SELECT pg_catalog.quote_ident(c.relname)", "SELECT c.relname")
2397
- .replace("c.relname ILIKE $1", "c.relname LIKE $1");
1593
+ if (!sql.includes("pg_catalog.pg_class c")) return null;
1594
+ if (!sql.includes("c.relname ILIKE $1")) return null;
1595
+ return sql.replace("SELECT pg_catalog.quote_ident(c.relname)", "SELECT c.relname").replace("c.relname ILIKE $1", "c.relname LIKE $1");
2398
1596
  };
2399
1597
  /** Case-insensitive prefix filter; preserves the candidate's original casing. */
2400
1598
  const filterCi = (candidates, prefix) => {
2401
- if (prefix.length === 0)
2402
- return candidates.slice();
2403
- const lp = prefix.toLowerCase();
2404
- return candidates.filter((c) => c.toLowerCase().startsWith(lp));
1599
+ if (prefix.length === 0) return candidates.slice();
1600
+ const lp = prefix.toLowerCase();
1601
+ return candidates.filter((c) => c.toLowerCase().startsWith(lp));
2405
1602
  };
2406
1603
  /** Names of variables currently SET on the settings (for `\unset` etc). */
2407
1604
  const listVarNames = (settings) => {
2408
- const out = [];
2409
- for (const [name] of settings.vars.entries())
2410
- out.push(name);
2411
- return out.sort();
1605
+ const out = [];
1606
+ for (const [name] of settings.vars.entries()) out.push(name);
1607
+ return out.sort();
2412
1608
  };
2413
1609
  /** Both currently-set variables AND special-variable names. */
2414
1610
  const listAllVarNames = (settings) => {
2415
- const set = new Set();
2416
- for (const [name] of settings.vars.entries())
2417
- set.add(name);
2418
- for (const n of SPECIAL_VARIABLES)
2419
- set.add(n);
2420
- return [...set].sort();
1611
+ const set = /* @__PURE__ */ new Set();
1612
+ for (const [name] of settings.vars.entries()) set.add(name);
1613
+ for (const n of SPECIAL_VARIABLES) set.add(n);
1614
+ return [...set].sort();
2421
1615
  };
2422
- // Re-exported so the index entrypoint can implement its own splitting helper
2423
- // using the same prefix logic.
2424
- export const _internals = { splitSchemaPrefix };
1616
+ const _internals = { splitSchemaPrefix };
1617
+ //#endregion
1618
+ export { ALTER_DATABASE_ACTIONS, ALTER_EXTENSION_ACTIONS, ALTER_FUNCTION_ACTIONS, ALTER_INDEX_ACTIONS, ALTER_MATVIEW_ACTIONS, ALTER_OBJECTS, ALTER_POLICY_ACTIONS, ALTER_PUBLICATION_ACTIONS, ALTER_ROLE_ACTIONS, ALTER_SCHEMA_ACTIONS, ALTER_SEQUENCE_ACTIONS, ALTER_SUBSCRIPTION_ACTIONS, ALTER_TABLE_ACTIONS, ALTER_TABLE_ADD, ALTER_TABLE_ALTER_COLUMN, ALTER_TABLE_DISABLE, ALTER_TABLE_DROP, ALTER_TABLE_ENABLE, ALTER_TABLE_RENAME, ALTER_TABLE_REPLICA_IDENTITY, ALTER_TABLE_SET, ALTER_TYPE_ACTIONS, ALTER_VIEW_ACTIONS, BACKSLASH_COMMANDS, BUILTIN_DATATYPE_KEYWORDS, COPY_FROM_OPTIONS, COPY_TO_OPTIONS, CREATE_INDEX_OPTIONS, CREATE_OBJECTS, DATESTYLE_VALUES, DROP_OBJECTS, POST_FROM_KEYWORDS, PRIVILEGE_KEYWORDS, SQL_TOP_KEYWORDS, TRANSACTION_KEYWORDS, WHERE_CONTINUATIONS, WINDOW_FRAME_KEYWORDS, _internals, findCompletions };