neonctl 2.28.0 → 2.29.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 +71 -71
- package/dist/analytics.js +35 -33
- package/dist/api.js +34 -34
- package/dist/auth.js +50 -44
- package/dist/cli.js +2 -2
- package/dist/commands/auth.js +58 -52
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +154 -147
- package/dist/commands/bucket.js +124 -118
- package/dist/commands/checkout.js +49 -49
- package/dist/commands/config.js +212 -88
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +96 -96
- package/dist/commands/databases.js +23 -23
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +97 -98
- package/dist/commands/index.js +26 -26
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +223 -166
- package/dist/commands/neon_auth.js +381 -363
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +101 -99
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +21 -21
- package/dist/commands/schema_diff.js +23 -23
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +17 -17
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +23 -16
- package/dist/current_branch_fast_path.js +6 -6
- package/dist/dev/env.js +34 -34
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +19 -19
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +94 -92
- package/dist/log.js +2 -2
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +15 -15
- package/dist/test_utils/fixtures.js +34 -31
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +13 -13
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +20 -15
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- package/package.json +6 -7
package/dist/psql/scanner/sql.js
CHANGED
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
* residue and current state, and the caller is expected to read more input and
|
|
85
85
|
* call again.
|
|
86
86
|
*/
|
|
87
|
-
import { initialScanState } from
|
|
88
|
-
import { tryConsumeVarSubstitution } from
|
|
87
|
+
import { initialScanState } from "../types/scanner.js";
|
|
88
|
+
import { tryConsumeVarSubstitution } from "./stringutils.js";
|
|
89
89
|
// ---------------------------------------------------------------------------
|
|
90
90
|
// Character predicates.
|
|
91
91
|
//
|
|
@@ -142,25 +142,25 @@ const cloneState = (s) => ({
|
|
|
142
142
|
// blocks, dollar-quoted bodies). Identifier letters are stored lower-cased.
|
|
143
143
|
// ---------------------------------------------------------------------------
|
|
144
144
|
const KEYWORD_PREFIX_LETTERS = new Set([
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
"c", // create
|
|
146
|
+
"f", // function
|
|
147
|
+
"p", // procedure
|
|
148
|
+
"o", // or
|
|
149
|
+
"r", // replace
|
|
150
150
|
]);
|
|
151
151
|
const PREFIX_MATCHES_CREATE_FN_OR_PROC = (letters) => {
|
|
152
|
-
if (letters[0] !==
|
|
152
|
+
if (letters[0] !== "c")
|
|
153
153
|
return false;
|
|
154
154
|
// CREATE FUNCTION
|
|
155
|
-
if (letters[1] ===
|
|
155
|
+
if (letters[1] === "f")
|
|
156
156
|
return true;
|
|
157
157
|
// CREATE PROCEDURE
|
|
158
|
-
if (letters[1] ===
|
|
158
|
+
if (letters[1] === "p")
|
|
159
159
|
return true;
|
|
160
160
|
// CREATE OR REPLACE FUNCTION / PROCEDURE
|
|
161
|
-
if (letters[1] ===
|
|
162
|
-
letters[2] ===
|
|
163
|
-
(letters[3] ===
|
|
161
|
+
if (letters[1] === "o" &&
|
|
162
|
+
letters[2] === "r" &&
|
|
163
|
+
(letters[3] === "f" || letters[3] === "p")) {
|
|
164
164
|
return true;
|
|
165
165
|
}
|
|
166
166
|
return false;
|
|
@@ -178,7 +178,7 @@ const maybeTrackBeginEnd = (st, word) => {
|
|
|
178
178
|
// PROCEDURE shape. Subsequent identifiers still bump `identifierCount`
|
|
179
179
|
// — that lets the prefix slots stay aligned with the first 4 idents only.
|
|
180
180
|
if (st.identifierCount === 0) {
|
|
181
|
-
st.identifierLetters = [
|
|
181
|
+
st.identifierLetters = ["", "", "", ""];
|
|
182
182
|
}
|
|
183
183
|
if (st.identifierCount < st.identifierLetters.length &&
|
|
184
184
|
KEYWORD_PREFIX_LETTERS.has(lower[0])) {
|
|
@@ -187,33 +187,33 @@ const maybeTrackBeginEnd = (st, word) => {
|
|
|
187
187
|
st.identifierCount++;
|
|
188
188
|
if (!PREFIX_MATCHES_CREATE_FN_OR_PROC(st.identifierLetters))
|
|
189
189
|
return;
|
|
190
|
-
if (lower ===
|
|
190
|
+
if (lower === "begin") {
|
|
191
191
|
st.beginDepth++;
|
|
192
192
|
}
|
|
193
|
-
else if (lower ===
|
|
193
|
+
else if (lower === "case") {
|
|
194
194
|
// Upstream comment: "CASE also ends with END. We only need to track
|
|
195
195
|
// this if we are already inside a BEGIN." Guard so `SELECT CASE WHEN
|
|
196
196
|
// ... END` outside a function body doesn't double-bump the counter.
|
|
197
197
|
if (st.beginDepth >= 1)
|
|
198
198
|
st.beginDepth++;
|
|
199
199
|
}
|
|
200
|
-
else if (lower ===
|
|
200
|
+
else if (lower === "end") {
|
|
201
201
|
if (st.beginDepth > 0)
|
|
202
202
|
st.beginDepth--;
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
205
|
const matchDollarDelim = (s, i) => {
|
|
206
|
-
if (s[i] !==
|
|
206
|
+
if (s[i] !== "$")
|
|
207
207
|
return null;
|
|
208
208
|
// Empty tag: `$$`
|
|
209
|
-
if (s[i + 1] ===
|
|
210
|
-
return { tag:
|
|
209
|
+
if (s[i + 1] === "$")
|
|
210
|
+
return { tag: "", end: i + 2 };
|
|
211
211
|
if (!isIdentStart(s[i + 1]))
|
|
212
212
|
return null;
|
|
213
213
|
let j = i + 2;
|
|
214
214
|
while (j < s.length && isIdentCont(s[j]))
|
|
215
215
|
j++;
|
|
216
|
-
if (s[j] !==
|
|
216
|
+
if (s[j] !== "$")
|
|
217
217
|
return null;
|
|
218
218
|
return { tag: s.slice(i + 1, j), end: j + 1 };
|
|
219
219
|
};
|
|
@@ -230,7 +230,7 @@ const isExtendedStringStart = (input, quotePos) => {
|
|
|
230
230
|
if (quotePos === 0)
|
|
231
231
|
return false;
|
|
232
232
|
const prev = input[quotePos - 1];
|
|
233
|
-
if (prev !==
|
|
233
|
+
if (prev !== "E" && prev !== "e")
|
|
234
234
|
return false;
|
|
235
235
|
// Must be a standalone E — not part of a longer identifier.
|
|
236
236
|
if (quotePos >= 2 && isIdentCont(input[quotePos - 2]))
|
|
@@ -249,20 +249,20 @@ const consumeXeEscape = (input, i) => {
|
|
|
249
249
|
const n = input[i + 1];
|
|
250
250
|
if (n === undefined)
|
|
251
251
|
return 1; // trailing backslash at EOF — caller stays open
|
|
252
|
-
if (n >=
|
|
252
|
+
if (n >= "0" && n <= "7") {
|
|
253
253
|
// Octal \ooo (1..3 digits)
|
|
254
254
|
let k = i + 2;
|
|
255
255
|
let count = 1;
|
|
256
256
|
while (k < input.length &&
|
|
257
257
|
count < 3 &&
|
|
258
|
-
input[k] >=
|
|
259
|
-
input[k] <=
|
|
258
|
+
input[k] >= "0" &&
|
|
259
|
+
input[k] <= "7") {
|
|
260
260
|
k++;
|
|
261
261
|
count++;
|
|
262
262
|
}
|
|
263
263
|
return k - i;
|
|
264
264
|
}
|
|
265
|
-
if (n ===
|
|
265
|
+
if (n === "x") {
|
|
266
266
|
let k = i + 2;
|
|
267
267
|
let count = 0;
|
|
268
268
|
while (k < input.length && count < 2 && /[0-9a-fA-F]/.test(input[k])) {
|
|
@@ -271,7 +271,7 @@ const consumeXeEscape = (input, i) => {
|
|
|
271
271
|
}
|
|
272
272
|
return k - i; // even \x with no hex digits consumes 2 chars (matches xeunicodefail vibe)
|
|
273
273
|
}
|
|
274
|
-
if (n ===
|
|
274
|
+
if (n === "u") {
|
|
275
275
|
let k = i + 2;
|
|
276
276
|
let count = 0;
|
|
277
277
|
while (k < input.length && count < 4 && /[0-9a-fA-F]/.test(input[k])) {
|
|
@@ -280,7 +280,7 @@ const consumeXeEscape = (input, i) => {
|
|
|
280
280
|
}
|
|
281
281
|
return k - i;
|
|
282
282
|
}
|
|
283
|
-
if (n ===
|
|
283
|
+
if (n === "U") {
|
|
284
284
|
let k = i + 2;
|
|
285
285
|
let count = 0;
|
|
286
286
|
while (k < input.length && count < 8 && /[0-9a-fA-F]/.test(input[k])) {
|
|
@@ -313,12 +313,12 @@ const tryQuoteContinue = (input, i) => {
|
|
|
313
313
|
let sawNewline = false;
|
|
314
314
|
while (k < input.length) {
|
|
315
315
|
const c = input[k];
|
|
316
|
-
if (c ===
|
|
316
|
+
if (c === "\n" || c === "\r") {
|
|
317
317
|
sawNewline = true;
|
|
318
318
|
k++;
|
|
319
319
|
continue;
|
|
320
320
|
}
|
|
321
|
-
if (c ===
|
|
321
|
+
if (c === " " || c === "\t" || c === "\f" || c === "\v") {
|
|
322
322
|
k++;
|
|
323
323
|
continue;
|
|
324
324
|
}
|
|
@@ -338,7 +338,7 @@ const tryQuoteContinue = (input, i) => {
|
|
|
338
338
|
const skipLineComment = (input, i) => {
|
|
339
339
|
// Assumes input[i] === '-' and input[i+1] === '-'.
|
|
340
340
|
let k = i + 2;
|
|
341
|
-
while (k < input.length && input[k] !==
|
|
341
|
+
while (k < input.length && input[k] !== "\n" && input[k] !== "\r")
|
|
342
342
|
k++;
|
|
343
343
|
return k;
|
|
344
344
|
};
|
|
@@ -355,16 +355,16 @@ const skipLineComment = (input, i) => {
|
|
|
355
355
|
// ---------------------------------------------------------------------------
|
|
356
356
|
const computePromptStatus = (state) => {
|
|
357
357
|
if (state.inBlockComment > 0)
|
|
358
|
-
return
|
|
358
|
+
return "comment";
|
|
359
359
|
if (state.inSingleQuote)
|
|
360
|
-
return
|
|
360
|
+
return "continue-quote";
|
|
361
361
|
if (state.inDoubleQuote)
|
|
362
|
-
return
|
|
362
|
+
return "continue-dquote";
|
|
363
363
|
if (state.dollarTag !== null)
|
|
364
|
-
return
|
|
364
|
+
return "continue-dollar";
|
|
365
365
|
if (state.parenDepth > 0)
|
|
366
|
-
return
|
|
367
|
-
return
|
|
366
|
+
return "paren";
|
|
367
|
+
return "continue";
|
|
368
368
|
};
|
|
369
369
|
export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
370
370
|
const singleline = options?.singleline ?? false;
|
|
@@ -372,7 +372,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
372
372
|
const st = cloneState(state ?? initialScanState());
|
|
373
373
|
// SQL accumulator. We append characters as we scan; this matches upstream's
|
|
374
374
|
// `output_buf` which receives all ECHOed text.
|
|
375
|
-
let sql =
|
|
375
|
+
let sql = "";
|
|
376
376
|
let i = 0;
|
|
377
377
|
// Convenience: emit characters from `from` (inclusive) up to `to` (exclusive)
|
|
378
378
|
// into the SQL accumulator and advance the cursor.
|
|
@@ -384,15 +384,15 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
384
384
|
const c = input[i];
|
|
385
385
|
// --- Inside a block comment: look for nested opens and closes. ---
|
|
386
386
|
if (st.inBlockComment > 0) {
|
|
387
|
-
if (c ===
|
|
387
|
+
if (c === "/" && input[i + 1] === "*") {
|
|
388
388
|
st.inBlockComment++;
|
|
389
|
-
sql +=
|
|
389
|
+
sql += "/*";
|
|
390
390
|
i += 2;
|
|
391
391
|
continue;
|
|
392
392
|
}
|
|
393
|
-
if (c ===
|
|
393
|
+
if (c === "*" && input[i + 1] === "/") {
|
|
394
394
|
st.inBlockComment--;
|
|
395
|
-
sql +=
|
|
395
|
+
sql += "*/";
|
|
396
396
|
i += 2;
|
|
397
397
|
continue;
|
|
398
398
|
}
|
|
@@ -402,7 +402,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
402
402
|
}
|
|
403
403
|
// --- Inside a single-quoted string (standard or extended). ---
|
|
404
404
|
if (st.inSingleQuote) {
|
|
405
|
-
if (st.inEscapeString && c ===
|
|
405
|
+
if (st.inEscapeString && c === "\\") {
|
|
406
406
|
const n = consumeXeEscape(input, i);
|
|
407
407
|
emit(i, i + n);
|
|
408
408
|
continue;
|
|
@@ -460,7 +460,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
460
460
|
}
|
|
461
461
|
// --- Inside a dollar-quoted string. ---
|
|
462
462
|
if (st.dollarTag !== null) {
|
|
463
|
-
if (c ===
|
|
463
|
+
if (c === "$") {
|
|
464
464
|
const m = matchDollarDelim(input, i);
|
|
465
465
|
if (m !== null && m.tag === st.dollarTag) {
|
|
466
466
|
sql += input.slice(i, m.end);
|
|
@@ -471,7 +471,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
471
471
|
// Either not a delim or a non-matching tag: consume just the $ and
|
|
472
472
|
// keep scanning. This matches upstream's `<xdolq>.` fallback which
|
|
473
473
|
// ECHOes the `$` and continues.
|
|
474
|
-
sql +=
|
|
474
|
+
sql += "$";
|
|
475
475
|
i++;
|
|
476
476
|
continue;
|
|
477
477
|
}
|
|
@@ -492,28 +492,28 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
492
492
|
// whitespace-only line emits the newline and keeps scanning so it never
|
|
493
493
|
// dispatches an empty statement.
|
|
494
494
|
if (singleline &&
|
|
495
|
-
(c ===
|
|
495
|
+
(c === "\n" || c === "\r") &&
|
|
496
496
|
st.beginDepth === 0 &&
|
|
497
497
|
sql.trim().length > 0) {
|
|
498
498
|
sql += c;
|
|
499
499
|
i++;
|
|
500
500
|
// Reset per-statement state, mirroring the `;` boundary below.
|
|
501
501
|
return {
|
|
502
|
-
kind:
|
|
502
|
+
kind: "semicolon",
|
|
503
503
|
sql,
|
|
504
504
|
consumed: i,
|
|
505
505
|
nextState: initialScanState(),
|
|
506
506
|
};
|
|
507
507
|
}
|
|
508
508
|
// Block comment start.
|
|
509
|
-
if (c ===
|
|
509
|
+
if (c === "/" && input[i + 1] === "*") {
|
|
510
510
|
st.inBlockComment = 1;
|
|
511
|
-
sql +=
|
|
511
|
+
sql += "/*";
|
|
512
512
|
i += 2;
|
|
513
513
|
continue;
|
|
514
514
|
}
|
|
515
515
|
// Line comment.
|
|
516
|
-
if (c ===
|
|
516
|
+
if (c === "-" && input[i + 1] === "-") {
|
|
517
517
|
const end = skipLineComment(input, i);
|
|
518
518
|
sql += input.slice(i, end);
|
|
519
519
|
i = end;
|
|
@@ -538,7 +538,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
538
538
|
continue;
|
|
539
539
|
}
|
|
540
540
|
// Dollar-quoted string start.
|
|
541
|
-
if (c ===
|
|
541
|
+
if (c === "$") {
|
|
542
542
|
const m = matchDollarDelim(input, i);
|
|
543
543
|
if (m !== null) {
|
|
544
544
|
sql += input.slice(i, m.end);
|
|
@@ -547,19 +547,19 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
547
547
|
continue;
|
|
548
548
|
}
|
|
549
549
|
// Lone `$` (e.g. param `$1` or just bare `$`): emit and continue.
|
|
550
|
-
sql +=
|
|
550
|
+
sql += "$";
|
|
551
551
|
i++;
|
|
552
552
|
continue;
|
|
553
553
|
}
|
|
554
554
|
// Parentheses tracking.
|
|
555
|
-
if (c ===
|
|
556
|
-
sql +=
|
|
555
|
+
if (c === "(") {
|
|
556
|
+
sql += "(";
|
|
557
557
|
i++;
|
|
558
558
|
st.parenDepth++;
|
|
559
559
|
continue;
|
|
560
560
|
}
|
|
561
|
-
if (c ===
|
|
562
|
-
sql +=
|
|
561
|
+
if (c === ")") {
|
|
562
|
+
sql += ")";
|
|
563
563
|
i++;
|
|
564
564
|
if (st.parenDepth > 0)
|
|
565
565
|
st.parenDepth--;
|
|
@@ -572,8 +572,8 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
572
572
|
// ATOMIC SELECT 1; SELECT 2; END;`) do not terminate the surrounding
|
|
573
573
|
// CREATE statement. The depth-gated case falls through to the catch-all,
|
|
574
574
|
// which emits the `;` and continues scanning.
|
|
575
|
-
if (c ===
|
|
576
|
-
sql +=
|
|
575
|
+
if (c === ";" && st.parenDepth === 0 && st.beginDepth === 0) {
|
|
576
|
+
sql += ";";
|
|
577
577
|
i++;
|
|
578
578
|
// Reset per-statement state. (parenDepth, dollarTag, comment depths,
|
|
579
579
|
// quote flags, beginDepth, and identifier tracking are all zero here
|
|
@@ -587,7 +587,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
587
587
|
// looked at yet; we hand that back inside `sql` only if we'd consumed
|
|
588
588
|
// it. Since we returned right after the `;`, sql ends in `;`.
|
|
589
589
|
return {
|
|
590
|
-
kind:
|
|
590
|
+
kind: "semicolon",
|
|
591
591
|
sql,
|
|
592
592
|
consumed: i,
|
|
593
593
|
nextState: next,
|
|
@@ -604,12 +604,12 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
604
604
|
// hands back the buffered SQL alongside the command name + remaining
|
|
605
605
|
// args; the mainloop forwards it into `BackslashContext.queryBuf` and the
|
|
606
606
|
// command's `run()` is free to read or ignore it.
|
|
607
|
-
if (c ===
|
|
607
|
+
if (c === "\\") {
|
|
608
608
|
// Upstream special: `\;` and `\:` are forced into the query buffer (so a
|
|
609
609
|
// user can write `SELECT 1\;` to suppress immediate dispatch). We honour
|
|
610
610
|
// those by emitting just the second char and not breaking.
|
|
611
611
|
const nxt = input[i + 1];
|
|
612
|
-
if (nxt ===
|
|
612
|
+
if (nxt === ";" || nxt === ":") {
|
|
613
613
|
sql += nxt;
|
|
614
614
|
i += 2;
|
|
615
615
|
continue;
|
|
@@ -634,7 +634,8 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
634
634
|
// modifier on `\d+`/`\dt+`). Underscore is required for psql's
|
|
635
635
|
// multi-word commands: `\lo_import`, `\lo_export`, `\lo_list`,
|
|
636
636
|
// `\lo_unlink`, `\bind_named`, `\close_prepared`.
|
|
637
|
-
while (cmdEnd < input.length &&
|
|
637
|
+
while (cmdEnd < input.length &&
|
|
638
|
+
/[A-Za-z0-9_+]/.test(input[cmdEnd])) {
|
|
638
639
|
cmdEnd++;
|
|
639
640
|
}
|
|
640
641
|
}
|
|
@@ -657,15 +658,16 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
657
658
|
// commands enter whole-line mode only when the first non-whitespace
|
|
658
659
|
// character is `|` — matching upstream's `<xslashargstart>` rule.
|
|
659
660
|
const argMode = slashCmdMode?.(cmd);
|
|
660
|
-
let consumeWholeLine = argMode ===
|
|
661
|
-
if (argMode ===
|
|
661
|
+
let consumeWholeLine = argMode === "whole-line";
|
|
662
|
+
if (argMode === "filepipe") {
|
|
662
663
|
// Skip leading whitespace inside the arg; if the next char is `|`,
|
|
663
664
|
// upstream switches into `<xslashwholeline>` for the rest of the line.
|
|
664
665
|
let p = cmdEnd;
|
|
665
|
-
while (p < input.length &&
|
|
666
|
+
while (p < input.length &&
|
|
667
|
+
(input[p] === " " || input[p] === "\t")) {
|
|
666
668
|
p++;
|
|
667
669
|
}
|
|
668
|
-
if (input[p] ===
|
|
670
|
+
if (input[p] === "|")
|
|
669
671
|
consumeWholeLine = true;
|
|
670
672
|
}
|
|
671
673
|
// Rest of the line — up to a newline OR (for normal-mode commands)
|
|
@@ -699,8 +701,8 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
699
701
|
// the trailing `\` as a fresh empty-name backslash command.
|
|
700
702
|
let sawDoubleBackslashSeparator = false;
|
|
701
703
|
while (restEnd < input.length &&
|
|
702
|
-
input[restEnd] !==
|
|
703
|
-
input[restEnd] !==
|
|
704
|
+
input[restEnd] !== "\n" &&
|
|
705
|
+
input[restEnd] !== "\r") {
|
|
704
706
|
const ch = input[restEnd];
|
|
705
707
|
if (consumeWholeLine) {
|
|
706
708
|
// Whole-line / filepipe-pipe path: never break on `\`. Just walk
|
|
@@ -710,7 +712,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
710
712
|
}
|
|
711
713
|
if (inSingle) {
|
|
712
714
|
// C-style `\'` escape inside single quotes.
|
|
713
|
-
if (ch ===
|
|
715
|
+
if (ch === "\\" && input[restEnd + 1] !== undefined) {
|
|
714
716
|
restEnd += 2;
|
|
715
717
|
continue;
|
|
716
718
|
}
|
|
@@ -722,17 +724,17 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
722
724
|
inDouble = false;
|
|
723
725
|
}
|
|
724
726
|
else if (inBack) {
|
|
725
|
-
if (ch ===
|
|
727
|
+
if (ch === "`")
|
|
726
728
|
inBack = false;
|
|
727
729
|
}
|
|
728
730
|
else {
|
|
729
|
-
if (ch ===
|
|
731
|
+
if (ch === "\\") {
|
|
730
732
|
// `\\` = "flush and continue" separator: end this command's
|
|
731
733
|
// args HERE, but consume both backslashes so the next scan
|
|
732
734
|
// resumes immediately after the pair. A lone `\` is a regular
|
|
733
735
|
// next-slash-cmd boundary; we stop at it without consuming so
|
|
734
736
|
// the next iteration picks it up.
|
|
735
|
-
if (input[restEnd + 1] ===
|
|
737
|
+
if (input[restEnd + 1] === "\\") {
|
|
736
738
|
sawDoubleBackslashSeparator = true;
|
|
737
739
|
}
|
|
738
740
|
break;
|
|
@@ -741,13 +743,15 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
741
743
|
inSingle = true;
|
|
742
744
|
else if (ch === '"')
|
|
743
745
|
inDouble = true;
|
|
744
|
-
else if (ch ===
|
|
746
|
+
else if (ch === "`")
|
|
745
747
|
inBack = true;
|
|
746
748
|
}
|
|
747
749
|
restEnd++;
|
|
748
750
|
}
|
|
749
751
|
const rest = input.slice(cmdEnd, restEnd);
|
|
750
|
-
const consumed = sawDoubleBackslashSeparator
|
|
752
|
+
const consumed = sawDoubleBackslashSeparator
|
|
753
|
+
? restEnd + 2
|
|
754
|
+
: restEnd;
|
|
751
755
|
// Note: we *don't* consume the newline; it's left for the next chunk
|
|
752
756
|
// so caller can see PROMPT1 reset cleanly. Upstream's
|
|
753
757
|
// `psql_scan_slash_command_end()` does eat the trailing newline, but
|
|
@@ -758,7 +762,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
758
762
|
// from the working chunk so the next statement's queryBuf doesn't
|
|
759
763
|
// pick it up via the `eof` accumulation path.
|
|
760
764
|
return {
|
|
761
|
-
kind:
|
|
765
|
+
kind: "backslash",
|
|
762
766
|
cmd,
|
|
763
767
|
rest,
|
|
764
768
|
sql,
|
|
@@ -778,12 +782,12 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
778
782
|
// rule before the `:{variable}` rule can fire. We do not need to gate
|
|
779
783
|
// this on `varLookup` — without substitution the result is byte-
|
|
780
784
|
// identical to the catch-all path.
|
|
781
|
-
if (c ===
|
|
782
|
-
sql +=
|
|
785
|
+
if (c === ":" && input[i + 1] === ":") {
|
|
786
|
+
sql += "::";
|
|
783
787
|
i += 2;
|
|
784
788
|
continue;
|
|
785
789
|
}
|
|
786
|
-
if (c ===
|
|
790
|
+
if (c === ":") {
|
|
787
791
|
const sub = tryConsumeVarSubstitution(input, i, varLookup);
|
|
788
792
|
if (sub !== null) {
|
|
789
793
|
sql += sub.text;
|
|
@@ -827,7 +831,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
827
831
|
const promptStatus = computePromptStatus(st);
|
|
828
832
|
st.promptStatus = promptStatus;
|
|
829
833
|
return {
|
|
830
|
-
kind:
|
|
834
|
+
kind: "incomplete",
|
|
831
835
|
sql,
|
|
832
836
|
consumed: i,
|
|
833
837
|
nextState: cloneState(st),
|
|
@@ -839,7 +843,7 @@ export const scanSql = (input, state, varLookup, slashCmdMode, options) => {
|
|
|
839
843
|
// mainloop bypasses the scanner and forwards raw lines until `\.`. See the
|
|
840
844
|
// file header for the contract.
|
|
841
845
|
return {
|
|
842
|
-
kind:
|
|
846
|
+
kind: "eof",
|
|
843
847
|
sql,
|
|
844
848
|
consumed: i,
|
|
845
849
|
nextState: cloneState(st),
|
|
@@ -873,7 +877,7 @@ export const splitStatements = (input, varLookup, slashCmdMode, options) => {
|
|
|
873
877
|
if (++safety > input.length + 10)
|
|
874
878
|
break;
|
|
875
879
|
const r = scanSql(remaining, state, varLookup, slashCmdMode, options);
|
|
876
|
-
if (r.kind ===
|
|
880
|
+
if (r.kind === "semicolon") {
|
|
877
881
|
// When the caller requested variable substitution, the scanner has
|
|
878
882
|
// already applied it to `r.sql` — we must push the transformed text,
|
|
879
883
|
// not the raw input slice. Without substitution the slice and `r.sql`
|
|
@@ -884,7 +888,7 @@ export const splitStatements = (input, varLookup, slashCmdMode, options) => {
|
|
|
884
888
|
state = r.nextState;
|
|
885
889
|
continue;
|
|
886
890
|
}
|
|
887
|
-
if (r.kind ===
|
|
891
|
+
if (r.kind === "backslash") {
|
|
888
892
|
// Emit the consumed input slice verbatim (covers any leading whitespace
|
|
889
893
|
// that scanSql skipped over before the `\`, plus `\cmd rest`). The
|
|
890
894
|
// slash-arg scanner will do its own variable expansion when the
|
|
@@ -894,7 +898,7 @@ export const splitStatements = (input, varLookup, slashCmdMode, options) => {
|
|
|
894
898
|
state = r.nextState;
|
|
895
899
|
continue;
|
|
896
900
|
}
|
|
897
|
-
if (r.kind ===
|
|
901
|
+
if (r.kind === "eof" || r.kind === "incomplete") {
|
|
898
902
|
// No more boundaries in this input. Append residue if non-empty.
|
|
899
903
|
// Same as the semicolon branch: emit `r.sql` (with substitutions
|
|
900
904
|
// applied) when `varLookup` is set, otherwise pass the raw slice
|
|
@@ -902,7 +906,7 @@ export const splitStatements = (input, varLookup, slashCmdMode, options) => {
|
|
|
902
906
|
if (remaining.length > 0) {
|
|
903
907
|
out.push(varLookup ? r.sql : remaining);
|
|
904
908
|
}
|
|
905
|
-
remaining =
|
|
909
|
+
remaining = "";
|
|
906
910
|
break;
|
|
907
911
|
}
|
|
908
912
|
}
|
|
@@ -85,7 +85,7 @@ export const strtokx = (input, whitespace, delim, quote, escape, eAcceptInUnquot
|
|
|
85
85
|
while (i < n && whitespace.includes(input[i]))
|
|
86
86
|
i++;
|
|
87
87
|
if (i >= n) {
|
|
88
|
-
return { token: null, rest:
|
|
88
|
+
return { token: null, rest: "" };
|
|
89
89
|
}
|
|
90
90
|
// 2. Single-character delim token.
|
|
91
91
|
if (delim.length > 0 && delim.includes(input[i])) {
|
|
@@ -112,7 +112,7 @@ export const strtokx = (input, whitespace, delim, quote, escape, eAcceptInUnquot
|
|
|
112
112
|
eAcceptInUnquoted.includes(input[p]) &&
|
|
113
113
|
input[p + 1] === "'") {
|
|
114
114
|
effectiveQuote = "'";
|
|
115
|
-
effectiveEscape =
|
|
115
|
+
effectiveEscape = "\\";
|
|
116
116
|
p++;
|
|
117
117
|
}
|
|
118
118
|
if (effectiveQuote.length > 0 && effectiveQuote.includes(input[p])) {
|
|
@@ -121,7 +121,9 @@ export const strtokx = (input, whitespace, delim, quote, escape, eAcceptInUnquot
|
|
|
121
121
|
p++; // step over opening quote
|
|
122
122
|
while (p < n) {
|
|
123
123
|
const c = input[p];
|
|
124
|
-
if (effectiveEscape.length > 0 &&
|
|
124
|
+
if (effectiveEscape.length > 0 &&
|
|
125
|
+
c === effectiveEscape &&
|
|
126
|
+
p + 1 < n) {
|
|
125
127
|
// escape + anything (except end-of-input) is a literal data char
|
|
126
128
|
p += 2;
|
|
127
129
|
continue;
|
|
@@ -185,10 +187,10 @@ export const strtokx = (input, whitespace, delim, quote, escape, eAcceptInUnquot
|
|
|
185
187
|
*/
|
|
186
188
|
export const quoteIfNeeded = (value, escapeChars, quote) => {
|
|
187
189
|
if (quote.length !== 1) {
|
|
188
|
-
throw new Error(
|
|
190
|
+
throw new Error("quoteIfNeeded: quote must be exactly one character");
|
|
189
191
|
}
|
|
190
192
|
let needsQuotes = false;
|
|
191
|
-
let escaped =
|
|
193
|
+
let escaped = "";
|
|
192
194
|
for (const c of value) {
|
|
193
195
|
if (c === quote) {
|
|
194
196
|
needsQuotes = true;
|
|
@@ -214,14 +216,16 @@ export const quoteIfNeeded = (value, escapeChars, quote) => {
|
|
|
214
216
|
*/
|
|
215
217
|
export const dequote = (value, quote) => {
|
|
216
218
|
if (quote.length !== 1) {
|
|
217
|
-
throw new Error(
|
|
219
|
+
throw new Error("dequote: quote must be exactly one character");
|
|
218
220
|
}
|
|
219
|
-
if (value.length < 2 ||
|
|
221
|
+
if (value.length < 2 ||
|
|
222
|
+
!value.startsWith(quote) ||
|
|
223
|
+
!value.endsWith(quote)) {
|
|
220
224
|
return value;
|
|
221
225
|
}
|
|
222
226
|
const inner = value.slice(1, -1);
|
|
223
227
|
// Undouble embedded quote chars.
|
|
224
|
-
let out =
|
|
228
|
+
let out = "";
|
|
225
229
|
let i = 0;
|
|
226
230
|
while (i < inner.length) {
|
|
227
231
|
if (inner[i] === quote && inner[i + 1] === quote) {
|
|
@@ -246,12 +250,12 @@ export const dequote = (value, quote) => {
|
|
|
246
250
|
*/
|
|
247
251
|
export const quoteSqlLiteral = (value) => {
|
|
248
252
|
let needsEscape = false;
|
|
249
|
-
let inner =
|
|
253
|
+
let inner = "";
|
|
250
254
|
for (const c of value) {
|
|
251
255
|
if (c === "'")
|
|
252
256
|
inner += "''";
|
|
253
|
-
else if (c ===
|
|
254
|
-
inner +=
|
|
257
|
+
else if (c === "\\") {
|
|
258
|
+
inner += "\\\\";
|
|
255
259
|
needsEscape = true;
|
|
256
260
|
}
|
|
257
261
|
else {
|
|
@@ -265,7 +269,7 @@ export const quoteSqlLiteral = (value) => {
|
|
|
265
269
|
* Wraps the value in `"…"` and doubles any embedded `"`.
|
|
266
270
|
*/
|
|
267
271
|
export const quoteSqlIdent = (value) => {
|
|
268
|
-
let inner =
|
|
272
|
+
let inner = "";
|
|
269
273
|
for (const c of value) {
|
|
270
274
|
inner += c === '"' ? '""' : c;
|
|
271
275
|
}
|
|
@@ -312,13 +316,13 @@ const isVarNameCont = (c) => c !== undefined && VAR_NAME_CONT_RE.test(c);
|
|
|
312
316
|
export const tryConsumeVarSubstitution = (s, i, varLookup) => {
|
|
313
317
|
if (varLookup === undefined)
|
|
314
318
|
return null;
|
|
315
|
-
if (s[i] !==
|
|
319
|
+
if (s[i] !== ":")
|
|
316
320
|
return null;
|
|
317
321
|
const next = s[i + 1];
|
|
318
322
|
if (next === undefined)
|
|
319
323
|
return null;
|
|
320
324
|
// `::` cast operator — never a substitution.
|
|
321
|
-
if (next ===
|
|
325
|
+
if (next === ":")
|
|
322
326
|
return null;
|
|
323
327
|
// :{?NAME} — defined-variable test. Emits literal `TRUE` if the named
|
|
324
328
|
// variable is set, `FALSE` otherwise. Mirrors upstream's
|
|
@@ -329,14 +333,14 @@ export const tryConsumeVarSubstitution = (s, i, varLookup) => {
|
|
|
329
333
|
// (missing closing `}`, empty NAME, or non-variable_char content) returns
|
|
330
334
|
// `null` so the caller emits the literal `:` and continues, matching the
|
|
331
335
|
// upstream flex fallback rule `:\{\?{variable_char}*`.
|
|
332
|
-
if (next ===
|
|
336
|
+
if (next === "{" && s[i + 2] === "?") {
|
|
333
337
|
let j = i + 3;
|
|
334
338
|
while (j < s.length && isVarNameCont(s[j]))
|
|
335
339
|
j++;
|
|
336
|
-
if (j > i + 3 && s[j] ===
|
|
340
|
+
if (j > i + 3 && s[j] === "}") {
|
|
337
341
|
const name = s.slice(i + 3, j);
|
|
338
342
|
const value = varLookup(name);
|
|
339
|
-
return { end: j + 1, text: value !== undefined ?
|
|
343
|
+
return { end: j + 1, text: value !== undefined ? "TRUE" : "FALSE" };
|
|
340
344
|
}
|
|
341
345
|
return null;
|
|
342
346
|
}
|
package/dist/psql/types/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
1
|
+
export * from "./backslash.js";
|
|
2
|
+
export * from "./connection.js";
|
|
3
|
+
export * from "./printer.js";
|
|
4
|
+
export * from "./repl.js";
|
|
5
|
+
export * from "./scanner.js";
|
|
6
|
+
export * from "./settings.js";
|
|
7
|
+
export * from "./variables.js";
|