neon 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +30 -5
  2. package/dist/{_shared/auth_selection.js → _chunks/auth_selection-DGgq6ifc.js} +3 -3
  3. package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
  4. package/dist/{_shared/credentials.js → _chunks/credentials-MYdHdKah.js} +3 -3
  5. package/dist/_chunks/env-NbA61JR3.js +585 -0
  6. package/dist/{_shared/env-core/env.js → _chunks/env_services-Tz9G4JeT.js} +179 -43
  7. package/dist/{_shared/paths.js → _chunks/paths-DMq0Lt7a.js} +2 -2
  8. package/dist/{_shared/profiles.js → _chunks/profiles-Ir29rqns.js} +4 -13
  9. package/dist/_chunks/psql-DWH-kc69.js +2169 -0
  10. package/dist/{_virtual/_rolldown/runtime.js → _chunks/rolldown-runtime-D7D4PA-g.js} +1 -1
  11. package/dist/{_shared/secure_file.js → _chunks/secure_file-BucZj4yQ.js} +2 -4
  12. package/dist/analytics.js +2 -2
  13. package/dist/auth_context.js +1 -1
  14. package/dist/commands/api.js +2 -2
  15. package/dist/commands/api_keys.js +2 -2
  16. package/dist/commands/auth.js +7 -6
  17. package/dist/commands/bootstrap.js +3 -3
  18. package/dist/commands/branches.js +2 -2
  19. package/dist/commands/bucket.js +2 -2
  20. package/dist/commands/checkout.js +2 -2
  21. package/dist/commands/config.js +2 -2
  22. package/dist/commands/connection_string.js +2 -2
  23. package/dist/commands/data_api.js +2 -2
  24. package/dist/commands/databases.js +2 -2
  25. package/dist/commands/deploy.js +2 -2
  26. package/dist/commands/dev.js +3 -3
  27. package/dist/commands/diff.js +2 -2
  28. package/dist/commands/env.js +42 -32
  29. package/dist/commands/functions.js +2 -2
  30. package/dist/commands/index.js +35 -33
  31. package/dist/commands/init.js +3 -3
  32. package/dist/commands/inspect.js +2 -2
  33. package/dist/commands/ip_allow.js +2 -2
  34. package/dist/commands/link.js +2 -2
  35. package/dist/commands/logs.js +2 -2
  36. package/dist/commands/neon_auth.js +2 -2
  37. package/dist/commands/open.js +33 -0
  38. package/dist/commands/operations.js +2 -2
  39. package/dist/commands/orgs.js +2 -2
  40. package/dist/commands/profile.js +7 -7
  41. package/dist/commands/projects.js +2 -2
  42. package/dist/commands/psql.js +2 -2
  43. package/dist/commands/roles.js +2 -2
  44. package/dist/commands/set_context.js +2 -2
  45. package/dist/commands/snapshots.js +2 -2
  46. package/dist/commands/status.js +2 -2
  47. package/dist/commands/user.js +2 -2
  48. package/dist/commands/vpc_endpoints.js +2 -2
  49. package/dist/config.js +1 -1
  50. package/dist/dev/env.js +1 -364
  51. package/dist/env_services.js +2 -49
  52. package/dist/index.js +2 -1
  53. package/dist/init/auth.js +4 -4
  54. package/dist/psql/cli.js +1 -1
  55. package/dist/psql/command/cmd_copy.js +1 -1
  56. package/dist/psql/command/cmd_io.js +1 -1403
  57. package/dist/psql/command/cmd_pipeline.js +1 -595
  58. package/dist/psql/command/dispatch.js +1 -2
  59. package/dist/psql/core/common.js +1 -834
  60. package/dist/psql/core/mainloop.js +1 -2
  61. package/dist/psql/core/startup.js +1 -656
  62. package/dist/psql/index.js +1 -1516
  63. package/dist/psql/io/psqlrc.js +1 -1
  64. package/dist/psql/types/index.js +6 -0
  65. package/dist/utils/inspect_db.js +1 -1
  66. package/dist/utils/middlewares.js +2 -1
  67. package/package.json +7 -4
  68. package/dist/_shared/env-core/reuse-secrets.js +0 -179
