neon 3.0.0 → 3.1.1
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.
- package/README.md +70 -5
- package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
- package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
- package/dist/_chunks/credentials-MYdHdKah.js +188 -0
- package/dist/_chunks/env-NbA61JR3.js +585 -0
- package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
- package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
- package/dist/_chunks/profiles-Ir29rqns.js +217 -0
- package/dist/_chunks/psql-DWH-kc69.js +2169 -0
- package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +573 -690
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +639 -719
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +255 -305
- package/dist/commands/functions.js +275 -355
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +615 -846
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +2 -446
- package/dist/dev/functions.js +54 -68
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +2 -51
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +728 -984
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +2 -2193
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +2 -1148
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +260 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +2 -1253
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1303
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +2 -846
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +2 -2030
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +8 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +130 -189
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +39 -45
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- package/package.json +11 -6
- package/dist/_shared/auth_selection.js +0 -86
- package/dist/_shared/credentials.js +0 -209
- package/dist/_shared/env-core/env.js +0 -558
- package/dist/_shared/env-core/reuse-secrets.js +0 -223
- package/dist/_shared/paths.js +0 -148
- package/dist/_shared/profiles.js +0 -276
- package/dist/_shared/secure_file.js +0 -43
|
@@ -1,43 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
* 1. `defaultSettings(varStore)` — build a fully-initialized `PsqlSettings`
|
|
10
|
-
* with the same defaults psql itself uses (prompts, verbosity, send mode,
|
|
11
|
-
* completion case, print options, etc.). The Connection (`settings.db`)
|
|
12
|
-
* stays `null` here; it is populated later by the startup/connect WP.
|
|
13
|
-
*
|
|
14
|
-
* 2. `applyEnvOverrides(settings, env?)` — bridge process environment to psql
|
|
15
|
-
* state. Some variables psql looks up "live" (e.g. PSQL_HISTORY when
|
|
16
|
-
* readline is initialized in `src/bin/psql/input.c`, PSQL_EDITOR/EDITOR/
|
|
17
|
-
* VISUAL when `\e` is invoked in `command.c`, PAGER/PSQL_PAGER from
|
|
18
|
-
* `fe_utils/print.c`). We capture them eagerly into the var store under
|
|
19
|
-
* stable names so later WPs can read them without re-reading
|
|
20
|
-
* `process.env`. The COLUMNS env var maps to `popt.topt.envColumns` to
|
|
21
|
-
* mirror upstream's `pset.popt.topt.env_columns` assignment.
|
|
22
|
-
*
|
|
23
|
-
* Deviations from upstream:
|
|
24
|
-
*
|
|
25
|
-
* - psql exposes `notty` based on isatty(stdin) && isatty(stdout). We have
|
|
26
|
-
* no stream context at construction time, so the default is `false`;
|
|
27
|
-
* higher-level WPs (startup) override this.
|
|
28
|
-
* - The PROMPT1/PROMPT2/PROMPT3 psql variables and `settings.prompt{1,2,3}`
|
|
29
|
-
* are seeded to the same defaults and kept in sync via `addHook` so
|
|
30
|
-
* `\set PROMPT1 …` reflects in `settings.prompt1`. Upstream achieves this
|
|
31
|
-
* with `prompt1_hook` etc. in `startup.c`.
|
|
32
|
-
* - We do not eagerly read PSQLRC (the rc loader lives in a later WP).
|
|
33
|
-
* Instead we surface PSQLRC verbatim under the `PSQLRC` psql variable.
|
|
34
|
-
*/
|
|
35
|
-
export const DEFAULT_PROMPT1 = "%/%R%x%# ";
|
|
36
|
-
export const DEFAULT_PROMPT2 = "%/%R%x%# ";
|
|
37
|
-
export const DEFAULT_PROMPT3 = ">> ";
|
|
38
|
-
export const DEFAULT_CSV_FIELD_SEP = ",";
|
|
39
|
-
export const DEFAULT_FIELD_SEP = "|";
|
|
40
|
-
export const DEFAULT_RECORD_SEP = "\n";
|
|
1
|
+
//#region src/psql/core/settings.ts
|
|
2
|
+
const DEFAULT_PROMPT1 = "%/%R%x%# ";
|
|
3
|
+
const DEFAULT_PROMPT2 = "%/%R%x%# ";
|
|
4
|
+
const DEFAULT_PROMPT3 = ">> ";
|
|
5
|
+
const DEFAULT_CSV_FIELD_SEP = ",";
|
|
6
|
+
const DEFAULT_FIELD_SEP = "|";
|
|
7
|
+
const DEFAULT_RECORD_SEP = "\n";
|
|
41
8
|
const DEFAULT_VERBOSITY = "default";
|
|
42
9
|
const DEFAULT_SHOW_CONTEXT = "errors";
|
|
43
10
|
const DEFAULT_ECHO = "none";
|
|
@@ -49,638 +16,456 @@ const DEFAULT_HIST_CONTROL = "none";
|
|
|
49
16
|
/** Upstream `DEFAULT_WATCH_INTERVAL` from settings.h (psql 18). */
|
|
50
17
|
const DEFAULT_WATCH_INTERVAL = "2";
|
|
51
18
|
/** Upstream `DEFAULT_WATCH_INTERVAL_MAX` (1e6 seconds). */
|
|
52
|
-
const WATCH_INTERVAL_MAX =
|
|
19
|
+
const WATCH_INTERVAL_MAX = 1e6;
|
|
53
20
|
/**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
21
|
+
* Build the psql `PrintQueryOpts` with the same defaults as upstream's
|
|
22
|
+
* pset.popt initialization (see `startup.c` after the `pset.db = NULL`
|
|
23
|
+
* block). border=1, format=aligned, pager="on" (upstream
|
|
24
|
+
* `pset.popt.topt.pager = 1` — the printer paginates only on a TTY and
|
|
25
|
+
* only when output exceeds the screen height, so the default is harmless
|
|
26
|
+
* for non-TTY runs), start/stop_table=true, default_footer=true.
|
|
27
|
+
*/
|
|
61
28
|
const buildDefaultPrintOpts = () => ({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
nullPrint: "",
|
|
103
|
-
title: null,
|
|
104
|
-
footers: null,
|
|
105
|
-
translateHeader: false,
|
|
106
|
-
translateColumns: null,
|
|
107
|
-
nTranslateColumns: 0,
|
|
29
|
+
topt: {
|
|
30
|
+
format: "aligned",
|
|
31
|
+
expanded: "off",
|
|
32
|
+
border: 1,
|
|
33
|
+
pager: "on",
|
|
34
|
+
pagerMinLines: 0,
|
|
35
|
+
tuplesOnly: false,
|
|
36
|
+
startTable: true,
|
|
37
|
+
stopTable: true,
|
|
38
|
+
defaultFooter: true,
|
|
39
|
+
prior: 0,
|
|
40
|
+
encoding: "UTF8",
|
|
41
|
+
envColumns: 0,
|
|
42
|
+
columns: 0,
|
|
43
|
+
unicodeBorderLineStyle: "ascii",
|
|
44
|
+
unicodeColumnLineStyle: "ascii",
|
|
45
|
+
unicodeHeaderLineStyle: "ascii",
|
|
46
|
+
unicodeBorderStyle: "single",
|
|
47
|
+
unicodeColumnStyle: "single",
|
|
48
|
+
unicodeHeaderStyle: "single",
|
|
49
|
+
fieldSep: "|",
|
|
50
|
+
recordSep: "\n",
|
|
51
|
+
numericLocale: false,
|
|
52
|
+
tableAttr: null,
|
|
53
|
+
title: null,
|
|
54
|
+
footers: null,
|
|
55
|
+
translateHeader: false,
|
|
56
|
+
translateColumns: null,
|
|
57
|
+
nullPrint: "",
|
|
58
|
+
truePrint: "t",
|
|
59
|
+
falsePrint: "f",
|
|
60
|
+
csvFieldSep: ",",
|
|
61
|
+
xheaderWidth: "full"
|
|
62
|
+
},
|
|
63
|
+
nullPrint: "",
|
|
64
|
+
title: null,
|
|
65
|
+
footers: null,
|
|
66
|
+
translateHeader: false,
|
|
67
|
+
translateColumns: null,
|
|
68
|
+
nTranslateColumns: 0
|
|
108
69
|
});
|
|
109
70
|
/**
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
settings.showContext = parsed;
|
|
271
|
-
}));
|
|
272
|
-
// ECHO — upstream `echo_substitute_hook` (empty → "none") +
|
|
273
|
-
// `echo_assign_hook`. Accepts none | errors | queries | all.
|
|
274
|
-
varStore.addHook("ECHO", makeEnumHook("ECHO", ["none", "errors", "queries", "all"], "none", (parsed) => {
|
|
275
|
-
settings.echo = parsed;
|
|
276
|
-
}));
|
|
277
|
-
// ECHO_HIDDEN — upstream `bool_substitute_hook` +
|
|
278
|
-
// `echo_hidden_assign_hook`. Tri-state: on / off / noexec. Empty → "on".
|
|
279
|
-
varStore.addHook("ECHO_HIDDEN", makeEchoHiddenHook((parsed) => {
|
|
280
|
-
settings.echoHidden = parsed;
|
|
281
|
-
}));
|
|
282
|
-
// COMP_KEYWORD_CASE assign hook. Upstream `assign_var_comp_keyword_case_hook`
|
|
283
|
-
// (in startup.c) reflects the spelling into `pset.comp_case`; the completer
|
|
284
|
-
// then consults that on every Tab to decide whether to upper/lower/preserve
|
|
285
|
-
// candidate casing. Accepts the four canonical spellings; the upstream
|
|
286
|
-
// diagnostic wording is `unrecognized value "<value>" for
|
|
287
|
-
// "COMP_KEYWORD_CASE"\nAvailable values are: lower, upper, preserve-lower,
|
|
288
|
-
// preserve-upper.`.
|
|
289
|
-
varStore.addHook("COMP_KEYWORD_CASE", makeEnumHook("COMP_KEYWORD_CASE", ["lower", "upper", "preserve-lower", "preserve-upper"], DEFAULT_COMP_CASE, (parsed) => {
|
|
290
|
-
settings.compCase = parsed;
|
|
291
|
-
}));
|
|
292
|
-
// HISTCONTROL — upstream `histcontrol_substitute_hook` (empty → "none") +
|
|
293
|
-
// `histcontrol_assign_hook`. Accepts ignorespace | ignoredups | ignoreboth
|
|
294
|
-
// | none.
|
|
295
|
-
varStore.addHook("HISTCONTROL", makeEnumHook("HISTCONTROL", ["none", "ignorespace", "ignoredups", "ignoreboth"], "none", (parsed) => {
|
|
296
|
-
settings.histControl = parsed;
|
|
297
|
-
}));
|
|
298
|
-
// SHOW_ALL_RESULTS — upstream `bool_substitute_hook` +
|
|
299
|
-
// `show_all_results_assign_hook`. Strict boolean; empty → "on".
|
|
300
|
-
varStore.addHook("SHOW_ALL_RESULTS", makeBoolHook("SHOW_ALL_RESULTS"));
|
|
301
|
-
// QUIET — upstream `bool_substitute_hook` + `quiet_hook`. Mirrors into
|
|
302
|
-
// `pset.quiet`. Default ("off") matches the unset substitute, so addHook
|
|
303
|
-
// registration seeds it via the substitute.
|
|
304
|
-
varStore.addHook("QUIET", makeBoolHook("QUIET", (parsed) => {
|
|
305
|
-
settings.quiet = parsed;
|
|
306
|
-
}));
|
|
307
|
-
// SINGLELINE — upstream `bool_substitute_hook` + `singleline_hook`.
|
|
308
|
-
// Mirrors into `pset.singleline`.
|
|
309
|
-
varStore.addHook("SINGLELINE", makeBoolHook("SINGLELINE", (parsed) => {
|
|
310
|
-
settings.singleline = parsed;
|
|
311
|
-
}));
|
|
312
|
-
// SINGLESTEP — upstream `bool_substitute_hook` + `singlestep_hook`.
|
|
313
|
-
// Mirrors into `pset.singlestep`.
|
|
314
|
-
varStore.addHook("SINGLESTEP", makeBoolHook("SINGLESTEP", (parsed) => {
|
|
315
|
-
settings.singlestep = parsed;
|
|
316
|
-
}));
|
|
317
|
-
// HIDE_TOAST_COMPRESSION — upstream `bool_substitute_hook` +
|
|
318
|
-
// `hide_compression_hook`. Mirrors into `pset.hide_compression` (our
|
|
319
|
-
// `settings.hideCompression`). Note: the upstream variable name is
|
|
320
|
-
// HIDE_TOAST_COMPRESSION, not HIDE_COMPRESSION.
|
|
321
|
-
varStore.addHook("HIDE_TOAST_COMPRESSION", makeBoolHook("HIDE_TOAST_COMPRESSION", (parsed) => {
|
|
322
|
-
settings.hideCompression = parsed;
|
|
323
|
-
}));
|
|
324
|
-
// HIDE_TABLEAM — upstream `bool_substitute_hook` + `hide_tableam_hook`.
|
|
325
|
-
// Mirrors into `pset.hide_tableam` (our `settings.hideTableam`).
|
|
326
|
-
varStore.addHook("HIDE_TABLEAM", makeBoolHook("HIDE_TABLEAM", (parsed) => {
|
|
327
|
-
settings.hideTableam = parsed;
|
|
328
|
-
}));
|
|
329
|
-
// HISTSIZE — upstream `histsize_substitute_hook` (null → "500") +
|
|
330
|
-
// `histsize_hook` which calls `ParseVariableNum`. Empty `\set HISTSIZE`
|
|
331
|
-
// reaches ParseVariableNum and fails with the "integer expected" message,
|
|
332
|
-
// preserving the prior value. We don't drive a derived settings field
|
|
333
|
-
// (readline isn't implemented), but we still install the hook so the
|
|
334
|
-
// variable surface matches vanilla: `\echo :HISTSIZE` prints "500" on
|
|
335
|
-
// startup, accepts integer values, rejects junk.
|
|
336
|
-
varStore.addHook("HISTSIZE", (newValue) => {
|
|
337
|
-
if (newValue === null) {
|
|
338
|
-
return { substitute: "500" };
|
|
339
|
-
}
|
|
340
|
-
const n = parseIntOrNull(newValue);
|
|
341
|
-
if (n === null) {
|
|
342
|
-
return `invalid value "${newValue}" for "HISTSIZE": integer expected`;
|
|
343
|
-
}
|
|
344
|
-
return true;
|
|
345
|
-
});
|
|
346
|
-
// IGNOREEOF — upstream `ignoreeof_substitute_hook` + `ignoreeof_hook`.
|
|
347
|
-
// The substitute hook is unusual: it silently rewrites non-numeric values
|
|
348
|
-
// to "10" (bash's documented default) instead of rejecting them, and maps
|
|
349
|
-
// null → "0" (feature disabled). The assign hook never fails in practice
|
|
350
|
-
// because the substitute already normalized the value — it's
|
|
351
|
-
// `ParseVariableNum(newval, "IGNOREEOF", &pset.ignoreeof)` over a value
|
|
352
|
-
// guaranteed to parse. We have no derived `settings.ignoreeof` field (the
|
|
353
|
-
// EOF-counting logic lives in the REPL, which isn't implemented), so the
|
|
354
|
-
// hook just maintains the variable surface: `\echo :IGNOREEOF` prints "0"
|
|
355
|
-
// on startup, accepts integers (incl. hex / octal — `ParseVariableNum`
|
|
356
|
-
// uses `strtol(..., 0)`), silently rewrites junk to "10", and on `\unset`
|
|
357
|
-
// resubstitutes "0". This is the key behavior the regress/psql `\gset
|
|
358
|
-
// IGNORE` test relies on (it expects `:IGNOREEOF` to render "0" even
|
|
359
|
-
// though `\gset` rejected the assignment as specially-treated).
|
|
360
|
-
varStore.addHook("IGNOREEOF", (newValue) => {
|
|
361
|
-
if (newValue === null) {
|
|
362
|
-
return { substitute: "0" };
|
|
363
|
-
}
|
|
364
|
-
if (parseStrtolBase0OrNull(newValue) === null) {
|
|
365
|
-
return { substitute: "10" };
|
|
366
|
-
}
|
|
367
|
-
return true;
|
|
368
|
-
});
|
|
369
|
-
return settings;
|
|
71
|
+
* Build a fresh `PsqlSettings` with psql defaults, backed by the provided
|
|
72
|
+
* variable store. The store is mutated to:
|
|
73
|
+
*
|
|
74
|
+
* - seed `PROMPT1`/`PROMPT2`/`PROMPT3` to their default strings, and
|
|
75
|
+
* - wire substitute-style hooks so that subsequent `\set PROMPT1 …` calls
|
|
76
|
+
* update the corresponding `settings.prompt{1,2,3}` field.
|
|
77
|
+
*
|
|
78
|
+
* Connection (`settings.db`) is left `null`; it is the responsibility of
|
|
79
|
+
* the startup/connect WP to populate it after a successful libpq-equivalent
|
|
80
|
+
* connection.
|
|
81
|
+
*/
|
|
82
|
+
const defaultSettings = (varStore) => {
|
|
83
|
+
const settings = {
|
|
84
|
+
db: null,
|
|
85
|
+
vars: varStore,
|
|
86
|
+
popt: buildDefaultPrintOpts(),
|
|
87
|
+
mainfile: null,
|
|
88
|
+
inputfile: null,
|
|
89
|
+
curCmdSource: "stdin",
|
|
90
|
+
restrictedKey: null,
|
|
91
|
+
prompt1: DEFAULT_PROMPT1,
|
|
92
|
+
prompt2: DEFAULT_PROMPT2,
|
|
93
|
+
prompt3: ">> ",
|
|
94
|
+
notty: false,
|
|
95
|
+
quiet: false,
|
|
96
|
+
singleline: false,
|
|
97
|
+
singlestep: false,
|
|
98
|
+
onErrorStop: false,
|
|
99
|
+
fetchCount: 0,
|
|
100
|
+
verbosity: DEFAULT_VERBOSITY,
|
|
101
|
+
showContext: DEFAULT_SHOW_CONTEXT,
|
|
102
|
+
echo: DEFAULT_ECHO,
|
|
103
|
+
echoHidden: DEFAULT_ECHO_HIDDEN,
|
|
104
|
+
onErrorRollback: DEFAULT_ON_ERROR_ROLLBACK,
|
|
105
|
+
compCase: DEFAULT_COMP_CASE,
|
|
106
|
+
sendMode: DEFAULT_SEND_MODE,
|
|
107
|
+
histControl: DEFAULT_HIST_CONTROL,
|
|
108
|
+
hideCompression: false,
|
|
109
|
+
hideTableam: false,
|
|
110
|
+
logfile: null,
|
|
111
|
+
timing: false,
|
|
112
|
+
lastErrorResult: null,
|
|
113
|
+
lastQuery: ""
|
|
114
|
+
};
|
|
115
|
+
varStore.addHook("PROMPT1", (newValue) => {
|
|
116
|
+
settings.prompt1 = newValue ?? "";
|
|
117
|
+
return true;
|
|
118
|
+
});
|
|
119
|
+
varStore.addHook("PROMPT2", (newValue) => {
|
|
120
|
+
settings.prompt2 = newValue ?? "";
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
varStore.addHook("PROMPT3", (newValue) => {
|
|
124
|
+
settings.prompt3 = newValue ?? "";
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
varStore.set("PROMPT1", DEFAULT_PROMPT1);
|
|
128
|
+
varStore.set("PROMPT2", DEFAULT_PROMPT2);
|
|
129
|
+
varStore.set("PROMPT3", ">> ");
|
|
130
|
+
varStore.set("LAST_ERROR_MESSAGE", "");
|
|
131
|
+
varStore.set("LAST_ERROR_SQLSTATE", "00000");
|
|
132
|
+
varStore.set("PIPELINE_SYNC_COUNT", "0");
|
|
133
|
+
varStore.set("PIPELINE_COMMAND_COUNT", "0");
|
|
134
|
+
varStore.set("PIPELINE_RESULT_COUNT", "0");
|
|
135
|
+
varStore.set("SHOW_ALL_RESULTS", "on");
|
|
136
|
+
varStore.set("ENCODING", "UTF8");
|
|
137
|
+
varStore.addHook("WATCH_INTERVAL", (newValue) => {
|
|
138
|
+
if (newValue === null) return { substitute: DEFAULT_WATCH_INTERVAL };
|
|
139
|
+
if (newValue === "") return `invalid input syntax for variable "WATCH_INTERVAL"`;
|
|
140
|
+
const range = validateWatchInterval(newValue);
|
|
141
|
+
if (range !== null) return range;
|
|
142
|
+
return true;
|
|
143
|
+
});
|
|
144
|
+
varStore.addHook("ON_ERROR_STOP", makeBoolHook("ON_ERROR_STOP", (parsed) => {
|
|
145
|
+
settings.onErrorStop = parsed;
|
|
146
|
+
}));
|
|
147
|
+
varStore.addHook("AUTOCOMMIT", makeBoolHook("AUTOCOMMIT"));
|
|
148
|
+
varStore.set("AUTOCOMMIT", "on");
|
|
149
|
+
varStore.addHook("FETCH_COUNT", (newValue) => {
|
|
150
|
+
if (newValue === null) {
|
|
151
|
+
settings.fetchCount = 0;
|
|
152
|
+
return { substitute: "0" };
|
|
153
|
+
}
|
|
154
|
+
const n = parseIntOrNull(newValue);
|
|
155
|
+
if (n === null) return `invalid value "${newValue}" for "FETCH_COUNT": integer expected`;
|
|
156
|
+
settings.fetchCount = Math.max(0, n);
|
|
157
|
+
return true;
|
|
158
|
+
});
|
|
159
|
+
varStore.addHook("ON_ERROR_ROLLBACK", makeOnErrorRollbackHook((parsed) => {
|
|
160
|
+
settings.onErrorRollback = parsed;
|
|
161
|
+
}));
|
|
162
|
+
varStore.addHook("VERBOSITY", makeEnumHook("VERBOSITY", [
|
|
163
|
+
"default",
|
|
164
|
+
"verbose",
|
|
165
|
+
"terse",
|
|
166
|
+
"sqlstate"
|
|
167
|
+
], "default", (parsed) => {
|
|
168
|
+
settings.verbosity = parsed;
|
|
169
|
+
}));
|
|
170
|
+
varStore.addHook("SHOW_CONTEXT", makeEnumHook("SHOW_CONTEXT", [
|
|
171
|
+
"never",
|
|
172
|
+
"errors",
|
|
173
|
+
"always"
|
|
174
|
+
], "errors", (parsed) => {
|
|
175
|
+
settings.showContext = parsed;
|
|
176
|
+
}));
|
|
177
|
+
varStore.addHook("ECHO", makeEnumHook("ECHO", [
|
|
178
|
+
"none",
|
|
179
|
+
"errors",
|
|
180
|
+
"queries",
|
|
181
|
+
"all"
|
|
182
|
+
], "none", (parsed) => {
|
|
183
|
+
settings.echo = parsed;
|
|
184
|
+
}));
|
|
185
|
+
varStore.addHook("ECHO_HIDDEN", makeEchoHiddenHook((parsed) => {
|
|
186
|
+
settings.echoHidden = parsed;
|
|
187
|
+
}));
|
|
188
|
+
varStore.addHook("COMP_KEYWORD_CASE", makeEnumHook("COMP_KEYWORD_CASE", [
|
|
189
|
+
"lower",
|
|
190
|
+
"upper",
|
|
191
|
+
"preserve-lower",
|
|
192
|
+
"preserve-upper"
|
|
193
|
+
], DEFAULT_COMP_CASE, (parsed) => {
|
|
194
|
+
settings.compCase = parsed;
|
|
195
|
+
}));
|
|
196
|
+
varStore.addHook("HISTCONTROL", makeEnumHook("HISTCONTROL", [
|
|
197
|
+
"none",
|
|
198
|
+
"ignorespace",
|
|
199
|
+
"ignoredups",
|
|
200
|
+
"ignoreboth"
|
|
201
|
+
], "none", (parsed) => {
|
|
202
|
+
settings.histControl = parsed;
|
|
203
|
+
}));
|
|
204
|
+
varStore.addHook("SHOW_ALL_RESULTS", makeBoolHook("SHOW_ALL_RESULTS"));
|
|
205
|
+
varStore.addHook("QUIET", makeBoolHook("QUIET", (parsed) => {
|
|
206
|
+
settings.quiet = parsed;
|
|
207
|
+
}));
|
|
208
|
+
varStore.addHook("SINGLELINE", makeBoolHook("SINGLELINE", (parsed) => {
|
|
209
|
+
settings.singleline = parsed;
|
|
210
|
+
}));
|
|
211
|
+
varStore.addHook("SINGLESTEP", makeBoolHook("SINGLESTEP", (parsed) => {
|
|
212
|
+
settings.singlestep = parsed;
|
|
213
|
+
}));
|
|
214
|
+
varStore.addHook("HIDE_TOAST_COMPRESSION", makeBoolHook("HIDE_TOAST_COMPRESSION", (parsed) => {
|
|
215
|
+
settings.hideCompression = parsed;
|
|
216
|
+
}));
|
|
217
|
+
varStore.addHook("HIDE_TABLEAM", makeBoolHook("HIDE_TABLEAM", (parsed) => {
|
|
218
|
+
settings.hideTableam = parsed;
|
|
219
|
+
}));
|
|
220
|
+
varStore.addHook("HISTSIZE", (newValue) => {
|
|
221
|
+
if (newValue === null) return { substitute: "500" };
|
|
222
|
+
if (parseIntOrNull(newValue) === null) return `invalid value "${newValue}" for "HISTSIZE": integer expected`;
|
|
223
|
+
return true;
|
|
224
|
+
});
|
|
225
|
+
varStore.addHook("IGNOREEOF", (newValue) => {
|
|
226
|
+
if (newValue === null) return { substitute: "0" };
|
|
227
|
+
if (parseStrtolBase0OrNull(newValue) === null) return { substitute: "10" };
|
|
228
|
+
return true;
|
|
229
|
+
});
|
|
230
|
+
return settings;
|
|
370
231
|
};
|
|
371
232
|
/**
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
233
|
+
* Build a strict-boolean hook with upstream's `bool_substitute_hook` +
|
|
234
|
+
* `bool_assign_hook` semantics:
|
|
235
|
+
*
|
|
236
|
+
* - empty / null → substitute "on"
|
|
237
|
+
* - on/off/true/false/yes/no/1/0 (case-insensitive) → accepted
|
|
238
|
+
* - anything else → reject with `unrecognized value "<value>" for
|
|
239
|
+
* "<name>": Boolean expected`
|
|
240
|
+
*
|
|
241
|
+
* The optional `apply` callback receives the parsed boolean so callers can
|
|
242
|
+
* keep a derived `PsqlSettings` field in sync (e.g. `settings.onErrorStop`).
|
|
243
|
+
*/
|
|
383
244
|
const makeBoolHook = (name, apply) => {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
const parsed = parseOnOffBool(newValue);
|
|
400
|
-
if (parsed === null) {
|
|
401
|
-
return `unrecognized value "${newValue}" for "${name}": Boolean expected`;
|
|
402
|
-
}
|
|
403
|
-
apply?.(parsed);
|
|
404
|
-
return true;
|
|
405
|
-
};
|
|
245
|
+
return (newValue) => {
|
|
246
|
+
if (newValue === null) {
|
|
247
|
+
apply?.(false);
|
|
248
|
+
return { substitute: "off" };
|
|
249
|
+
}
|
|
250
|
+
if (newValue === "") {
|
|
251
|
+
apply?.(true);
|
|
252
|
+
return { substitute: "on" };
|
|
253
|
+
}
|
|
254
|
+
const parsed = parseOnOffBool(newValue);
|
|
255
|
+
if (parsed === null) return `unrecognized value "${newValue}" for "${name}": Boolean expected`;
|
|
256
|
+
apply?.(parsed);
|
|
257
|
+
return true;
|
|
258
|
+
};
|
|
406
259
|
};
|
|
407
260
|
/**
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
261
|
+
* Build an enum-style hook with upstream's substitute / assign pair (the
|
|
262
|
+
* VERBOSITY / SHOW_CONTEXT / ECHO / COMP_KEYWORD_CASE / HISTCONTROL pattern):
|
|
263
|
+
*
|
|
264
|
+
* - null → substitute the supplied `defaultValue` (matches
|
|
265
|
+
* `verbosity_substitute_hook` etc. — `\unset NAME` re-stores the default)
|
|
266
|
+
* - exact-match (case-insensitive) against `allowed` → accepted
|
|
267
|
+
* - empty / unrecognized → reject with `unrecognized value "<value>" for
|
|
268
|
+
* "<name>"\nAvailable values are: a, b, c.` (the substitute hooks only
|
|
269
|
+
* handle NULL upstream — empty falls through to the assign hook's
|
|
270
|
+
* PsqlVarEnumError path, preserving the prior value)
|
|
271
|
+
*
|
|
272
|
+
* `apply` is invoked with the canonical lowercase spelling whenever the
|
|
273
|
+
* variable is set (including the default-on-null path).
|
|
274
|
+
*/
|
|
422
275
|
const makeEnumHook = (name, allowed, defaultValue, apply) => {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
return true;
|
|
436
|
-
};
|
|
276
|
+
const list = allowed.join(", ");
|
|
277
|
+
return (newValue) => {
|
|
278
|
+
if (newValue === null) {
|
|
279
|
+
apply?.(defaultValue);
|
|
280
|
+
return { substitute: defaultValue };
|
|
281
|
+
}
|
|
282
|
+
const lower = newValue.toLowerCase();
|
|
283
|
+
const match = allowed.find((a) => a.toLowerCase() === lower);
|
|
284
|
+
if (match === void 0) return `unrecognized value "${newValue}" for "${name}"\nAvailable values are: ${list}.`;
|
|
285
|
+
apply?.(match);
|
|
286
|
+
return true;
|
|
287
|
+
};
|
|
437
288
|
};
|
|
438
289
|
/**
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
290
|
+
* `ON_ERROR_ROLLBACK` is upstream's only tri-state boolean — `on` / `off` /
|
|
291
|
+
* `interactive`. Empty / null → substitute "on" (matches
|
|
292
|
+
* `on_error_rollback_substitute_hook`). Other values get the multi-line
|
|
293
|
+
* diagnostic upstream emits from `on_error_rollback_assign_hook`.
|
|
294
|
+
*/
|
|
444
295
|
const makeOnErrorRollbackHook = (apply) => {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
apply(lower);
|
|
462
|
-
return true;
|
|
463
|
-
}
|
|
464
|
-
return `unrecognized value "${newValue}" for "ON_ERROR_ROLLBACK"\nAvailable values are: on, off, interactive.`;
|
|
465
|
-
};
|
|
296
|
+
return (newValue) => {
|
|
297
|
+
if (newValue === null) {
|
|
298
|
+
apply("off");
|
|
299
|
+
return { substitute: "off" };
|
|
300
|
+
}
|
|
301
|
+
if (newValue === "") {
|
|
302
|
+
apply("on");
|
|
303
|
+
return { substitute: "on" };
|
|
304
|
+
}
|
|
305
|
+
const lower = newValue.toLowerCase();
|
|
306
|
+
if (lower === "on" || lower === "off" || lower === "interactive") {
|
|
307
|
+
apply(lower);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return `unrecognized value "${newValue}" for "ON_ERROR_ROLLBACK"\nAvailable values are: on, off, interactive.`;
|
|
311
|
+
};
|
|
466
312
|
};
|
|
467
313
|
/**
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
314
|
+
* ECHO_HIDDEN tri-state: `on` / `off` / `noexec`. Upstream uses
|
|
315
|
+
* `bool_substitute_hook` (null → "off", empty → "on") plus
|
|
316
|
+
* `echo_hidden_hook` which accepts `noexec` or falls back to
|
|
317
|
+
* `ParseVariableBool`. Anything else gets the upstream "unrecognized
|
|
318
|
+
* value" line with the three-element list.
|
|
319
|
+
*/
|
|
474
320
|
const makeEchoHiddenHook = (apply) => {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return `unrecognized value "${newValue}" for "ECHO_HIDDEN"\nAvailable values are: on, off, noexec.`;
|
|
494
|
-
}
|
|
495
|
-
apply(parsed ? "on" : "off");
|
|
496
|
-
return true;
|
|
497
|
-
};
|
|
321
|
+
return (newValue) => {
|
|
322
|
+
if (newValue === null) {
|
|
323
|
+
apply("off");
|
|
324
|
+
return { substitute: "off" };
|
|
325
|
+
}
|
|
326
|
+
if (newValue === "") {
|
|
327
|
+
apply("on");
|
|
328
|
+
return { substitute: "on" };
|
|
329
|
+
}
|
|
330
|
+
if (newValue.toLowerCase() === "noexec") {
|
|
331
|
+
apply("noexec");
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
const parsed = parseOnOffBool(newValue);
|
|
335
|
+
if (parsed === null) return `unrecognized value "${newValue}" for "ECHO_HIDDEN"\nAvailable values are: on, off, noexec.`;
|
|
336
|
+
apply(parsed ? "on" : "off");
|
|
337
|
+
return true;
|
|
338
|
+
};
|
|
498
339
|
};
|
|
499
340
|
/**
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
341
|
+
* Parse a non-empty string as a base-10 signed integer. Returns `null` on
|
|
342
|
+
* any junk. Used by FETCH_COUNT / HISTSIZE — both invoke
|
|
343
|
+
* `ParseVariableNum` upstream which internally uses `strtol(..., 0)`
|
|
344
|
+
* (accepting hex/octal/decimal); we tighten to decimal-only here. The
|
|
345
|
+
* deviation is observable for `\set FETCH_COUNT 0xff` (upstream accepts,
|
|
346
|
+
* we reject); intentional for now because the regress doesn't exercise it.
|
|
347
|
+
*/
|
|
507
348
|
const parseIntOrNull = (raw) => {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
return null;
|
|
516
|
-
if (n < -0x80000000 || n > 0x7fffffff)
|
|
517
|
-
return null;
|
|
518
|
-
return n;
|
|
349
|
+
const trimmed = raw.trim();
|
|
350
|
+
if (trimmed.length === 0) return null;
|
|
351
|
+
if (!/^[+-]?\d+$/.test(trimmed)) return null;
|
|
352
|
+
const n = parseInt(trimmed, 10);
|
|
353
|
+
if (!Number.isFinite(n)) return null;
|
|
354
|
+
if (n < -2147483648 || n > 2147483647) return null;
|
|
355
|
+
return n;
|
|
519
356
|
};
|
|
520
357
|
/**
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
358
|
+
* Parse a non-empty string as a strtol-base-0 signed integer (decimal,
|
|
359
|
+
* `0x`/`0X` hex, leading-`0` octal). Returns `null` on any junk. Mirrors
|
|
360
|
+
* upstream `ParseVariableNum`'s `strtol(value, &end, 0)` syntax — used by
|
|
361
|
+
* IGNOREEOF's substitute hook, where the original string is preserved on
|
|
362
|
+
* success (the hook only checks parsability to decide whether to swap in
|
|
363
|
+
* "10").
|
|
364
|
+
*/
|
|
528
365
|
const parseStrtolBase0OrNull = (raw) => {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
if (body.length === 0)
|
|
555
|
-
return null;
|
|
556
|
-
// Reject trailing junk — strtol returns success only when the entire
|
|
557
|
-
// body is consumed (`*end == '\0'`), and ParseVariableNum requires that.
|
|
558
|
-
const digitRe = radix === 16 ? /^[0-9a-fA-F]+$/ : radix === 8 ? /^[0-7]+$/ : /^[0-9]+$/;
|
|
559
|
-
if (!digitRe.test(body))
|
|
560
|
-
return null;
|
|
561
|
-
const parsed = sign * parseInt(body, radix);
|
|
562
|
-
if (!Number.isFinite(parsed))
|
|
563
|
-
return null;
|
|
564
|
-
// 32-bit signed range (ParseVariableNum's `numval == (int) numval` check).
|
|
565
|
-
if (parsed < -0x80000000 || parsed > 0x7fffffff)
|
|
566
|
-
return null;
|
|
567
|
-
return parsed;
|
|
366
|
+
const trimmed = raw.replace(/^\s+/, "");
|
|
367
|
+
if (trimmed.length === 0) return null;
|
|
368
|
+
let body = trimmed;
|
|
369
|
+
let sign = 1;
|
|
370
|
+
if (body.startsWith("+")) body = body.slice(1);
|
|
371
|
+
else if (body.startsWith("-")) {
|
|
372
|
+
sign = -1;
|
|
373
|
+
body = body.slice(1);
|
|
374
|
+
}
|
|
375
|
+
if (body.length === 0) return null;
|
|
376
|
+
let radix = 10;
|
|
377
|
+
if (body.startsWith("0x") || body.startsWith("0X")) {
|
|
378
|
+
radix = 16;
|
|
379
|
+
body = body.slice(2);
|
|
380
|
+
} else if (body.length > 1 && body.startsWith("0")) {
|
|
381
|
+
radix = 8;
|
|
382
|
+
body = body.slice(1);
|
|
383
|
+
}
|
|
384
|
+
if (body.length === 0) return null;
|
|
385
|
+
if (!(radix === 16 ? /^[0-9a-fA-F]+$/ : radix === 8 ? /^[0-7]+$/ : /^[0-9]+$/).test(body)) return null;
|
|
386
|
+
const parsed = sign * parseInt(body, radix);
|
|
387
|
+
if (!Number.isFinite(parsed)) return null;
|
|
388
|
+
if (parsed < -2147483648 || parsed > 2147483647) return null;
|
|
389
|
+
return parsed;
|
|
568
390
|
};
|
|
569
391
|
/**
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
392
|
+
* Parse the `on`/`off`/`true`/`false`/`yes`/`no`/`1`/`0` boolean spelling
|
|
393
|
+
* upstream uses for psql variables. Mirrors `ParseVariableBool` (without the
|
|
394
|
+
* error reporting — callers handle that). Returns null on unrecognised input.
|
|
395
|
+
*/
|
|
574
396
|
const parseOnOffBool = (raw) => {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (v === "off" || v === "false" || v === "no" || v === "0") {
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
|
-
return null;
|
|
397
|
+
const v = raw.toLowerCase().trim();
|
|
398
|
+
if (v === "" || v === "on" || v === "true" || v === "yes" || v === "1") return true;
|
|
399
|
+
if (v === "off" || v === "false" || v === "no" || v === "0") return false;
|
|
400
|
+
return null;
|
|
583
401
|
};
|
|
584
402
|
/**
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
403
|
+
* Strict validation for non-empty `WATCH_INTERVAL` values. Mirrors
|
|
404
|
+
* upstream's `ParseVariableDouble(name, ..., 0, DEFAULT_WATCH_INTERVAL_MAX)`
|
|
405
|
+
* three distinct error paths:
|
|
406
|
+
*
|
|
407
|
+
* - junk (strtod doesn't consume it) → `invalid value "<value>" for
|
|
408
|
+
* variable "WATCH_INTERVAL"`
|
|
409
|
+
* - parses but value < 0 → `must be greater than 0.00`
|
|
410
|
+
* - parses but value > 1000000 → `must be less than 1000000.00`
|
|
411
|
+
*
|
|
412
|
+
* Returns `null` if the value is valid; otherwise returns the upstream
|
|
413
|
+
* error string (sent through `trySet`'s hook-veto channel so the prior
|
|
414
|
+
* value is preserved).
|
|
415
|
+
*/
|
|
598
416
|
const validateWatchInterval = (raw) => {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
// Upstream `ParseVariableDouble` consults `errno = ERANGE` after
|
|
607
|
-
// `strtod` to distinguish overflow from generic junk: the overflow
|
|
608
|
-
// case emits `is out of range` rather than the generic `invalid
|
|
609
|
-
// value`. JS `parseFloat` returns Infinity on overflow — use that as
|
|
610
|
-
// the ERANGE proxy.
|
|
611
|
-
if (Number.isNaN(value)) {
|
|
612
|
-
return `invalid value "${raw}" for variable "WATCH_INTERVAL"`;
|
|
613
|
-
}
|
|
614
|
-
if (!Number.isFinite(value)) {
|
|
615
|
-
return `invalid value "${raw}" for variable "WATCH_INTERVAL": is out of range`;
|
|
616
|
-
}
|
|
617
|
-
if (value < 0) {
|
|
618
|
-
return `invalid value "${raw}" for variable "WATCH_INTERVAL": must be greater than 0.00`;
|
|
619
|
-
}
|
|
620
|
-
if (value > WATCH_INTERVAL_MAX) {
|
|
621
|
-
return `invalid value "${raw}" for variable "WATCH_INTERVAL": must be less than ${WATCH_INTERVAL_MAX.toFixed(2)}`;
|
|
622
|
-
}
|
|
623
|
-
return null;
|
|
417
|
+
if (!/^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/.test(raw)) return `invalid value "${raw}" for variable "WATCH_INTERVAL"`;
|
|
418
|
+
const value = parseFloat(raw);
|
|
419
|
+
if (Number.isNaN(value)) return `invalid value "${raw}" for variable "WATCH_INTERVAL"`;
|
|
420
|
+
if (!Number.isFinite(value)) return `invalid value "${raw}" for variable "WATCH_INTERVAL": is out of range`;
|
|
421
|
+
if (value < 0) return `invalid value "${raw}" for variable "WATCH_INTERVAL": must be greater than 0.00`;
|
|
422
|
+
if (value > WATCH_INTERVAL_MAX) return `invalid value "${raw}" for variable "WATCH_INTERVAL": must be less than ${WATCH_INTERVAL_MAX.toFixed(2)}`;
|
|
423
|
+
return null;
|
|
624
424
|
};
|
|
625
425
|
/**
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
// RC file path.
|
|
670
|
-
bridge("PSQLRC", "PSQLRC");
|
|
671
|
-
// NO_COLOR — captured under a psql-style var name. The convention is
|
|
672
|
-
// "any non-empty value disables color". We store the raw value so the
|
|
673
|
-
// printer can decide via VarStore.asBool / its own check.
|
|
674
|
-
if (env.NO_COLOR !== undefined && env.NO_COLOR !== "") {
|
|
675
|
-
settings.vars.set("NO_COLOR", env.NO_COLOR);
|
|
676
|
-
}
|
|
677
|
-
// COLUMNS — feed into popt.topt.envColumns (upstream:
|
|
678
|
-
// pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(...) : 0).
|
|
679
|
-
const cols = env.COLUMNS;
|
|
680
|
-
if (cols !== undefined && cols !== "") {
|
|
681
|
-
const parsed = parseInt(cols, 10);
|
|
682
|
-
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
683
|
-
settings.popt.topt.envColumns = parsed;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
426
|
+
* Bridge environment variables to psql state. Read-only with respect to the
|
|
427
|
+
* environment; mutates `settings` (and its var store) in place.
|
|
428
|
+
*
|
|
429
|
+
* The variable names below match upstream where it reads them lazily:
|
|
430
|
+
*
|
|
431
|
+
* - PAGER, PSQL_PAGER, PSQL_WATCH_PAGER — consulted by `fe_utils/print.c`
|
|
432
|
+
* (`PageOutput`) and `\watch`. Captured as psql vars so later WPs read
|
|
433
|
+
* a single source of truth.
|
|
434
|
+
* - EDITOR, VISUAL, PSQL_EDITOR, PSQL_EDITOR_LINENUMBER_ARG — consulted
|
|
435
|
+
* by `\e`/`\ef`/`\ev` in `command.c::editFile`.
|
|
436
|
+
* - PSQL_HISTORY — `src/bin/psql/input.c::initializeInput` uses it as the
|
|
437
|
+
* history file path when readline is active. Stored as HISTFILE for
|
|
438
|
+
* parity with psql's own `\set HISTFILE` convention.
|
|
439
|
+
* - PSQL_HISTSIZE / PSQL_HISTCONTROL — likewise from `input.c`, stored
|
|
440
|
+
* under the corresponding psql var names HISTSIZE / HISTCONTROL.
|
|
441
|
+
* - PSQLRC — origin of the rc-file path (`startup.c::process_psqlrc`).
|
|
442
|
+
* - COLUMNS — terminal width hint (`startup.c`: `pset.popt.topt.env_columns`).
|
|
443
|
+
* - NO_COLOR — `https://no-color.org/` convention; consulted by the
|
|
444
|
+
* printer/color code in upstream `fe_utils/print.c`. Captured here so
|
|
445
|
+
* later WPs can opt out of ANSI coloring without re-reading `process.env`.
|
|
446
|
+
*/
|
|
447
|
+
const applyEnvOverrides = (settings, env = process.env) => {
|
|
448
|
+
const bridge = (envName, varName) => {
|
|
449
|
+
const value = env[envName];
|
|
450
|
+
if (value !== void 0 && value !== "") settings.vars.set(varName, value);
|
|
451
|
+
};
|
|
452
|
+
bridge("PAGER", "PAGER");
|
|
453
|
+
bridge("PSQL_PAGER", "PAGER");
|
|
454
|
+
bridge("PSQL_WATCH_PAGER", "PSQL_WATCH_PAGER");
|
|
455
|
+
bridge("EDITOR", "EDITOR");
|
|
456
|
+
bridge("VISUAL", "EDITOR");
|
|
457
|
+
bridge("PSQL_EDITOR", "EDITOR");
|
|
458
|
+
bridge("PSQL_EDITOR_LINENUMBER_ARG", "EDITOR_LINENUMBER_ARG");
|
|
459
|
+
bridge("PSQL_HISTORY", "HISTFILE");
|
|
460
|
+
bridge("PSQL_HISTSIZE", "HISTSIZE");
|
|
461
|
+
bridge("PSQL_HISTCONTROL", "HISTCONTROL");
|
|
462
|
+
bridge("PSQLRC", "PSQLRC");
|
|
463
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") settings.vars.set("NO_COLOR", env.NO_COLOR);
|
|
464
|
+
const cols = env.COLUMNS;
|
|
465
|
+
if (cols !== void 0 && cols !== "") {
|
|
466
|
+
const parsed = parseInt(cols, 10);
|
|
467
|
+
if (Number.isFinite(parsed) && parsed >= 0) settings.popt.topt.envColumns = parsed;
|
|
468
|
+
}
|
|
686
469
|
};
|
|
470
|
+
//#endregion
|
|
471
|
+
export { DEFAULT_CSV_FIELD_SEP, DEFAULT_FIELD_SEP, DEFAULT_PROMPT1, DEFAULT_PROMPT2, DEFAULT_PROMPT3, DEFAULT_RECORD_SEP, applyEnvOverrides, defaultSettings };
|