neon 2.47.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.
- package/README.md +54 -0
- package/dist/_shared/auth_selection.js +76 -79
- package/dist/_shared/credentials.js +166 -187
- package/dist/_shared/env-core/env.js +395 -0
- package/dist/_shared/env-core/reuse-secrets.js +179 -0
- package/dist/_shared/paths.js +129 -126
- package/dist/_shared/profiles.js +192 -242
- package/dist/_shared/secure_file.js +36 -38
- package/dist/_virtual/_rolldown/runtime.js +13 -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 +575 -658
- 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 +642 -681
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +243 -303
- package/dist/commands/functions.js +275 -341
- 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 +614 -845
- 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 +313 -394
- package/dist/dev/functions.js +54 -64
- 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 +36 -38
- 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 +727 -983
- 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 +1293 -2082
- 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 +547 -1099
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +261 -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 +762 -1180
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1302
- 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 +626 -815
- 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 +1414 -1927
- 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 +2 -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 +133 -147
- 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 +37 -44
- 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 +9 -7
|
@@ -1,437 +1,262 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* psql conditional backslash commands: `\if`, `\elif`, `\else`, `\endif`.
|
|
3
|
-
*
|
|
4
|
-
* TypeScript port of `exec_command_if`/`exec_command_elif`/`exec_command_else`/
|
|
5
|
-
* `exec_command_endif` in `src/bin/psql/command.c`, plus the `ConditionalStack`
|
|
6
|
-
* machinery from `src/fe_utils/conditional.c`.
|
|
7
|
-
*
|
|
8
|
-
* Semantics (mirroring upstream exactly):
|
|
9
|
-
*
|
|
10
|
-
* - `\if <expr>`
|
|
11
|
-
* • If outer branch is active, push a new frame whose state is `TRUE` or
|
|
12
|
-
* `FALSE` depending on the parsed expression value.
|
|
13
|
-
* • Otherwise push `IGNORED`: every nested branch is suppressed regardless
|
|
14
|
-
* of expression. This is how upstream achieves transitive suppression
|
|
15
|
-
* without `conditional_active()` itself being transitive.
|
|
16
|
-
* - `\elif <expr>`
|
|
17
|
-
* • Top is `TRUE` → branch already taken; skip rest until `\endif`
|
|
18
|
-
* (poke to `IGNORED`).
|
|
19
|
-
* • Top is `FALSE` → first true branch wins; evaluate expression.
|
|
20
|
-
* • Top is `IGNORED` → leave untouched, ignore expression.
|
|
21
|
-
* • Top is `ELSE_*` → error: `\elif: cannot occur after \else`.
|
|
22
|
-
* • Top is `NONE` (empty) → error: `\elif: no matching \if`.
|
|
23
|
-
* - `\else`
|
|
24
|
-
* • Top is `TRUE` → poke `ELSE_FALSE`.
|
|
25
|
-
* • Top is `FALSE` → poke `ELSE_TRUE`.
|
|
26
|
-
* • Top is `IGNORED` → poke `ELSE_FALSE` (still suppressed).
|
|
27
|
-
* • Top is `ELSE_*` → error: `\else: cannot occur after \else`.
|
|
28
|
-
* • Top is `NONE` → error: `\else: no matching \if`.
|
|
29
|
-
* - `\endif`
|
|
30
|
-
* • Pop the top frame.
|
|
31
|
-
* • Top was `NONE` → error: `\endif: no matching \if`.
|
|
32
|
-
*
|
|
33
|
-
* Expression evaluation: upstream `read_boolean_expression` reads tokens
|
|
34
|
-
* with `OT_NORMAL` (which expands `:vars` and backticks) and concatenates
|
|
35
|
-
* them with single spaces; the assembled string is passed through
|
|
36
|
-
* `ParseVariableBool`. We mirror that pipeline in {@link collectExpr} +
|
|
37
|
-
* {@link parseBool}: collect every `nextArg('normal')` token, join with
|
|
38
|
-
* spaces, parse. Unrecognised tokens emit the upstream
|
|
39
|
-
* `unrecognized value "<tok>" for "\<cmd> expression": Boolean expected`
|
|
40
|
-
* diagnostic and evaluate to false.
|
|
41
|
-
*
|
|
42
|
-
* Inactive branches: when the surrounding scope is suppressed, upstream
|
|
43
|
-
* `ignore_boolean_expression` drops the argument tokens WITHOUT running
|
|
44
|
-
* `:var` / backtick expansion (regress psql.sql ~line 1028 covers this).
|
|
45
|
-
* We achieve the same by NOT calling `nextArg` at all — the
|
|
46
|
-
* BackslashContext factory only invokes the slash scanner lazily on the
|
|
47
|
-
* first `nextArg` request, so leaving the args queue untouched is
|
|
48
|
-
* equivalent to upstream's "discard without expansion".
|
|
49
|
-
*
|
|
50
|
-
* Diagnostic format: cond commands emit their errors BARE (no
|
|
51
|
-
* `psql: ERROR:` prefix). This mirrors `expected/psql.out` (e.g.
|
|
52
|
-
* `\endif: no matching \if` on a single line). We use `writeErr` directly
|
|
53
|
-
* and return `errorWritten: true` so the mainloop's `psql: ERROR:` fallback
|
|
54
|
-
* is suppressed.
|
|
55
|
-
*
|
|
56
|
-
* Note on the "transitive suppression" requirement: upstream's
|
|
57
|
-
* `conditional_active()` only inspects the top frame, but the resulting state
|
|
58
|
-
* machine *is* transitive because `\if` inside an inactive outer always pushes
|
|
59
|
-
* `IGNORED`, and `IGNORED` never transitions to `TRUE`. So `isActive()`
|
|
60
|
-
* inspecting just the top frame produces the right answer.
|
|
61
|
-
*/
|
|
62
1
|
import { writeErr } from "./shared.js";
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
2
|
+
//#region src/psql/command/cmd_cond.ts
|
|
3
|
+
const INACTIVE_STATES = [
|
|
4
|
+
"false",
|
|
5
|
+
"else-false",
|
|
6
|
+
"ignored"
|
|
7
|
+
];
|
|
67
8
|
/**
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return;
|
|
112
|
-
frames[frames.length - 1].savedQueryBufLen = len;
|
|
113
|
-
},
|
|
114
|
-
depth() {
|
|
115
|
-
return frames.length;
|
|
116
|
-
},
|
|
117
|
-
};
|
|
9
|
+
* Build an empty {@link CondStack}.
|
|
10
|
+
*
|
|
11
|
+
* Frames are stored in an array; `top()` is the last element. `branchTaken`
|
|
12
|
+
* records whether a `TRUE`/`ELSE_TRUE` branch has been seen at this level —
|
|
13
|
+
* mainloop and the elif/else commands use it implicitly via the state-machine
|
|
14
|
+
* transitions described above (we don't expose it through the public API, but
|
|
15
|
+
* it's part of the frame shape declared in `types/repl.ts`).
|
|
16
|
+
*/
|
|
17
|
+
const createCondStack = () => {
|
|
18
|
+
const frames = [];
|
|
19
|
+
const branchTakenForInitial = (state) => state === "true" || state === "else-true";
|
|
20
|
+
return {
|
|
21
|
+
push(state, savedQueryBufLen = 0) {
|
|
22
|
+
frames.push({
|
|
23
|
+
state,
|
|
24
|
+
branchTaken: branchTakenForInitial(state),
|
|
25
|
+
savedQueryBufLen
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
pop() {
|
|
29
|
+
return frames.pop();
|
|
30
|
+
},
|
|
31
|
+
top() {
|
|
32
|
+
return frames.length === 0 ? void 0 : frames[frames.length - 1];
|
|
33
|
+
},
|
|
34
|
+
isActive() {
|
|
35
|
+
if (frames.length === 0) return true;
|
|
36
|
+
return !INACTIVE_STATES.includes(frames[frames.length - 1].state);
|
|
37
|
+
},
|
|
38
|
+
setState(state) {
|
|
39
|
+
if (frames.length === 0) return;
|
|
40
|
+
const top = frames[frames.length - 1];
|
|
41
|
+
top.state = state;
|
|
42
|
+
if (state === "true" || state === "else-true") top.branchTaken = true;
|
|
43
|
+
},
|
|
44
|
+
setSavedQueryBufLen(len) {
|
|
45
|
+
if (frames.length === 0) return;
|
|
46
|
+
frames[frames.length - 1].savedQueryBufLen = len;
|
|
47
|
+
},
|
|
48
|
+
depth() {
|
|
49
|
+
return frames.length;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
118
52
|
};
|
|
119
|
-
|
|
120
|
-
// ParseVariableBool — mirrors variables.c::ParseVariableBool.
|
|
121
|
-
//
|
|
122
|
-
// We re-implement it here (rather than importing from core/variables.ts) so
|
|
123
|
-
// the slash-cmd modules stay decoupled from the var-store implementation;
|
|
124
|
-
// they just need a value-parser. Recognised forms are case-insensitive with
|
|
125
|
-
// unique-prefix matching for word forms:
|
|
126
|
-
//
|
|
127
|
-
// true / false / yes / no (unique-prefix accepted)
|
|
128
|
-
// on / off (need at least 2 chars; bare 'o' ambiguous)
|
|
129
|
-
// 1 / 0 (literal)
|
|
130
|
-
//
|
|
131
|
-
// Anything else is an error (upstream prints a warning and returns false). We
|
|
132
|
-
// follow upstream by treating unrecognised tokens as `false` while pushing
|
|
133
|
-
// the frame.
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
const isPrefixOf = (value, prefix) => value.length > 0 &&
|
|
136
|
-
value.length <= prefix.length &&
|
|
137
|
-
prefix.slice(0, value.length).toLowerCase() === value.toLowerCase();
|
|
53
|
+
const isPrefixOf = (value, prefix) => value.length > 0 && value.length <= prefix.length && prefix.slice(0, value.length).toLowerCase() === value.toLowerCase();
|
|
138
54
|
/** Returns the parsed boolean, or `null` if the string was not recognised. */
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return true;
|
|
154
|
-
if ("off".startsWith(lower))
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
if (value === "1")
|
|
158
|
-
return true;
|
|
159
|
-
if (value === "0")
|
|
160
|
-
return false;
|
|
161
|
-
return null;
|
|
55
|
+
const parseBool = (value) => {
|
|
56
|
+
if (value.length === 0) return null;
|
|
57
|
+
if (isPrefixOf(value, "true")) return true;
|
|
58
|
+
if (isPrefixOf(value, "false")) return false;
|
|
59
|
+
if (isPrefixOf(value, "yes")) return true;
|
|
60
|
+
if (isPrefixOf(value, "no")) return false;
|
|
61
|
+
if (value.length >= 2) {
|
|
62
|
+
const lower = value.toLowerCase();
|
|
63
|
+
if ("on".startsWith(lower)) return true;
|
|
64
|
+
if ("off".startsWith(lower)) return false;
|
|
65
|
+
}
|
|
66
|
+
if (value === "1") return true;
|
|
67
|
+
if (value === "0") return false;
|
|
68
|
+
return null;
|
|
162
69
|
};
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
// Backslash command implementations
|
|
165
|
-
// ---------------------------------------------------------------------------
|
|
166
70
|
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
71
|
+
* Read every remaining `'normal'`-mode argument off the BackslashContext and
|
|
72
|
+
* concatenate them with single spaces. Mirrors upstream
|
|
73
|
+
* `read_boolean_expression`, which calls `psql_scan_slash_option(OT_NORMAL)`
|
|
74
|
+
* in a loop and joins tokens with `" "`. Returns the empty string when no
|
|
75
|
+
* args follow the command name — upstream's "missing expression" path
|
|
76
|
+
* surfaces the same `unrecognized value ""...` diagnostic as any other
|
|
77
|
+
* unparseable token, then evaluates to false.
|
|
78
|
+
*
|
|
79
|
+
* After joining, we resolve the `:{?varname}` "defined-variable" substitution
|
|
80
|
+
* form. Upstream's slash lexer recognises `:{?name}` directly and emits
|
|
81
|
+
* `TRUE` / `FALSE` depending on whether the named variable is currently
|
|
82
|
+
* set; we re-create that pass here because the underlying scanner only
|
|
83
|
+
* handles the plain `:name`, `:'name'`, `:"name"` forms (the `:{?name}`
|
|
84
|
+
* form lives in a separate `xslashdefined` flex rule upstream). Doing it
|
|
85
|
+
* after the join keeps the rule local to the conditional-expression
|
|
86
|
+
* pipeline — every other call site of the slash scanner has its own
|
|
87
|
+
* variable-expansion needs that we don't want to disturb.
|
|
88
|
+
*/
|
|
185
89
|
const DEFINED_VAR_RE = /:\{\?([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
186
90
|
const expandDefinedVar = (text, isDefined) => text.replace(DEFINED_VAR_RE, (_match, name) => isDefined(name) ? "TRUE" : "FALSE");
|
|
187
91
|
const collectExpr = (ctx) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return expandDefinedVar(parts.join(" "), (name) => ctx.settings.vars.has(name));
|
|
196
|
-
};
|
|
197
|
-
/**
|
|
198
|
-
* Marker call indicating "discard the expression without evaluating it".
|
|
199
|
-
* Mirrors upstream `ignore_boolean_expression` — when we're already inside
|
|
200
|
-
* an inactive branch, `\if` / `\elif` arguments are dropped without
|
|
201
|
-
* expanding `:vars` or running backticks. We achieve this by simply NOT
|
|
202
|
-
* calling `nextArg`: the BackslashContext factory in `mainloop.ts` only
|
|
203
|
-
* invokes the slash scanner lazily on the first arg request, so leaving
|
|
204
|
-
* the queue untouched skips all expansion. The unconsumed `rawArgs` are
|
|
205
|
-
* dropped after the cmd returns.
|
|
206
|
-
*
|
|
207
|
-
* Kept as a named no-op so call sites read intent-fully ("dropExpr") and
|
|
208
|
-
* future refactors can replace the body without touching every caller.
|
|
209
|
-
*/
|
|
210
|
-
const dropExpr = () => {
|
|
211
|
-
// Intentionally empty — see the doc comment.
|
|
92
|
+
const parts = [];
|
|
93
|
+
for (;;) {
|
|
94
|
+
const arg = ctx.nextArg("normal");
|
|
95
|
+
if (arg === null) break;
|
|
96
|
+
parts.push(arg);
|
|
97
|
+
}
|
|
98
|
+
return expandDefinedVar(parts.join(" "), (name) => ctx.settings.vars.has(name));
|
|
212
99
|
};
|
|
213
100
|
/**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
101
|
+
* Evaluate the joined expression against {@link parseBool}. Unrecognised
|
|
102
|
+
* tokens surface `unrecognized value "<tok>" for "\<cmd> expression":
|
|
103
|
+
* Boolean expected` to stderr (bare, no `psql: ERROR:` prefix — upstream
|
|
104
|
+
* `psql_error` shape) and evaluate to false. The caller is responsible for
|
|
105
|
+
* setting the stack state to `'false'` on this path. `cmdName` is the
|
|
106
|
+
* caller's command identifier (`'if'` / `'elif'`) so the diagnostic matches
|
|
107
|
+
* upstream verbatim.
|
|
108
|
+
*/
|
|
222
109
|
const evalExpr = (ctx, cmdName) => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
110
|
+
const raw = collectExpr(ctx);
|
|
111
|
+
const parsed = parseBool(raw);
|
|
112
|
+
if (parsed === null) {
|
|
113
|
+
const message = `unrecognized value "${raw}" for "\\${cmdName} expression": Boolean expected`;
|
|
114
|
+
ctx.settings.lastErrorResult = { message };
|
|
115
|
+
writeErr(`${message}\n`);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return parsed;
|
|
232
119
|
};
|
|
233
120
|
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
121
|
+
* Marker symbol on BackslashContext.cmdName so the mainloop can recognise the
|
|
122
|
+
* cond commands without an interface-pollution argument. We instead attach the
|
|
123
|
+
* CondStack via a well-known field on the `settings` object — see {@link
|
|
124
|
+
* attachCondStack} / {@link getCondStack}.
|
|
125
|
+
*
|
|
126
|
+
* The mainloop is the sole owner of the CondStack, and it threads it onto the
|
|
127
|
+
* BackslashContext via this helper pair so command modules don't have to know
|
|
128
|
+
* about REPLContext.
|
|
129
|
+
*/
|
|
243
130
|
const COND_STACK_KEY = Symbol.for("neonctl.psql.condStack");
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
131
|
+
const attachCondStack = (ctx, cond) => {
|
|
132
|
+
const settings = ctx.settings;
|
|
133
|
+
settings[COND_STACK_KEY] = cond;
|
|
247
134
|
};
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
throw new Error("cond stack not attached; cmd_cond commands must be dispatched via runMainLoop");
|
|
253
|
-
}
|
|
254
|
-
return stack;
|
|
135
|
+
const getCondStack = (ctx) => {
|
|
136
|
+
const stack = ctx.settings[COND_STACK_KEY];
|
|
137
|
+
if (stack === void 0) throw new Error("cond stack not attached; cmd_cond commands must be dispatched via runMainLoop");
|
|
138
|
+
return stack;
|
|
255
139
|
};
|
|
256
140
|
const errResult = (ctx, message) => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// `psql: ERROR: <msg>` fallback line.
|
|
264
|
-
writeErr(`${message}\n`);
|
|
265
|
-
return { status: "error", errorWritten: true };
|
|
141
|
+
ctx.settings.lastErrorResult = { message };
|
|
142
|
+
writeErr(`${message}\n`);
|
|
143
|
+
return {
|
|
144
|
+
status: "error",
|
|
145
|
+
errorWritten: true
|
|
146
|
+
};
|
|
266
147
|
};
|
|
267
148
|
const okResult = () => ({ status: "ok" });
|
|
268
149
|
/**
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
150
|
+
* Build an `{ status: 'ok' }` result that also asks the mainloop to truncate
|
|
151
|
+
* `queryBuf` back to `len`. Mirrors upstream `discard_query_text` — called
|
|
152
|
+
* by `\elif` / `\else` / `\endif` when the just-completed branch was
|
|
153
|
+
* INACTIVE, so the SQL text the skipped branch accumulated doesn't bleed
|
|
154
|
+
* into the surrounding statement.
|
|
155
|
+
*/
|
|
275
156
|
const truncResult = (len) => ({
|
|
276
|
-
|
|
277
|
-
|
|
157
|
+
status: "ok",
|
|
158
|
+
truncateBufTo: len
|
|
278
159
|
});
|
|
279
160
|
/**
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
161
|
+
* `true` when {@link IfState} corresponds to a branch that was skipping
|
|
162
|
+
* statements. Upstream `conditional_active` returns false for these. Used
|
|
163
|
+
* by the elif/else/endif commands to decide whether the
|
|
164
|
+
* `discard_query_text` step should fire on transition out of the branch.
|
|
165
|
+
*/
|
|
285
166
|
const isInactiveState = (state) => state === "false" || state === "else-false" || state === "ignored";
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// Suppressed by outer: push IGNORED and drop the expression WITHOUT
|
|
301
|
-
// expanding it. Upstream `ignore_boolean_expression` calls the lexer
|
|
302
|
-
// with backticks/vars disabled (psql.sql:1028 covers this with
|
|
303
|
-
// `\if false { \if \`nosuchcommand\` ... }`).
|
|
304
|
-
dropExpr();
|
|
305
|
-
cond.push("ignored", savedLen);
|
|
306
|
-
return Promise.resolve(okResult());
|
|
307
|
-
}
|
|
308
|
-
const truthy = evalExpr(ctx, "if");
|
|
309
|
-
cond.push(truthy ? "true" : "false", savedLen);
|
|
310
|
-
return Promise.resolve(okResult());
|
|
311
|
-
},
|
|
167
|
+
const cmdIf = {
|
|
168
|
+
name: "if",
|
|
169
|
+
argMode: "lex",
|
|
170
|
+
async run(ctx) {
|
|
171
|
+
const cond = getCondStack(ctx);
|
|
172
|
+
const savedLen = ctx.queryBuf.length;
|
|
173
|
+
if (!cond.isActive()) {
|
|
174
|
+
cond.push("ignored", savedLen);
|
|
175
|
+
return Promise.resolve(okResult());
|
|
176
|
+
}
|
|
177
|
+
const truthy = evalExpr(ctx, "if");
|
|
178
|
+
cond.push(truthy ? "true" : "false", savedLen);
|
|
179
|
+
return Promise.resolve(okResult());
|
|
180
|
+
}
|
|
312
181
|
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
case "false": {
|
|
342
|
-
// Have not yet found a true branch — evaluate this one.
|
|
343
|
-
// evalExpr emits its own `unrecognized value` diagnostic on failure
|
|
344
|
-
// and falls through to false, mirroring upstream.
|
|
345
|
-
const truthy = evalExpr(ctx, "elif");
|
|
346
|
-
cond.setState(truthy ? "true" : "false");
|
|
347
|
-
// Apply the discard (was INACTIVE) and re-anchor at the rolled-back
|
|
348
|
-
// length so the new branch's bookkeeping starts fresh.
|
|
349
|
-
cond.setSavedQueryBufLen(savedLen);
|
|
350
|
-
return wasInactive
|
|
351
|
-
? Promise.resolve(truncResult(savedLen))
|
|
352
|
-
: Promise.resolve(okResult());
|
|
353
|
-
}
|
|
354
|
-
case "ignored": {
|
|
355
|
-
// Outer is suppressed — stay ignored, drop args without expanding.
|
|
356
|
-
// Still discard accumulated buffer text and re-anchor.
|
|
357
|
-
dropExpr();
|
|
358
|
-
cond.setSavedQueryBufLen(savedLen);
|
|
359
|
-
return wasInactive
|
|
360
|
-
? Promise.resolve(truncResult(savedLen))
|
|
361
|
-
: Promise.resolve(okResult());
|
|
362
|
-
}
|
|
363
|
-
case "else-true":
|
|
364
|
-
case "else-false":
|
|
365
|
-
dropExpr();
|
|
366
|
-
return Promise.resolve(errResult(ctx, "\\elif: cannot occur after \\else"));
|
|
367
|
-
case "none":
|
|
368
|
-
dropExpr();
|
|
369
|
-
return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
|
|
370
|
-
}
|
|
371
|
-
},
|
|
182
|
+
const cmdElif = {
|
|
183
|
+
name: "elif",
|
|
184
|
+
argMode: "lex",
|
|
185
|
+
async run(ctx) {
|
|
186
|
+
const cond = getCondStack(ctx);
|
|
187
|
+
const top = cond.top();
|
|
188
|
+
if (top === void 0) return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
|
|
189
|
+
const wasInactive = isInactiveState(top.state);
|
|
190
|
+
const savedLen = top.savedQueryBufLen;
|
|
191
|
+
switch (top.state) {
|
|
192
|
+
case "true":
|
|
193
|
+
cond.setState("ignored");
|
|
194
|
+
cond.setSavedQueryBufLen(ctx.queryBuf.length);
|
|
195
|
+
return Promise.resolve(okResult());
|
|
196
|
+
case "false": {
|
|
197
|
+
const truthy = evalExpr(ctx, "elif");
|
|
198
|
+
cond.setState(truthy ? "true" : "false");
|
|
199
|
+
cond.setSavedQueryBufLen(savedLen);
|
|
200
|
+
return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
|
|
201
|
+
}
|
|
202
|
+
case "ignored":
|
|
203
|
+
cond.setSavedQueryBufLen(savedLen);
|
|
204
|
+
return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
|
|
205
|
+
case "else-true":
|
|
206
|
+
case "else-false": return Promise.resolve(errResult(ctx, "\\elif: cannot occur after \\else"));
|
|
207
|
+
case "none": return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
372
210
|
};
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
cond.setSavedQueryBufLen(savedLen);
|
|
401
|
-
return wasInactive
|
|
402
|
-
? Promise.resolve(truncResult(savedLen))
|
|
403
|
-
: Promise.resolve(okResult());
|
|
404
|
-
case "else-true":
|
|
405
|
-
case "else-false":
|
|
406
|
-
return Promise.resolve(errResult(ctx, "\\else: cannot occur after \\else"));
|
|
407
|
-
case "none":
|
|
408
|
-
return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
|
|
409
|
-
}
|
|
410
|
-
},
|
|
211
|
+
const cmdElse = {
|
|
212
|
+
name: "else",
|
|
213
|
+
argMode: "lex",
|
|
214
|
+
async run(ctx) {
|
|
215
|
+
const cond = getCondStack(ctx);
|
|
216
|
+
const top = cond.top();
|
|
217
|
+
if (top === void 0) return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
|
|
218
|
+
const wasInactive = isInactiveState(top.state);
|
|
219
|
+
const savedLen = top.savedQueryBufLen;
|
|
220
|
+
switch (top.state) {
|
|
221
|
+
case "true":
|
|
222
|
+
cond.setState("else-false");
|
|
223
|
+
cond.setSavedQueryBufLen(ctx.queryBuf.length);
|
|
224
|
+
return Promise.resolve(okResult());
|
|
225
|
+
case "false":
|
|
226
|
+
cond.setState("else-true");
|
|
227
|
+
cond.setSavedQueryBufLen(savedLen);
|
|
228
|
+
return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
|
|
229
|
+
case "ignored":
|
|
230
|
+
cond.setState("else-false");
|
|
231
|
+
cond.setSavedQueryBufLen(savedLen);
|
|
232
|
+
return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
|
|
233
|
+
case "else-true":
|
|
234
|
+
case "else-false": return Promise.resolve(errResult(ctx, "\\else: cannot occur after \\else"));
|
|
235
|
+
case "none": return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
411
238
|
};
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return wasInactive
|
|
425
|
-
? Promise.resolve(truncResult(savedLen))
|
|
426
|
-
: Promise.resolve(okResult());
|
|
427
|
-
},
|
|
239
|
+
const cmdEndif = {
|
|
240
|
+
name: "endif",
|
|
241
|
+
argMode: "lex",
|
|
242
|
+
async run(ctx) {
|
|
243
|
+
const cond = getCondStack(ctx);
|
|
244
|
+
const top = cond.top();
|
|
245
|
+
if (top === void 0) return Promise.resolve(errResult(ctx, "\\endif: no matching \\if"));
|
|
246
|
+
const wasInactive = isInactiveState(top.state);
|
|
247
|
+
const savedLen = top.savedQueryBufLen;
|
|
248
|
+
cond.pop();
|
|
249
|
+
return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
|
|
250
|
+
}
|
|
428
251
|
};
|
|
429
252
|
/** Names of the conditional commands — the mainloop dispatches these
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
253
|
+
* unconditionally (i.e. ignoring `cond.isActive()`) so an `\if false` block
|
|
254
|
+
* can still be closed by `\endif`. */
|
|
255
|
+
const COND_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
256
|
+
"if",
|
|
257
|
+
"elif",
|
|
258
|
+
"else",
|
|
259
|
+
"endif"
|
|
437
260
|
]);
|
|
261
|
+
//#endregion
|
|
262
|
+
export { COND_COMMAND_NAMES, attachCondStack, cmdElif, cmdElse, cmdEndif, cmdIf, createCondStack, getCondStack, parseBool };
|