@@ -1,835 +1,2 @@
1
- import { formatDurationMs } from "../print/units.js";
2
- import { alignedPrinter } from "../print/aligned.js";
3
- import { asciidocPrinter } from "../print/asciidoc.js";
4
- import { csvPrinter } from "../print/csv.js";
5
- import { htmlPrinter } from "../print/html.js";
6
- import { jsonPrinter } from "../print/json.js";
7
- import { latexLongtablePrinter, latexPrinter } from "../print/latex.js";
8
- import { troffMsPrinter } from "../print/troff.js";
9
- import { unalignedPrinter } from "../print/unaligned.js";
10
- import { openPager, shouldPage } from "../print/pager.js";
11
- import { formatErrorReport, psqlErrorPrefix } from "../command/cmd_meta.js";
12
- import { getQueryFout } from "../command/cmd_io.js";
13
- //#region src/psql/core/common.ts
14
- /**
15
- * psql common — the unified send-query / process-result pipeline.
16
- *
17
- * TypeScript port of selected functions in `src/bin/psql/common.c`:
18
- *
19
- * - {@link sendQuery} ← `SendQuery`
20
- * - {@link executeAndPrint} ← `ExecQueryAndProcessResults` (the inner
21
- * result-processing slice, without the
22
- * AUTOCOMMIT / savepoint scaffolding)
23
- * - {@link psqlExec} ← `PSQLexec`
24
- *
25
- * The minimal version of this logic was inlined in `mainloop.ts` after WP-12;
26
- * this WP extracts and polishes it. The pieces wired in here:
27
- *
28
- * - AUTOCOMMIT: when the variable is 'off' (default 'on') and the current
29
- * transaction is idle, prepend `BEGIN` once before the next non-exempt
30
- * statement. The set of exempt commands mirrors upstream
31
- * `command_no_begin()` — transaction-control verbs and a handful of
32
- * non-transactional DDL.
33
- *
34
- * - ON_ERROR_ROLLBACK: 'off' (default) | 'on' | 'interactive'. When active
35
- * and we're inside a transaction, issue
36
- * `SAVEPOINT pg_psql_temporary_savepoint` before the statement; on error
37
- * `ROLLBACK TO`, on success `RELEASE` (unless the statement is itself a
38
- * savepoint-management verb that already drops/replaces it).
39
- *
40
- * - FETCH_COUNT: integer; when >0 and the statement is a single SELECT-ish
41
- * query, wrap it in `DECLARE _psql_cursor CURSOR FOR ...; FETCH FORWARD
42
- * N FROM _psql_cursor` until exhausted. Non-SELECT statements fall back
43
- * to the simple path.
44
- *
45
- * - SINGLESTEP: when `settings.singlestep` is true, print the SQL to stderr
46
- * with the upstream confirmation banner and read one line from stdin.
47
- * Input that starts with 'x' cancels the statement.
48
- *
49
- * - Timing: when `settings.timing` is true, measure wallclock around the
50
- * send/print path and write a `Time: X.XXX ms` line to stdout (mirrors
51
- * upstream's `printf` in common.c, and matches the existing mainloop
52
- * test expectations).
53
- *
54
- * What's deliberately not done here:
55
- *
56
- * - SINGLELINE (-S): treating LF as a semicolon is a scanner concern, so it
57
- * lives there — `scanSql` honours `ScanOptions.singleline` and the mainloop
58
- * passes `settings.singleline` through on each pass. `sendQuery` itself
59
- * needs no SINGLELINE branch; by the time a statement reaches here the
60
- * scanner has already drawn the boundary.
61
- *
62
- * - Pipeline mode (-X / `\startpipeline`): upstream gates SendQuery on
63
- * `pset.send_mode == PSQL_SEND_PIPELINE`; that path is owned by WP-21.
64
- *
65
- * - COPY FROM STDIN / TO STDOUT: upstream's ProcessResult dispatches on
66
- * `PGRES_COPY_IN/OUT`. That belongs to WP-16; here we surface a clear
67
- * error message if we ever see a copy result come back through
68
- * `execSimple`.
69
- */
70
- const readTxStatus = (conn) => {
71
- return conn.txStatus ?? "I";
72
- };
73
- const SAVEPOINT_NAME = "pg_psql_temporary_savepoint";
74
- const CURSOR_NAME = "_psql_cursor";
75
- /**
76
- * Strip leading whitespace and `--` line / slash-star block comments from
77
- * `sql`. Mirrors what upstream psql's scanner advances past before handing a
78
- * statement to `PQexec` — the server-reported error `position` is a 1-based
79
- * offset into THAT trimmed buffer, so the `LINE N:` re-print computed from
80
- * `count('\n')` in `sql.slice(0, position - 1)` aligns with vanilla output
81
- * only when the same leading prelude is stripped here too.
82
- *
83
- * Block comments support nested depths (PG extension). Embedded comments
84
- * mid-statement are intentionally NOT stripped — they participate in the
85
- * line count of the executing statement, same as upstream.
86
- *
87
- * Exported for cmd_io / cmd_pipeline so backslash commands that capture or
88
- * inspect `queryBuf` see the same shape that the wire and `lastQuery`
89
- * receive.
90
- */
91
- const stripLeadingCommentsAndWS = (sql) => {
92
- let i = 0;
93
- const n = sql.length;
94
- while (i < n) {
95
- const c = sql.charCodeAt(i);
96
- if (c === 32 || c === 9 || c === 10 || c === 13 || c === 12 || c === 11) {
97
- i++;
98
- continue;
99
- }
100
- if (c === 45 && sql.charCodeAt(i + 1) === 45) {
101
- i += 2;
102
- while (i < n && sql.charCodeAt(i) !== 10) i++;
103
- continue;
104
- }
105
- if (c === 47 && sql.charCodeAt(i + 1) === 42) {
106
- i += 2;
107
- let depth = 1;
108
- while (i < n && depth > 0) if (sql.charCodeAt(i) === 47 && sql.charCodeAt(i + 1) === 42) {
109
- depth++;
110
- i += 2;
111
- } else if (sql.charCodeAt(i) === 42 && sql.charCodeAt(i + 1) === 47) {
112
- depth--;
113
- i += 2;
114
- } else i++;
115
- continue;
116
- }
117
- break;
118
- }
119
- return i === 0 ? sql : sql.slice(i);
120
- };
121
- /** Strip leading whitespace and SQL comments, then upper-case for matching. */
122
- const peekKeywords = (sql, count = 3) => {
123
- let i = 0;
124
- while (i < sql.length) {
125
- const ch = sql[i];
126
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
127
- i++;
128
- continue;
129
- }
130
- if (ch === "-" && sql[i + 1] === "-") {
131
- const nl = sql.indexOf("\n", i + 2);
132
- if (nl === -1) return [];
133
- i = nl + 1;
134
- continue;
135
- }
136
- if (ch === "/" && sql[i + 1] === "*") {
137
- const end = sql.indexOf("*/", i + 2);
138
- if (end === -1) return [];
139
- i = end + 2;
140
- continue;
141
- }
142
- break;
143
- }
144
- return sql.slice(i).split(/[\s,;()]+/u, count + 1).slice(0, count).map((w) => w.toUpperCase());
145
- };
146
- /** Mirror of `command_no_begin()` in psql/common.c. */
147
- const commandNoBegin = (sql) => {
148
- const [w0, w1, w2, w3] = peekKeywords(sql, 4);
149
- if (!w0) return false;
150
- switch (w0) {
151
- case "ABORT":
152
- case "BEGIN":
153
- case "COMMIT":
154
- case "END":
155
- case "ROLLBACK":
156
- case "START":
157
- case "SAVEPOINT":
158
- case "RELEASE": return true;
159
- case "PREPARE": return w1 === "TRANSACTION";
160
- case "VACUUM": return true;
161
- case "CLUSTER": return w1 === void 0 || w1 === "";
162
- case "CREATE":
163
- if (w1 === "DATABASE" || w1 === "TABLESPACE") return true;
164
- if (w1 === "INDEX" && w2 === "CONCURRENTLY") return true;
165
- if (w1 === "UNIQUE" && w2 === "INDEX" && w3 === "CONCURRENTLY") return true;
166
- return false;
167
- case "DROP":
168
- if (w1 === "DATABASE" || w1 === "TABLESPACE") return true;
169
- if (w1 === "INDEX" && w2 === "CONCURRENTLY") return true;
170
- return false;
171
- case "REINDEX":
172
- if (w1 === "DATABASE" || w1 === "SYSTEM") return true;
173
- if (w1 === "INDEX" && w2 === "CONCURRENTLY") return true;
174
- if (w1 === "TABLE" && w2 === "CONCURRENTLY") return true;
175
- return false;
176
- case "ALTER": return w1 === "SYSTEM";
177
- case "DISCARD": return w1 === "ALL";
178
- default: return false;
179
- }
180
- };
181
- /** True when the statement opens with SELECT / VALUES / TABLE / WITH. */
182
- const isSelectCommand = (sql) => {
183
- const [w0] = peekKeywords(sql, 1);
184
- return w0 === "SELECT" || w0 === "VALUES" || w0 === "TABLE" || w0 === "WITH";
185
- };
186
- /**
187
- * Does this statement effectively destroy / replace the temporary savepoint?
188
- * Upstream's `svpt_gone` flag is set when the user's command is one of
189
- * COMMIT / ROLLBACK / SAVEPOINT / RELEASE. In those cases we must skip the
190
- * matching RELEASE because the named savepoint no longer exists.
191
- */
192
- const destroysSavepoint = (sql) => {
193
- const [w0] = peekKeywords(sql, 1);
194
- return w0 === "COMMIT" || w0 === "ROLLBACK" || w0 === "SAVEPOINT" || w0 === "RELEASE";
195
- };
196
- const pickPrinter = (settings) => {
197
- switch (settings.popt.topt.format) {
198
- case "aligned":
199
- case "wrapped": return alignedPrinter;
200
- case "unaligned": return unalignedPrinter;
201
- case "csv": return csvPrinter;
202
- case "json": return jsonPrinter;
203
- case "html": return htmlPrinter;
204
- case "asciidoc": return asciidocPrinter;
205
- case "latex": return latexPrinter;
206
- case "latex-longtable": return latexLongtablePrinter;
207
- case "troff-ms": return troffMsPrinter;
208
- default: return alignedPrinter;
209
- }
210
- };
211
- /**
212
- * Pick the output target for a query result.
213
- *
214
- * Precedence: explicit `oneShot` argument > the settings stash from
215
- * `\o FILE` (WP-15) > the REPL context's `stdout`.
216
- */
217
- const pickOut = (ctx, oneShot) => {
218
- if (oneShot) return oneShot;
219
- return getQueryFout(ctx.settings) ?? ctx.stdout;
220
- };
221
- const readAutocommit = (settings) => settings.vars.asBool("AUTOCOMMIT", true);
222
- const readOnErrorRollback = (settings) => {
223
- const raw = settings.vars.get("ON_ERROR_ROLLBACK");
224
- if (raw === void 0) return settings.onErrorRollback;
225
- const v = raw.toLowerCase();
226
- if (v === "interactive") return "interactive";
227
- if (v === "on" || v === "true" || v === "yes" || v === "1") return "on";
228
- return "off";
229
- };
230
- const readFetchCount = (settings) => {
231
- const v = settings.vars.asInt("FETCH_COUNT", settings.fetchCount);
232
- if (typeof v !== "number") return 0;
233
- return Math.max(0, v | 0);
234
- };
235
- const readSinglestep = (settings) => settings.singlestep || settings.vars.asBool("SINGLESTEP", false);
236
- /**
237
- * SHOW_ALL_RESULTS controls multi-statement `\;` printing. Default 'on' —
238
- * every result set is rendered. When 'off' / '0', only the LAST result set
239
- * is printed (upstream's `pset.show_all_results` flag, consulted by
240
- * `PrintQueryResults` in common.c).
241
- */
242
- const readShowAllResults = (settings) => settings.vars.asBool("SHOW_ALL_RESULTS", true);
243
- const writeError = (ctx, message) => {
244
- ctx.stderr.write(`psql: ERROR: ${message}\n`);
245
- };
246
- /**
247
- * Render the verbosity-aware error report for the most recently captured
248
- * query error and write it to `ctx.stderr`. The leading severity line gets
249
- * the same `psql:[<file>:<n>]:` diagnostic prefix that upstream's
250
- * `pg_log_pre_callback` prepends — matching the format the regression-
251
- * derived conformance suite expects (e.g. `psql:<stdin>:N: ERROR: ...`).
252
- * Subsequent layers (`LINE N: ...`, caret, `DETAIL: ...`, ...) follow on
253
- * their own lines per {@link formatErrorReport}, unprefixed, to match
254
- * libpq's `PQresultErrorMessage` output shape. When the captured error is
255
- * missing (defensive — callers should always pair this with a preceding
256
- * `recordError`) we fall back to the plain client-side {@link writeError}
257
- * form so we never swallow the message entirely.
258
- *
259
- * Exported so the bind-path in mainloop can share the renderer.
260
- */
261
- const writeQueryError = (ctx, fallbackMessage) => {
262
- const e = ctx.settings.lastErrorResult;
263
- if (!e || !e.message && !e.code && !e.sqlstate) {
264
- writeError(ctx, fallbackMessage);
265
- return;
266
- }
267
- const lines = formatErrorReport(e, ctx.settings.verbosity, ctx.settings.showContext);
268
- const prefixed = [psqlErrorPrefix(ctx.settings) + lines[0], ...lines.slice(1)];
269
- ctx.stderr.write(prefixed.join("\n") + "\n");
270
- };
271
- /**
272
- * Strip leading whitespace from a query and rebase a 1-based server position
273
- * to match. Mirrors upstream psql/mainloop.c's behaviour: a line containing
274
- * only a backslash command does not leave a `\n` in the query buffer, so the
275
- * subsequent SQL statement starts at "line 1" of its own context rather than
276
- * inheriting a blank line. Our mainloop doesn't perform that strip, so the
277
- * captured `sqlText` sometimes has a leading `\n` (e.g. after `\set
278
- * FETCH_COUNT 1\nSELECT error;`). Without this normalisation, `\errverbose`
279
- * would render `LINE 2: SELECT error;` where upstream renders `LINE 1: …`.
280
- *
281
- * Returns the trimmed text and the rebased position. If the rebased
282
- * position would land outside the trimmed text, it is dropped so the
283
- * formatter skips the `LINE`/caret block instead of mis-pointing.
284
- */
285
- const normaliseSqlAndPosition = (sqlText, position) => {
286
- let leading = 0;
287
- while (leading < sqlText.length) {
288
- const ch = sqlText.charCodeAt(leading);
289
- if (ch !== 32 && ch !== 9 && ch !== 10 && ch !== 13 && ch !== 12) break;
290
- leading++;
291
- }
292
- if (leading === 0) return {
293
- sqlText,
294
- position
295
- };
296
- const trimmed = sqlText.slice(leading);
297
- if (typeof position !== "string") return {
298
- sqlText: trimmed,
299
- position
300
- };
301
- const original = parseInt(position, 10);
302
- if (!Number.isFinite(original) || original <= 0) return {
303
- sqlText: trimmed,
304
- position
305
- };
306
- const rebased = original - leading;
307
- if (rebased <= 0 || rebased > trimmed.length) return {
308
- sqlText: trimmed,
309
- position: void 0
310
- };
311
- return {
312
- sqlText: trimmed,
313
- position: String(rebased)
314
- };
315
- };
316
- /**
317
- * Capture the full ErrorResponse-shaped payload from a thrown error.
318
- *
319
- * Our wire layer copies every named field of the server's ErrorResponse
320
- * (severity / code / detail / hint / position / file / line / routine /
321
- * …) onto the thrown Error as own properties (see `asThrowable` in
322
- * `wire/connection.ts`). We mirror those onto `settings.lastErrorResult`
323
- * so `\errverbose` can re-render the error in VERBOSE form — including
324
- * the `LINE N: …` re-print + `^` pointer and the `LOCATION:` footer.
325
- *
326
- * `sqlText` is the originating SQL text from the caller; required so the
327
- * `^` pointer can be positioned under the failing character. Leading
328
- * whitespace is stripped (and `position` is rebased) so the `LINE N`
329
- * counter reflects offsets within the user's statement rather than any
330
- * buffer noise carried over from prior backslash commands.
331
- */
332
- const captureLastError = (settings, err, sqlText) => {
333
- const fallbackMessage = err instanceof Error ? err.message : String(err);
334
- const e = err ?? {};
335
- const code = e.code;
336
- const normalised = normaliseSqlAndPosition(sqlText, e.position);
337
- settings.lastErrorResult = {
338
- severity: e.severity,
339
- code,
340
- sqlstate: code,
341
- message: e.message ?? fallbackMessage,
342
- detail: e.detail,
343
- hint: e.hint,
344
- position: normalised.position,
345
- internalPosition: e.internalPosition,
346
- internalQuery: e.internalQuery,
347
- where: e.where,
348
- schema: e.schema,
349
- table: e.table,
350
- column: e.column,
351
- dataType: e.dataType,
352
- constraint: e.constraint,
353
- file: e.file,
354
- line: e.line,
355
- routine: e.routine,
356
- sqlText: normalised.sqlText
357
- };
358
- return settings.lastErrorResult.message ?? fallbackMessage;
359
- };
360
- const recordError = (ctx, err, sqlText = "") => captureLastError(ctx.settings, err, sqlText);
361
- /**
362
- * Update the per-statement diagnostic psql variables that upstream's
363
- * `SetResultVariables` / `SetErrorVariables` in `src/bin/psql/common.c`
364
- * maintains. Called after every dispatched statement (success and error
365
- * paths) so `\echo :LAST_ERROR_MESSAGE` and friends produce the same
366
- * values vanilla psql does.
367
- *
368
- * - `SQLSTATE` SQLSTATE of the *most recent* statement —
369
- * `"00000"` on success, the server-reported code
370
- * on error (defaults to `"XX000"` when missing).
371
- * - `ERROR` `"true"` if the most recent statement failed,
372
- * else `"false"`.
373
- * - `ROW_COUNT` affected/returned row count of the most recent
374
- * statement (from libpq's `PQcmdTuples`). `"0"`
375
- * on error.
376
- * - `LAST_ERROR_*` sticky — only mutated on error. Mirrors
377
- * upstream's "preserve until the next failure"
378
- * contract so a successful statement does not
379
- * clobber the prior error info.
380
- *
381
- * Exported so mainloop's bind / pipeline paths (which bypass {@link
382
- * sendQuery}) can share the same updater.
383
- */
384
- const refreshErrorVars = (settings, outcome) => {
385
- const { vars } = settings;
386
- if (outcome.kind === "error") {
387
- const last = settings.lastErrorResult;
388
- const code = last?.code ?? last?.sqlstate ?? "XX000";
389
- const message = last?.message ?? "";
390
- vars.set("LAST_ERROR_MESSAGE", message);
391
- vars.set("LAST_ERROR_SQLSTATE", code);
392
- vars.set("SQLSTATE", code);
393
- vars.set("ERROR", "true");
394
- vars.set("ROW_COUNT", "0");
395
- return;
396
- }
397
- vars.set("SQLSTATE", "00000");
398
- vars.set("ERROR", "false");
399
- const rc = outcome.rowCount ?? 0;
400
- vars.set("ROW_COUNT", String(rc));
401
- };
402
- const readOneLine = (stdin) => new Promise((resolve) => {
403
- let buf = "";
404
- let resolved = false;
405
- const onData = (chunk) => {
406
- buf += chunk.toString();
407
- const nl = buf.indexOf("\n");
408
- if (nl !== -1) {
409
- const line = buf.slice(0, nl);
410
- cleanup();
411
- if (!resolved) {
412
- resolved = true;
413
- resolve(line);
414
- }
415
- }
416
- };
417
- const onEnd = () => {
418
- cleanup();
419
- if (!resolved) {
420
- resolved = true;
421
- resolve(buf);
422
- }
423
- };
424
- const cleanup = () => {
425
- stdin.off("data", onData);
426
- stdin.off("end", onEnd);
427
- stdin.off("close", onEnd);
428
- };
429
- stdin.on("data", onData);
430
- stdin.once("end", onEnd);
431
- stdin.once("close", onEnd);
432
- });
433
- const confirmSinglestep = async (ctx, sql) => {
434
- ctx.stderr.write(`***(Single step mode: verify command)*******************************************\n${sql}\n***(press return to proceed or enter x and return to cancel)********************\n`);
435
- return !(await readOneLine(ctx.stdin)).trim().toLowerCase().startsWith("x");
436
- };
437
- /**
438
- * Reconstruct the libpq-style CommandComplete tag from the parsed parts our
439
- * wire layer stores on a ResultSet. INSERT carries `oid` + `rowCount`; the
440
- * other DML verbs (UPDATE/DELETE/MERGE/SELECT/MOVE/FETCH/COPY) carry just a
441
- * `rowCount`; DDL has neither.
442
- *
443
- * Matches upstream psql's `PQcmdStatus(conn)` output (which is the raw tag
444
- * the server sent — we round-trip it through our parser).
445
- */
446
- const formatCommandTag = (rs) => {
447
- const command = (rs.command || "").trim();
448
- if (command.length === 0) return "";
449
- if (command === "INSERT") return `INSERT ${rs.oid ?? 0} ${rs.rowCount ?? 0}`;
450
- if (rs.rowCount !== null && rs.rowCount !== void 0) return `${command} ${rs.rowCount}`;
451
- return command;
452
- };
453
- /**
454
- * Decide whether the pager should activate for the given batch of result
455
- * sets. We page when ANY of the sets-that-will-be-printed (i.e. tuples-
456
- * producing, and not gated out by SHOW_ALL_RESULTS) crosses the threshold.
457
- * The decision is centralised here so callers can override individual
458
- * decision inputs (e.g. tests that inject a fake `output`).
459
- */
460
- const pickPagerDecision = (ctx, results, out) => {
461
- const popt = ctx.settings.popt.topt;
462
- if (popt.pager === "off") return false;
463
- const redirectedOutput = getQueryFout(ctx.settings) !== null;
464
- if (redirectedOutput) return false;
465
- const showAll = readShowAllResults(ctx.settings);
466
- const lastIdx = results.length - 1;
467
- for (let i = 0; i < results.length; i++) {
468
- const rs = results[i];
469
- if (rs.fields.length === 0) continue;
470
- if (!(showAll || i === lastIdx)) continue;
471
- if (shouldPage({
472
- pager: popt.pager,
473
- pagerMinLines: popt.pagerMinLines,
474
- rowCount: rs.rows.length,
475
- colCount: rs.fields.length,
476
- output: out,
477
- redirectedOutput
478
- })) return true;
479
- }
480
- return false;
481
- };
482
- const renderResultSets = async (ctx, results, out) => {
483
- const printer = pickPrinter(ctx.settings);
484
- let rowsAffected = 0;
485
- let rowsPrinted = 0;
486
- const showAll = readShowAllResults(ctx.settings);
487
- const lastIdx = results.length - 1;
488
- const tuplesOnly = ctx.settings.popt.topt.tuplesOnly;
489
- const pager = pickPagerDecision(ctx, results, out) ? openPager({
490
- pager: ctx.settings.popt.topt.pager,
491
- pagerMinLines: ctx.settings.popt.topt.pagerMinLines,
492
- stdout: out
493
- }) : null;
494
- const sink = pager?.spawned ? pager.out : out;
495
- try {
496
- for (let i = 0; i < results.length; i++) {
497
- const rs = results[i];
498
- const shouldEmit = showAll || i === lastIdx;
499
- if (rs.copyOutBytes && rs.copyOutBytes.length > 0) for (const chunk of rs.copyOutBytes) sink.write(chunk);
500
- if (rs.fields.length === 0) {
501
- if (shouldEmit && !tuplesOnly && !ctx.settings.quiet && !rs.copyOutBytes) {
502
- const tag = formatCommandTag(rs);
503
- if (tag.length > 0) sink.write(`${tag}\n`);
504
- }
505
- rowsAffected += rs.rowCount ?? 0;
506
- } else {
507
- if (shouldEmit) await printer.printQuery(rs, ctx.settings.popt, sink);
508
- rowsPrinted += rs.rows.length;
509
- }
510
- }
511
- } finally {
512
- if (pager?.spawned) try {
513
- await pager.close();
514
- } catch {}
515
- }
516
- const lastRowCount = results.length === 0 ? null : (() => {
517
- const rs = results[results.length - 1];
518
- if (rs.fields.length > 0) return rs.rows.length;
519
- return rs.rowCount ?? null;
520
- })();
521
- return {
522
- rowsAffected,
523
- rowsPrinted,
524
- lastRowCount
525
- };
526
- };
527
- /**
528
- * Render a single {@link ResultSet} through the active printer and the
529
- * configured output target (respecting `\o FILE` redirects). Used by the
530
- * `\bind` / extended-query path in {@link mainloop.dispatchSendQuery} which
531
- * comes back with a single result instead of the array shape `execSimple`
532
- * produces. Returns a tally consistent with {@link renderResultSets}.
533
- */
534
- const renderResultSet = (ctx, rs, out) => {
535
- return renderResultSets(ctx, [rs], out ?? pickOut(ctx));
536
- };
537
- /**
538
- * Re-base a server-side `position` field so it points into the user's
539
- * original SQL rather than the synthetic statement we actually sent.
540
- *
541
- * The FETCH_COUNT path sends `DECLARE _psql_cursor NO SCROLL CURSOR FOR
542
- * <userSql>` for the DECLARE leg and `FETCH FORWARD N FROM _psql_cursor`
543
- * for each fetch. Server error positions (`P` field) come back in the
544
- * coordinates of whatever query we sent:
545
- *
546
- * - DECLARE-time parser/planner errors carry a position into the DECLARE
547
- * statement. We subtract the length of the prefix (`DECLARE … FOR `)
548
- * so the caret lands under the failing token in `userSql`.
549
- *
550
- * - FETCH-time runtime errors come from executing the cursor's underlying
551
- * query (which IS `userSql`). The server reports the position relative
552
- * to that underlying query, so it's already in `userSql` coordinates
553
- * and we leave it alone.
554
- *
555
- * If we can't rebase a DECLARE-coord position into `userSql` bounds, we
556
- * strip it rather than render a caret pointing past end-of-line.
557
- */
558
- const rebasePositionForCursor = (err, wrapper, userSql) => {
559
- if (!err || typeof err !== "object") return;
560
- const e = err;
561
- if (typeof e.position !== "string") return;
562
- const original = parseInt(e.position, 10);
563
- if (!Number.isFinite(original) || original <= 0) return;
564
- const stripped = userSql.replace(/;\s*$/u, "");
565
- const offset = wrapper.indexOf(stripped);
566
- if (offset === -1) return;
567
- const rebased = original - offset;
568
- if (rebased <= 0 || rebased > userSql.length) {
569
- delete e.position;
570
- return;
571
- }
572
- e.position = String(rebased);
573
- };
574
- const runCursorLoop = async (ctx, sql, fetchCount, out) => {
575
- if (!ctx.settings.db) throw new Error("no connection to the server");
576
- const db = ctx.settings.db;
577
- const initiallyIdle = readTxStatus(db) === "I";
578
- if (initiallyIdle) await db.execSimple("BEGIN");
579
- const stripped = sql.replace(/;\s*$/u, "");
580
- const declared = `DECLARE ${CURSOR_NAME} NO SCROLL CURSOR FOR ${stripped}`;
581
- const fetchSql = `FETCH FORWARD ${String(fetchCount)} FROM ${CURSOR_NAME}`;
582
- const rowsAffected = 0;
583
- let rowsPrinted = 0;
584
- let cursorOpen = false;
585
- let currentWrapper = declared;
586
- const printer = pickPrinter(ctx.settings);
587
- let merged = null;
588
- try {
589
- await db.execSimple(declared);
590
- cursorOpen = true;
591
- while (true) {
592
- currentWrapper = fetchSql;
593
- const sets = await db.execSimple(fetchSql);
594
- if (sets.length === 0) break;
595
- const rs = sets[sets.length - 1];
596
- const chunkRows = rs.rows.length;
597
- if (chunkRows === 0) break;
598
- if (merged === null) merged = {
599
- command: rs.command,
600
- fields: rs.fields,
601
- rows: rs.rows.slice(),
602
- rowCount: rs.rowCount,
603
- oid: rs.oid,
604
- notices: rs.notices
605
- };
606
- else for (const row of rs.rows) merged.rows.push(row);
607
- rowsPrinted += chunkRows;
608
- if (chunkRows < fetchCount) break;
609
- }
610
- if (merged !== null) {
611
- merged.rowCount = merged.rows.length;
612
- await printer.printQuery(merged, ctx.settings.popt, out);
613
- }
614
- await db.execSimple(`CLOSE ${CURSOR_NAME}`);
615
- cursorOpen = false;
616
- if (initiallyIdle) await db.execSimple("COMMIT");
617
- return {
618
- rowsAffected,
619
- rowsPrinted,
620
- lastRowCount: rowsPrinted
621
- };
622
- } catch (err) {
623
- if (merged !== null) {
624
- merged.rowCount = merged.rows.length;
625
- const partialOpts = {
626
- ...ctx.settings.popt,
627
- topt: {
628
- ...ctx.settings.popt.topt,
629
- defaultFooter: false,
630
- stopTable: false
631
- }
632
- };
633
- try {
634
- await printer.printQuery(merged, partialOpts, out);
635
- } catch {}
636
- }
637
- rebasePositionForCursor(err, currentWrapper, sql);
638
- if (cursorOpen) try {
639
- await db.execSimple(`CLOSE ${CURSOR_NAME}`);
640
- } catch {}
641
- if (initiallyIdle) try {
642
- await db.execSimple("ROLLBACK");
643
- } catch {}
644
- throw err;
645
- }
646
- };
647
- const executeAndPrint = async (ctx, sqlRaw, opts = {}) => {
648
- const sql = stripLeadingCommentsAndWS(sqlRaw);
649
- const started = ctx.settings.timing ? performance.now() : 0;
650
- const stats = {
651
- rowsAffected: 0,
652
- rowsPrinted: 0,
653
- fetched: false,
654
- hadError: false,
655
- durationMs: 0
656
- };
657
- if (!ctx.settings.db) {
658
- writeError(ctx, "no connection to the server");
659
- stats.hadError = true;
660
- return stats;
661
- }
662
- const out = pickOut(ctx, opts.oneShotOut);
663
- const fetchCount = readFetchCount(ctx.settings);
664
- let lastRowCount = null;
665
- try {
666
- if (fetchCount > 0 && isSelectCommand(sql)) {
667
- const r = await runCursorLoop(ctx, sql, fetchCount, out);
668
- stats.rowsAffected = r.rowsAffected;
669
- stats.rowsPrinted = r.rowsPrinted;
670
- stats.fetched = true;
671
- lastRowCount = r.lastRowCount;
672
- } else {
673
- const results = await ctx.settings.db.execSimple(sql);
674
- const r = await renderResultSets(ctx, results, out);
675
- stats.rowsAffected = r.rowsAffected;
676
- stats.rowsPrinted = r.rowsPrinted;
677
- lastRowCount = r.lastRowCount;
678
- }
679
- } catch (err) {
680
- const partial = err.partialResults;
681
- if (partial && partial.length > 0) try {
682
- const r = await renderResultSets(ctx, partial, out);
683
- stats.rowsAffected = r.rowsAffected;
684
- stats.rowsPrinted = r.rowsPrinted;
685
- } catch {}
686
- const message = recordError(ctx, err, sql);
687
- writeQueryError(ctx, message);
688
- stats.hadError = true;
689
- } finally {
690
- if (ctx.settings.timing) {
691
- stats.durationMs = performance.now() - started;
692
- ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
693
- }
694
- }
695
- refreshErrorVars(ctx.settings, stats.hadError ? { kind: "error" } : {
696
- kind: "success",
697
- rowCount: lastRowCount
698
- });
699
- return stats;
700
- };
701
- const sendQuery = async (ctx, sqlRaw, opts = {}) => {
702
- const sql = stripLeadingCommentsAndWS(sqlRaw);
703
- const stats = {
704
- rowsAffected: 0,
705
- rowsPrinted: 0,
706
- fetched: false,
707
- hadError: false,
708
- durationMs: 0
709
- };
710
- ctx.settings.lastQuery = sql;
711
- if (!ctx.settings.db) {
712
- writeError(ctx, "no connection to the server");
713
- stats.hadError = true;
714
- return stats;
715
- }
716
- if (readSinglestep(ctx.settings)) {
717
- if (!await confirmSinglestep(ctx, sql)) {
718
- stats.hadError = true;
719
- ctx.settings.lastErrorResult = { message: "command cancelled by user" };
720
- return stats;
721
- }
722
- }
723
- const db = ctx.settings.db;
724
- const autocommit = readAutocommit(ctx.settings);
725
- const onErrorRollback = readOnErrorRollback(ctx.settings);
726
- const interactive = !ctx.settings.notty;
727
- const started = ctx.settings.timing ? performance.now() : 0;
728
- if (!autocommit && readTxStatus(db) === "I" && !commandNoBegin(sql)) try {
729
- await db.execSimple("BEGIN");
730
- } catch (err) {
731
- const message = recordError(ctx, err);
732
- writeQueryError(ctx, message);
733
- stats.hadError = true;
734
- if (ctx.settings.timing) {
735
- stats.durationMs = performance.now() - started;
736
- ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
737
- }
738
- return stats;
739
- }
740
- const savepointActive = onErrorRollback !== "off" && (onErrorRollback === "on" || onErrorRollback === "interactive" && interactive) && readTxStatus(db) === "T";
741
- let savepointIssued = false;
742
- if (savepointActive) try {
743
- await db.execSimple(`SAVEPOINT ${SAVEPOINT_NAME}`);
744
- savepointIssued = true;
745
- } catch (err) {
746
- const message = recordError(ctx, err);
747
- writeQueryError(ctx, message);
748
- stats.hadError = true;
749
- if (ctx.settings.timing) {
750
- stats.durationMs = performance.now() - started;
751
- ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
752
- }
753
- return stats;
754
- }
755
- const out = pickOut(ctx, opts.oneShotOut);
756
- const fetchCount = readFetchCount(ctx.settings);
757
- let lastRowCount = null;
758
- try {
759
- if (fetchCount > 0 && isSelectCommand(sql)) {
760
- const r = await runCursorLoop(ctx, sql, fetchCount, out);
761
- stats.rowsAffected = r.rowsAffected;
762
- stats.rowsPrinted = r.rowsPrinted;
763
- stats.fetched = true;
764
- lastRowCount = r.lastRowCount;
765
- } else {
766
- const results = await db.execSimple(sql);
767
- const r = await renderResultSets(ctx, results, out);
768
- stats.rowsAffected = r.rowsAffected;
769
- stats.rowsPrinted = r.rowsPrinted;
770
- lastRowCount = r.lastRowCount;
771
- }
772
- } catch (err) {
773
- const partial = err.partialResults;
774
- if (partial && partial.length > 0) try {
775
- const r = await renderResultSets(ctx, partial, out);
776
- stats.rowsAffected = r.rowsAffected;
777
- stats.rowsPrinted = r.rowsPrinted;
778
- } catch {}
779
- const message = recordError(ctx, err, sql);
780
- writeQueryError(ctx, message);
781
- stats.hadError = true;
782
- }
783
- if (savepointIssued) try {
784
- if (stats.hadError) {
785
- await db.execSimple(`ROLLBACK TO SAVEPOINT ${SAVEPOINT_NAME}`);
786
- await db.execSimple(`RELEASE SAVEPOINT ${SAVEPOINT_NAME}`);
787
- } else if (!destroysSavepoint(sql) && readTxStatus(db) === "T") await db.execSimple(`RELEASE SAVEPOINT ${SAVEPOINT_NAME}`);
788
- } catch (err) {
789
- if (!stats.hadError) {
790
- const message = recordError(ctx, err);
791
- writeQueryError(ctx, message);
792
- stats.hadError = true;
793
- }
794
- }
795
- if (stats.hadError && ctx.settings.echo === "errors") {
796
- let stmt = sql;
797
- while (true) {
798
- const before = stmt.length;
799
- stmt = stmt.replace(/^[ \t\n\r\f]+/, "");
800
- stmt = stmt.replace(/^--[^\n\r]*/, "");
801
- if (stmt.length === before) break;
802
- }
803
- if (stmt.endsWith("\n")) stmt = stmt.slice(0, -1);
804
- ctx.stderr.write(`STATEMENT: ${stmt}\n`);
805
- }
806
- refreshErrorVars(ctx.settings, stats.hadError ? { kind: "error" } : {
807
- kind: "success",
808
- rowCount: lastRowCount
809
- });
810
- if (ctx.settings.timing) {
811
- stats.durationMs = performance.now() - started;
812
- ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
813
- }
814
- return stats;
815
- };
816
- const psqlExec = async (conn, sql, ignoreError = false) => {
817
- try {
818
- const sets = await conn.execSimple(sql);
819
- if (sets.length === 0) return null;
820
- return sets[sets.length - 1];
821
- } catch (err) {
822
- if (ignoreError) return null;
823
- throw err;
824
- }
825
- };
826
- const __testing = {
827
- commandNoBegin,
828
- isSelectCommand,
829
- destroysSavepoint,
830
- peekKeywords,
831
- SAVEPOINT_NAME,
832
- CURSOR_NAME
833
- };
834
- //#endregion
1
+ import { B as psqlExec, G as writeQueryError, H as renderResultSet, I as __testing, L as captureLastError, R as executeAndPrint, U as sendQuery, V as refreshErrorVars, W as stripLeadingCommentsAndWS, z as pickOut } from "../../_chunks/cmd_pipeline-CUbBO9U_.js";
835
2
  export { __testing, captureLastError, executeAndPrint, pickOut, psqlExec, refreshErrorVars, renderResultSet, sendQuery, stripLeadingCommentsAndWS, writeQueryError };