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,515 +1,460 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* psql backslash-command argument scanner.
|
|
3
|
-
*
|
|
4
|
-
* Hand-port of PostgreSQL's `src/bin/psql/psqlscanslash.l`. The upstream is a
|
|
5
|
-
* flex-generated state machine with these exclusive states:
|
|
6
|
-
*
|
|
7
|
-
* - `xslashcmd` — reading the command name (the letters after `\`)
|
|
8
|
-
* - `xslashargstart` — skipping whitespace before the next arg; `|` at
|
|
9
|
-
* this position is special in `filepipe` mode
|
|
10
|
-
* - `xslasharg` — reading an unquoted arg (handles `:var`,
|
|
11
|
-
* `:'var'`, `:"var"` substitutions and the start of
|
|
12
|
-
* `'`, `"`, `` ` `` quoted runs)
|
|
13
|
-
* - `xslashquote` — inside `'…'` (C-string-style escapes processed)
|
|
14
|
-
* - `xslashbackquote` — inside `` `…` `` (variable expansion only; the
|
|
15
|
-
* body is shipped to the shell by upstream)
|
|
16
|
-
* - `xslashdquote` — inside `"…"` (literal copy, double quotes kept)
|
|
17
|
-
* - `xslashwholeline` — slurp rest of line, suppressing leading whitespace
|
|
18
|
-
* - `xslashend` — terminator (we don't model it here; the caller
|
|
19
|
-
* knows where the slash command ended)
|
|
20
|
-
*
|
|
21
|
-
* The TS port collapses these into a single {@link scanSlashArgs} function
|
|
22
|
-
* that takes the post-command-name remainder of the input line plus a
|
|
23
|
-
* {@link SlashArgMode} and returns the list of parsed arguments. We hand-roll
|
|
24
|
-
* the state machine instead of attempting to mechanically translate flex
|
|
25
|
-
* rules; the resulting code is easier to read and trivially testable.
|
|
26
|
-
*
|
|
27
|
-
* Behavioural notes vs upstream:
|
|
28
|
-
*
|
|
29
|
-
* - **Whole-line mode** returns a single-element array containing the entire
|
|
30
|
-
* rest-of-line, with leading whitespace suppressed. Empty input still
|
|
31
|
-
* yields `[]` so callers can treat the result uniformly.
|
|
32
|
-
* - **filepipe mode** treats a leading `|` as the start of a shell command
|
|
33
|
-
* and slurps the rest of the line as one argument. Anything else is
|
|
34
|
-
* handled as a normal arg.
|
|
35
|
-
* - **Variable substitution** matches upstream's three forms:
|
|
36
|
-
* `:varname` — plain expansion
|
|
37
|
-
* `:'varname'` — SQL-literal-quoted expansion
|
|
38
|
-
* `:"varname"` — SQL-identifier-quoted expansion
|
|
39
|
-
* `varname` is `[A-Za-z0-9_\x80-\xff]+` (upstream's `variable_char`). When
|
|
40
|
-
* the variable is unset, the colon form is emitted literally — matching
|
|
41
|
-
* the upstream `ECHO` fallback.
|
|
42
|
-
* - **no-vars mode** disables all `:var` substitution; the lexer emits the
|
|
43
|
-
* raw text. Useful for commands that should never expand variables
|
|
44
|
-
* (e.g. `\setenv`'s value argument).
|
|
45
|
-
* - **sql-id / sql-id-keep-case modes** post-process each arg through
|
|
46
|
-
* `dequoteDowncaseIdentifier`, which mirrors upstream's
|
|
47
|
-
* `dequote_downcase_identifier()`: collapse `"…"` quoting, double `""`
|
|
48
|
-
* into a single `"`, and (for `sql-id`) lowercase unquoted letters.
|
|
49
|
-
* - **Backticks** ARE executed here, synchronously via `child_process.execSync`
|
|
50
|
-
* on `sh -c <body>`. Variable references inside the backticked body are
|
|
51
|
-
* expanded first (matching upstream's `xslashbackquote` rules), then the
|
|
52
|
-
* resulting command is run with the inherited environment but NO shell
|
|
53
|
-
* state; the child's stdout (trimmed of one trailing newline) is the
|
|
54
|
-
* arg's value. Non-zero exits / spawn failures are reported on stderr
|
|
55
|
-
* in the upstream `psql:...: error: \!:` shape and substitute the empty
|
|
56
|
-
* string. This is the scanner-level analogue of upstream's `evaluate_backtick`.
|
|
57
|
-
* - **Inside-quote escapes** match upstream `xslashquote`: `\n \t \b \r \f`,
|
|
58
|
-
* octal `\ooo`, hex `\xhh`, and `\<other>` as a literal character. We
|
|
59
|
-
* apply them in-line so the returned arg contains the decoded value.
|
|
60
|
-
*/
|
|
61
|
-
import { execSync } from "node:child_process";
|
|
62
1
|
import { dequote } from "./stringutils.js";
|
|
63
|
-
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
//#region src/psql/scanner/slash.ts
|
|
4
|
+
/**
|
|
5
|
+
* psql backslash-command argument scanner.
|
|
6
|
+
*
|
|
7
|
+
* Hand-port of PostgreSQL's `src/bin/psql/psqlscanslash.l`. The upstream is a
|
|
8
|
+
* flex-generated state machine with these exclusive states:
|
|
9
|
+
*
|
|
10
|
+
* - `xslashcmd` — reading the command name (the letters after `\`)
|
|
11
|
+
* - `xslashargstart` — skipping whitespace before the next arg; `|` at
|
|
12
|
+
* this position is special in `filepipe` mode
|
|
13
|
+
* - `xslasharg` — reading an unquoted arg (handles `:var`,
|
|
14
|
+
* `:'var'`, `:"var"` substitutions and the start of
|
|
15
|
+
* `'`, `"`, `` ` `` quoted runs)
|
|
16
|
+
* - `xslashquote` — inside `'…'` (C-string-style escapes processed)
|
|
17
|
+
* - `xslashbackquote` — inside `` `…` `` (variable expansion only; the
|
|
18
|
+
* body is shipped to the shell by upstream)
|
|
19
|
+
* - `xslashdquote` — inside `"…"` (literal copy, double quotes kept)
|
|
20
|
+
* - `xslashwholeline` — slurp rest of line, suppressing leading whitespace
|
|
21
|
+
* - `xslashend` — terminator (we don't model it here; the caller
|
|
22
|
+
* knows where the slash command ended)
|
|
23
|
+
*
|
|
24
|
+
* The TS port collapses these into a single {@link scanSlashArgs} function
|
|
25
|
+
* that takes the post-command-name remainder of the input line plus a
|
|
26
|
+
* {@link SlashArgMode} and returns the list of parsed arguments. We hand-roll
|
|
27
|
+
* the state machine instead of attempting to mechanically translate flex
|
|
28
|
+
* rules; the resulting code is easier to read and trivially testable.
|
|
29
|
+
*
|
|
30
|
+
* Behavioural notes vs upstream:
|
|
31
|
+
*
|
|
32
|
+
* - **Whole-line mode** returns a single-element array containing the entire
|
|
33
|
+
* rest-of-line, with leading whitespace suppressed. Empty input still
|
|
34
|
+
* yields `[]` so callers can treat the result uniformly.
|
|
35
|
+
* - **filepipe mode** treats a leading `|` as the start of a shell command
|
|
36
|
+
* and slurps the rest of the line as one argument. Anything else is
|
|
37
|
+
* handled as a normal arg.
|
|
38
|
+
* - **Variable substitution** matches upstream's three forms:
|
|
39
|
+
* `:varname` — plain expansion
|
|
40
|
+
* `:'varname'` — SQL-literal-quoted expansion
|
|
41
|
+
* `:"varname"` — SQL-identifier-quoted expansion
|
|
42
|
+
* `varname` is `[A-Za-z0-9_\x80-\xff]+` (upstream's `variable_char`). When
|
|
43
|
+
* the variable is unset, the colon form is emitted literally — matching
|
|
44
|
+
* the upstream `ECHO` fallback.
|
|
45
|
+
* - **no-vars mode** disables all `:var` substitution; the lexer emits the
|
|
46
|
+
* raw text. Useful for commands that should never expand variables
|
|
47
|
+
* (e.g. `\setenv`'s value argument).
|
|
48
|
+
* - **sql-id / sql-id-keep-case modes** post-process each arg through
|
|
49
|
+
* `dequoteDowncaseIdentifier`, which mirrors upstream's
|
|
50
|
+
* `dequote_downcase_identifier()`: collapse `"…"` quoting, double `""`
|
|
51
|
+
* into a single `"`, and (for `sql-id`) lowercase unquoted letters.
|
|
52
|
+
* - **Backticks** ARE executed here, synchronously via `child_process.execSync`
|
|
53
|
+
* on `sh -c <body>`. Variable references inside the backticked body are
|
|
54
|
+
* expanded first (matching upstream's `xslashbackquote` rules), then the
|
|
55
|
+
* resulting command is run with the inherited environment but NO shell
|
|
56
|
+
* state; the child's stdout (trimmed of one trailing newline) is the
|
|
57
|
+
* arg's value. Non-zero exits / spawn failures are reported on stderr
|
|
58
|
+
* in the upstream `psql:...: error: \!:` shape and substitute the empty
|
|
59
|
+
* string. This is the scanner-level analogue of upstream's `evaluate_backtick`.
|
|
60
|
+
* - **Inside-quote escapes** match upstream `xslashquote`: `\n \t \b \r \f`,
|
|
61
|
+
* octal `\ooo`, hex `\xhh`, and `\<other>` as a literal character. We
|
|
62
|
+
* apply them in-line so the returned arg contains the decoded value.
|
|
63
|
+
*/
|
|
64
|
+
const WHITESPACE = " \n\r\f\v";
|
|
64
65
|
const VARIABLE_CHAR_RE = /[A-Za-z0-9_\x80-\xff]/;
|
|
65
|
-
const isVarChar = (c) => c !==
|
|
66
|
-
const isWhitespace = (c) => c !==
|
|
66
|
+
const isVarChar = (c) => c !== void 0 && VARIABLE_CHAR_RE.test(c);
|
|
67
|
+
const isWhitespace = (c) => c !== void 0 && WHITESPACE.includes(c);
|
|
67
68
|
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
* SQL-literal-quote a value for the `:'varname'` substitution form.
|
|
70
|
+
* Mirrors libpq's `PQescapeLiteral` for the common case: wrap in `'…'`,
|
|
71
|
+
* double any embedded `'`, and backslash-escape any embedded `\`. Upstream
|
|
72
|
+
* additionally emits an `E` prefix when the value contains backslashes; we
|
|
73
|
+
* preserve that behaviour for compatibility with code that round-trips
|
|
74
|
+
* through the SQL parser.
|
|
75
|
+
*/
|
|
75
76
|
const quoteSqlLiteral = (value) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
inner += c;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return needsEscape ? `E'${inner}'` : `'${inner}'`;
|
|
77
|
+
let needsEscape = false;
|
|
78
|
+
let inner = "";
|
|
79
|
+
for (const c of value) if (c === "'") inner += "''";
|
|
80
|
+
else if (c === "\\") {
|
|
81
|
+
inner += "\\\\";
|
|
82
|
+
needsEscape = true;
|
|
83
|
+
} else inner += c;
|
|
84
|
+
return needsEscape ? `E'${inner}'` : `'${inner}'`;
|
|
90
85
|
};
|
|
91
86
|
/**
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
* SQL-identifier-quote a value for the `:"varname"` substitution form.
|
|
88
|
+
* Wraps the value in `"…"` and doubles any embedded `"`.
|
|
89
|
+
*/
|
|
95
90
|
const quoteSqlIdent = (value) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
return `"${inner}"`;
|
|
91
|
+
let inner = "";
|
|
92
|
+
for (const c of value) inner += c === "\"" ? "\"\"" : c;
|
|
93
|
+
return `"${inner}"`;
|
|
101
94
|
};
|
|
102
95
|
/**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
* Match upstream's `dequote_downcase_identifier()`. Strips out `"…"` quoting
|
|
97
|
+
* (collapsing `""` to `"` inside quotes) and optionally downcases unquoted
|
|
98
|
+
* letters. The transformation is in-place semantically: a string like
|
|
99
|
+
* `FOO"BAR"BAZ` becomes `fooBARbaz` (when `downcase`) or `FOOBARBAZ`
|
|
100
|
+
* (otherwise).
|
|
101
|
+
*/
|
|
109
102
|
const dequoteDowncaseIdentifier = (str, downcase) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return out;
|
|
103
|
+
let out = "";
|
|
104
|
+
let inquotes = false;
|
|
105
|
+
let i = 0;
|
|
106
|
+
while (i < str.length) {
|
|
107
|
+
const c = str[i];
|
|
108
|
+
if (c === "\"") {
|
|
109
|
+
if (inquotes && str[i + 1] === "\"") {
|
|
110
|
+
out += "\"";
|
|
111
|
+
i += 2;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
inquotes = !inquotes;
|
|
115
|
+
i++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
out += downcase && !inquotes ? c.toLowerCase() : c;
|
|
119
|
+
i++;
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
130
122
|
};
|
|
131
123
|
/**
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
124
|
+
* Attempt to consume one of the `:var`, `:'var'`, `:"var"` variable
|
|
125
|
+
* substitution forms at position `i` in `s`. Returns the new index plus the
|
|
126
|
+
* substituted text, or `null` if no recognised form is present.
|
|
127
|
+
*
|
|
128
|
+
* Caller controls whether the colon forms are honoured at all via
|
|
129
|
+
* `varLookup`: pass `undefined` to disable substitution entirely (`no-vars`
|
|
130
|
+
* mode).
|
|
131
|
+
*/
|
|
140
132
|
const tryConsumeVarSubstitution = (s, i, varLookup) => {
|
|
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
|
-
const value = varLookup(name);
|
|
202
|
-
if (value === undefined) {
|
|
203
|
-
// Unset → emit literally so it stays visible. Upstream ECHOes the
|
|
204
|
-
// entire `:name` text in this case.
|
|
205
|
-
return { end: j, text: s.slice(i, j) };
|
|
206
|
-
}
|
|
207
|
-
return { end: j, text: value };
|
|
208
|
-
}
|
|
209
|
-
return null;
|
|
133
|
+
if (varLookup === void 0) return null;
|
|
134
|
+
if (s[i] !== ":") return null;
|
|
135
|
+
if (s[i + 1] === "{" && s[i + 2] === "?") {
|
|
136
|
+
let j = i + 3;
|
|
137
|
+
while (j < s.length && isVarChar(s[j])) j++;
|
|
138
|
+
if (j > i + 3 && s[j] === "}") {
|
|
139
|
+
const value = varLookup(s.slice(i + 3, j));
|
|
140
|
+
return {
|
|
141
|
+
end: j + 1,
|
|
142
|
+
text: value !== void 0 ? "TRUE" : "FALSE"
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
if (s[i + 1] === "\"") {
|
|
148
|
+
let j = i + 2;
|
|
149
|
+
while (j < s.length && isVarChar(s[j])) j++;
|
|
150
|
+
if (j > i + 2 && s[j] === "\"") {
|
|
151
|
+
const value = varLookup(s.slice(i + 2, j));
|
|
152
|
+
if (value === void 0) return {
|
|
153
|
+
end: j + 1,
|
|
154
|
+
text: s.slice(i, j + 1)
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
end: j + 1,
|
|
158
|
+
text: quoteSqlIdent(value)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
if (s[i + 1] === "'") {
|
|
164
|
+
let j = i + 2;
|
|
165
|
+
while (j < s.length && isVarChar(s[j])) j++;
|
|
166
|
+
if (j > i + 2 && s[j] === "'") {
|
|
167
|
+
const value = varLookup(s.slice(i + 2, j));
|
|
168
|
+
if (value === void 0) return {
|
|
169
|
+
end: j + 1,
|
|
170
|
+
text: s.slice(i, j + 1)
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
end: j + 1,
|
|
174
|
+
text: quoteSqlLiteral(value)
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
if (isVarChar(s[i + 1])) {
|
|
180
|
+
let j = i + 1;
|
|
181
|
+
while (j < s.length && isVarChar(s[j])) j++;
|
|
182
|
+
const value = varLookup(s.slice(i + 1, j));
|
|
183
|
+
if (value === void 0) return {
|
|
184
|
+
end: j,
|
|
185
|
+
text: s.slice(i, j)
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
end: j,
|
|
189
|
+
text: value
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
210
193
|
};
|
|
211
194
|
/**
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
195
|
+
* Process the contents of a `'…'` slash-quoted token: handle psql's C-style
|
|
196
|
+
* escapes (\n, \t, \b, \r, \f, octal, hex, and \<other>) and undouble `''`.
|
|
197
|
+
* The opening quote has already been consumed; we read until the matching
|
|
198
|
+
* closing quote and return the decoded payload plus the new index (pointing
|
|
199
|
+
* just past the closing quote).
|
|
200
|
+
*/
|
|
218
201
|
const consumeSingleQuoted = (s, start) => {
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
out += c;
|
|
296
|
-
i++;
|
|
297
|
-
}
|
|
298
|
-
// Unterminated — return what we have. Upstream reports an error; for the
|
|
299
|
-
// scanner-as-library shape we'd rather surface the partial text and let
|
|
300
|
-
// the caller decide. Tests cover both well-formed and unterminated cases.
|
|
301
|
-
return { end: i, text: out };
|
|
202
|
+
let out = "";
|
|
203
|
+
let i = start;
|
|
204
|
+
while (i < s.length) {
|
|
205
|
+
const c = s[i];
|
|
206
|
+
if (c === "'") {
|
|
207
|
+
if (s[i + 1] === "'") {
|
|
208
|
+
out += "'";
|
|
209
|
+
i += 2;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
end: i + 1,
|
|
214
|
+
text: out
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if (c === "\\" && i + 1 < s.length) {
|
|
218
|
+
const next = s[i + 1];
|
|
219
|
+
if (next === "n") {
|
|
220
|
+
out += "\n";
|
|
221
|
+
i += 2;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (next === "t") {
|
|
225
|
+
out += " ";
|
|
226
|
+
i += 2;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (next === "b") {
|
|
230
|
+
out += "\b";
|
|
231
|
+
i += 2;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (next === "r") {
|
|
235
|
+
out += "\r";
|
|
236
|
+
i += 2;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (next === "f") {
|
|
240
|
+
out += "\f";
|
|
241
|
+
i += 2;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (next >= "0" && next <= "7") {
|
|
245
|
+
const j = i + 1;
|
|
246
|
+
let octEnd = j;
|
|
247
|
+
while (octEnd < s.length && octEnd - j < 3 && s[octEnd] >= "0" && s[octEnd] <= "7") octEnd++;
|
|
248
|
+
const code = parseInt(s.slice(j, octEnd), 8);
|
|
249
|
+
out += String.fromCharCode(code);
|
|
250
|
+
i = octEnd;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (next === "x") {
|
|
254
|
+
const j = i + 2;
|
|
255
|
+
const hexRe = /[0-9a-fA-F]/;
|
|
256
|
+
let hexEnd = j;
|
|
257
|
+
while (hexEnd < s.length && hexEnd - j < 2 && hexRe.test(s[hexEnd])) hexEnd++;
|
|
258
|
+
if (hexEnd > j) {
|
|
259
|
+
const code = parseInt(s.slice(j, hexEnd), 16);
|
|
260
|
+
out += String.fromCharCode(code);
|
|
261
|
+
i = hexEnd;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
out += next;
|
|
266
|
+
i += 2;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
out += c;
|
|
270
|
+
i++;
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
end: i,
|
|
274
|
+
text: out
|
|
275
|
+
};
|
|
302
276
|
};
|
|
303
277
|
/**
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
278
|
+
* Process the contents of a `"…"` slash-quoted token. Upstream copies the
|
|
279
|
+
* body verbatim *including the double quotes themselves* (see `xslashdquote`
|
|
280
|
+
* rule, which ECHOes the opening dquote on entry). That preserves
|
|
281
|
+
* SQL-identifier semantics — the caller's `dequoteDowncaseIdentifier()` is
|
|
282
|
+
* what eventually unwraps the quotes for `sql-id` modes.
|
|
283
|
+
*/
|
|
310
284
|
const consumeDoubleQuoted = (s, start) => {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
285
|
+
let i = start;
|
|
286
|
+
while (i < s.length) {
|
|
287
|
+
if (s[i] === "\"") return {
|
|
288
|
+
end: i + 1,
|
|
289
|
+
text: s.slice(start - 1, i + 1)
|
|
290
|
+
};
|
|
291
|
+
i++;
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
end: i,
|
|
295
|
+
text: s.slice(start - 1, i)
|
|
296
|
+
};
|
|
320
297
|
};
|
|
321
298
|
/**
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
maxBuffer: 1 << 20,
|
|
338
|
-
}),
|
|
339
|
-
};
|
|
299
|
+
* Test seam for swapping the shell executor. Vitest sets `current` to its
|
|
300
|
+
* own mock; production uses `execSync(cmd, { shell: '/bin/sh' })`. Kept as
|
|
301
|
+
* an exported object so tests can flip it in `beforeEach` without monkey-
|
|
302
|
+
* patching `child_process`.
|
|
303
|
+
*/
|
|
304
|
+
const BACKTICK_EXECUTOR = { current: (cmd) => execSync(cmd, {
|
|
305
|
+
shell: "/bin/sh",
|
|
306
|
+
encoding: "utf8",
|
|
307
|
+
stdio: [
|
|
308
|
+
"ignore",
|
|
309
|
+
"pipe",
|
|
310
|
+
"inherit"
|
|
311
|
+
],
|
|
312
|
+
maxBuffer: 1 << 20
|
|
313
|
+
}) };
|
|
340
314
|
/**
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
315
|
+
* Execute the lexed body of a `` `…` `` token via `sh -c` and return its
|
|
316
|
+
* stdout with one trailing newline stripped (matching shell command-
|
|
317
|
+
* substitution convention). Errors are reported on stderr in the upstream
|
|
318
|
+
* `psql: error: \!: <command>: <message>` shape and substitute the empty
|
|
319
|
+
* string — so a failed backtick never aborts the surrounding slash command.
|
|
320
|
+
*
|
|
321
|
+
* Called only by {@link consumeBackQuoted}; lives at module scope so the
|
|
322
|
+
* scanner stays free of inline I/O and tests can spy on the executor via
|
|
323
|
+
* {@link BACKTICK_EXECUTOR}.
|
|
324
|
+
*/
|
|
351
325
|
const runBacktickCommand = (cmd) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
362
|
-
// Upstream prints `psql:file:line: error: \!: <cmd>: <msg>`. We don't
|
|
363
|
-
// have file/line context in the scanner; emit the prefix verbatim and
|
|
364
|
-
// include the command for diagnosis.
|
|
365
|
-
process.stderr.write(`psql: error: \\!: ${cmd}: ${msg}\n`);
|
|
366
|
-
return "";
|
|
367
|
-
}
|
|
326
|
+
if (cmd.length === 0) return "";
|
|
327
|
+
try {
|
|
328
|
+
const out = BACKTICK_EXECUTOR.current(cmd);
|
|
329
|
+
return out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
330
|
+
} catch (err) {
|
|
331
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
332
|
+
process.stderr.write(`psql: error: \\!: ${cmd}: ${msg}\n`);
|
|
333
|
+
return "";
|
|
334
|
+
}
|
|
368
335
|
};
|
|
369
336
|
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
337
|
+
* Process the contents of a `` `…` `` slash-backquoted token.
|
|
338
|
+
*
|
|
339
|
+
* Phase 1 (this function): consume the body, expanding `:var` references
|
|
340
|
+
* along the way (matching upstream's `xslashbackquote` rules).
|
|
341
|
+
*
|
|
342
|
+
* Phase 2 (delegated to {@link runBacktickCommand}): run the assembled
|
|
343
|
+
* command via `sh -c` and return its stdout.
|
|
344
|
+
*
|
|
345
|
+
* Returns the command's stdout (one trailing `\n` stripped). Unterminated
|
|
346
|
+
* backticks still execute the body so partial input doesn't silently
|
|
347
|
+
* succeed; tests cover both well-formed and unterminated cases.
|
|
348
|
+
*/
|
|
382
349
|
const consumeBackQuoted = (s, start, varLookup) => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
350
|
+
let inner = "";
|
|
351
|
+
let i = start;
|
|
352
|
+
while (i < s.length) {
|
|
353
|
+
const c = s[i];
|
|
354
|
+
if (c === "`") return {
|
|
355
|
+
end: i + 1,
|
|
356
|
+
text: runBacktickCommand(inner)
|
|
357
|
+
};
|
|
358
|
+
const sub = tryConsumeVarSubstitution(s, i, varLookup);
|
|
359
|
+
if (sub !== null) {
|
|
360
|
+
inner += sub.text;
|
|
361
|
+
i = sub.end;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
inner += c;
|
|
365
|
+
i++;
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
end: i,
|
|
369
|
+
text: runBacktickCommand(inner)
|
|
370
|
+
};
|
|
402
371
|
};
|
|
403
372
|
/**
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
373
|
+
* Lex a single slash-command argument starting at `s[i]`. Returns the parsed
|
|
374
|
+
* argument text and the index just past it, or `null` if no argument is
|
|
375
|
+
* available before end of input.
|
|
376
|
+
*/
|
|
408
377
|
const scanOneArg = (s, i, mode, varLookup) => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
i = sub.end;
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
out += c;
|
|
458
|
-
i++;
|
|
459
|
-
}
|
|
460
|
-
return { end: i, arg: out };
|
|
378
|
+
while (i < s.length && isWhitespace(s[i])) i++;
|
|
379
|
+
if (i >= s.length) return null;
|
|
380
|
+
if (mode === "filepipe" && s[i] === "|") {
|
|
381
|
+
const rest = s.slice(i);
|
|
382
|
+
return {
|
|
383
|
+
end: s.length,
|
|
384
|
+
arg: rest
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
let out = "";
|
|
388
|
+
while (i < s.length) {
|
|
389
|
+
const c = s[i];
|
|
390
|
+
if (isWhitespace(c)) break;
|
|
391
|
+
if (c === "\\") break;
|
|
392
|
+
if (c === "'") {
|
|
393
|
+
const r = consumeSingleQuoted(s, i + 1);
|
|
394
|
+
out += r.text;
|
|
395
|
+
i = r.end;
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (c === "\"") {
|
|
399
|
+
const r = consumeDoubleQuoted(s, i + 1);
|
|
400
|
+
out += r.text;
|
|
401
|
+
i = r.end;
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
if (c === "`") {
|
|
405
|
+
const r = consumeBackQuoted(s, i + 1, varLookup);
|
|
406
|
+
out += r.text;
|
|
407
|
+
i = r.end;
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
const sub = tryConsumeVarSubstitution(s, i, varLookup);
|
|
411
|
+
if (sub !== null) {
|
|
412
|
+
out += sub.text;
|
|
413
|
+
i = sub.end;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
out += c;
|
|
417
|
+
i++;
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
end: i,
|
|
421
|
+
arg: out
|
|
422
|
+
};
|
|
461
423
|
};
|
|
462
424
|
/**
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if (mode === "sql-id") {
|
|
497
|
-
arg = dequoteDowncaseIdentifier(arg, true);
|
|
498
|
-
}
|
|
499
|
-
else if (mode === "sql-id-keep-case") {
|
|
500
|
-
arg = dequoteDowncaseIdentifier(arg, false);
|
|
501
|
-
}
|
|
502
|
-
args.push(arg);
|
|
503
|
-
i = result.end;
|
|
504
|
-
// Consume the inter-arg whitespace so the next iteration starts cleanly.
|
|
505
|
-
while (i < input.length && isWhitespace(input[i]))
|
|
506
|
-
i++;
|
|
507
|
-
// Stop on a `\` — start of the next backslash command.
|
|
508
|
-
if (input[i] === "\\")
|
|
509
|
-
break;
|
|
510
|
-
}
|
|
511
|
-
return args;
|
|
425
|
+
* Scan the argument portion of a backslash command.
|
|
426
|
+
*
|
|
427
|
+
* @param input the rest of the input line *after* the command name (e.g.
|
|
428
|
+
* `" foo 'bar baz'"` for `\echo foo 'bar baz'`)
|
|
429
|
+
* @param mode argument processing mode — see {@link SlashArgMode}
|
|
430
|
+
* @param varLookup callback that resolves `:varname` references. Omit (or
|
|
431
|
+
* pass `undefined`) for `no-vars` mode behaviour even when
|
|
432
|
+
* `mode !== 'no-vars'`.
|
|
433
|
+
*
|
|
434
|
+
* @returns array of parsed argument strings; empty input yields `[]`.
|
|
435
|
+
*/
|
|
436
|
+
const scanSlashArgs = (input, mode, varLookup) => {
|
|
437
|
+
if (mode === "whole-line") {
|
|
438
|
+
let start = 0;
|
|
439
|
+
while (start < input.length && isWhitespace(input[start])) start++;
|
|
440
|
+
if (start >= input.length) return [];
|
|
441
|
+
return [input.slice(start)];
|
|
442
|
+
}
|
|
443
|
+
const effectiveLookup = mode === "no-vars" ? void 0 : varLookup;
|
|
444
|
+
const args = [];
|
|
445
|
+
let i = 0;
|
|
446
|
+
while (i < input.length) {
|
|
447
|
+
const result = scanOneArg(input, i, mode, effectiveLookup);
|
|
448
|
+
if (result === null) break;
|
|
449
|
+
let arg = result.arg;
|
|
450
|
+
if (mode === "sql-id") arg = dequoteDowncaseIdentifier(arg, true);
|
|
451
|
+
else if (mode === "sql-id-keep-case") arg = dequoteDowncaseIdentifier(arg, false);
|
|
452
|
+
args.push(arg);
|
|
453
|
+
i = result.end;
|
|
454
|
+
while (i < input.length && isWhitespace(input[i])) i++;
|
|
455
|
+
if (input[i] === "\\") break;
|
|
456
|
+
}
|
|
457
|
+
return args;
|
|
512
458
|
};
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
export { dequote };
|
|
459
|
+
//#endregion
|
|
460
|
+
export { BACKTICK_EXECUTOR, dequote, scanSlashArgs };
|