neonctl 2.28.0 → 2.29.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 +2 -2
- 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 +33 -33
- 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/core/common.js
CHANGED
|
@@ -54,21 +54,21 @@
|
|
|
54
54
|
* error message if we ever see a copy result come back through
|
|
55
55
|
* `execSimple`.
|
|
56
56
|
*/
|
|
57
|
-
import {
|
|
58
|
-
import {
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
import {
|
|
65
|
-
import {
|
|
66
|
-
import {
|
|
67
|
-
import {
|
|
68
|
-
import {
|
|
57
|
+
import { getQueryFout } from "../command/cmd_io.js";
|
|
58
|
+
import { formatErrorReport, psqlErrorPrefix } from "../command/cmd_meta.js";
|
|
59
|
+
import { alignedPrinter } from "../print/aligned.js";
|
|
60
|
+
import { asciidocPrinter } from "../print/asciidoc.js";
|
|
61
|
+
import { csvPrinter } from "../print/csv.js";
|
|
62
|
+
import { htmlPrinter } from "../print/html.js";
|
|
63
|
+
import { jsonPrinter } from "../print/json.js";
|
|
64
|
+
import { latexLongtablePrinter, latexPrinter } from "../print/latex.js";
|
|
65
|
+
import { openPager, shouldPage } from "../print/pager.js";
|
|
66
|
+
import { troffMsPrinter } from "../print/troff.js";
|
|
67
|
+
import { unalignedPrinter } from "../print/unaligned.js";
|
|
68
|
+
import { formatDurationMs } from "../print/units.js";
|
|
69
69
|
const readTxStatus = (conn) => {
|
|
70
70
|
const status = conn.txStatus;
|
|
71
|
-
return status ??
|
|
71
|
+
return status ?? "I";
|
|
72
72
|
};
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
74
|
// Statement classification.
|
|
@@ -79,8 +79,8 @@ const readTxStatus = (conn) => {
|
|
|
79
79
|
// mirror both with a lightweight prefix matcher — the SQL was already
|
|
80
80
|
// normalised by the scanner before reaching us.
|
|
81
81
|
// ---------------------------------------------------------------------------
|
|
82
|
-
const SAVEPOINT_NAME =
|
|
83
|
-
const CURSOR_NAME =
|
|
82
|
+
const SAVEPOINT_NAME = "pg_psql_temporary_savepoint";
|
|
83
|
+
const CURSOR_NAME = "_psql_cursor";
|
|
84
84
|
/**
|
|
85
85
|
* Strip leading whitespace and `--` line / slash-star block comments from
|
|
86
86
|
* `sql`. Mirrors what upstream psql's scanner advances past before handing a
|
|
@@ -124,7 +124,8 @@ export const stripLeadingCommentsAndWS = (sql) => {
|
|
|
124
124
|
i += 2;
|
|
125
125
|
let depth = 1;
|
|
126
126
|
while (i < n && depth > 0) {
|
|
127
|
-
if (sql.charCodeAt(i) === 0x2f &&
|
|
127
|
+
if (sql.charCodeAt(i) === 0x2f &&
|
|
128
|
+
sql.charCodeAt(i + 1) === 0x2a) {
|
|
128
129
|
depth++;
|
|
129
130
|
i += 2;
|
|
130
131
|
}
|
|
@@ -150,19 +151,19 @@ const peekKeywords = (sql, count = 3) => {
|
|
|
150
151
|
let i = 0;
|
|
151
152
|
while (i < sql.length) {
|
|
152
153
|
const ch = sql[i];
|
|
153
|
-
if (ch ===
|
|
154
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
|
|
154
155
|
i++;
|
|
155
156
|
continue;
|
|
156
157
|
}
|
|
157
|
-
if (ch ===
|
|
158
|
-
const nl = sql.indexOf(
|
|
158
|
+
if (ch === "-" && sql[i + 1] === "-") {
|
|
159
|
+
const nl = sql.indexOf("\n", i + 2);
|
|
159
160
|
if (nl === -1)
|
|
160
161
|
return [];
|
|
161
162
|
i = nl + 1;
|
|
162
163
|
continue;
|
|
163
164
|
}
|
|
164
|
-
if (ch ===
|
|
165
|
-
const end = sql.indexOf(
|
|
165
|
+
if (ch === "/" && sql[i + 1] === "*") {
|
|
166
|
+
const end = sql.indexOf("*/", i + 2);
|
|
166
167
|
if (end === -1)
|
|
167
168
|
return [];
|
|
168
169
|
i = end + 2;
|
|
@@ -182,56 +183,56 @@ const commandNoBegin = (sql) => {
|
|
|
182
183
|
if (!w0)
|
|
183
184
|
return false;
|
|
184
185
|
switch (w0) {
|
|
185
|
-
case
|
|
186
|
-
case
|
|
187
|
-
case
|
|
188
|
-
case
|
|
189
|
-
case
|
|
190
|
-
case
|
|
191
|
-
case
|
|
192
|
-
case
|
|
186
|
+
case "ABORT":
|
|
187
|
+
case "BEGIN":
|
|
188
|
+
case "COMMIT":
|
|
189
|
+
case "END":
|
|
190
|
+
case "ROLLBACK":
|
|
191
|
+
case "START":
|
|
192
|
+
case "SAVEPOINT":
|
|
193
|
+
case "RELEASE":
|
|
193
194
|
return true;
|
|
194
|
-
case
|
|
195
|
-
return w1 ===
|
|
196
|
-
case
|
|
195
|
+
case "PREPARE":
|
|
196
|
+
return w1 === "TRANSACTION";
|
|
197
|
+
case "VACUUM":
|
|
197
198
|
return true;
|
|
198
|
-
case
|
|
199
|
+
case "CLUSTER":
|
|
199
200
|
// CLUSTER without an explicit argument runs over the whole DB and
|
|
200
201
|
// cannot be transactional.
|
|
201
|
-
return w1 === undefined || w1 ===
|
|
202
|
-
case
|
|
203
|
-
if (w1 ===
|
|
202
|
+
return w1 === undefined || w1 === "";
|
|
203
|
+
case "CREATE":
|
|
204
|
+
if (w1 === "DATABASE" || w1 === "TABLESPACE")
|
|
204
205
|
return true;
|
|
205
206
|
// CREATE INDEX CONCURRENTLY / CREATE UNIQUE INDEX CONCURRENTLY cannot
|
|
206
207
|
// run inside a transaction block — psql must send them bare even with
|
|
207
208
|
// AUTOCOMMIT=off (review item #24).
|
|
208
|
-
if (w1 ===
|
|
209
|
+
if (w1 === "INDEX" && w2 === "CONCURRENTLY")
|
|
209
210
|
return true;
|
|
210
|
-
if (w1 ===
|
|
211
|
+
if (w1 === "UNIQUE" && w2 === "INDEX" && w3 === "CONCURRENTLY") {
|
|
211
212
|
return true;
|
|
212
213
|
}
|
|
213
214
|
return false;
|
|
214
|
-
case
|
|
215
|
+
case "DROP":
|
|
215
216
|
// DROP DATABASE / TABLESPACE / INDEX CONCURRENTLY. (There is no
|
|
216
217
|
// `DROP TABLE … CONCURRENTLY` in PostgreSQL — removed that bogus case.)
|
|
217
|
-
if (w1 ===
|
|
218
|
+
if (w1 === "DATABASE" || w1 === "TABLESPACE")
|
|
218
219
|
return true;
|
|
219
|
-
if (w1 ===
|
|
220
|
+
if (w1 === "INDEX" && w2 === "CONCURRENTLY")
|
|
220
221
|
return true;
|
|
221
222
|
return false;
|
|
222
|
-
case
|
|
223
|
+
case "REINDEX":
|
|
223
224
|
// REINDEX DATABASE / SYSTEM / INDEX CONCURRENTLY / TABLE CONCURRENTLY.
|
|
224
|
-
if (w1 ===
|
|
225
|
+
if (w1 === "DATABASE" || w1 === "SYSTEM")
|
|
225
226
|
return true;
|
|
226
|
-
if (w1 ===
|
|
227
|
+
if (w1 === "INDEX" && w2 === "CONCURRENTLY")
|
|
227
228
|
return true;
|
|
228
|
-
if (w1 ===
|
|
229
|
+
if (w1 === "TABLE" && w2 === "CONCURRENTLY")
|
|
229
230
|
return true;
|
|
230
231
|
return false;
|
|
231
|
-
case
|
|
232
|
-
return w1 ===
|
|
233
|
-
case
|
|
234
|
-
return w1 ===
|
|
232
|
+
case "ALTER":
|
|
233
|
+
return w1 === "SYSTEM";
|
|
234
|
+
case "DISCARD":
|
|
235
|
+
return w1 === "ALL";
|
|
235
236
|
default:
|
|
236
237
|
return false;
|
|
237
238
|
}
|
|
@@ -239,7 +240,7 @@ const commandNoBegin = (sql) => {
|
|
|
239
240
|
/** True when the statement opens with SELECT / VALUES / TABLE / WITH. */
|
|
240
241
|
const isSelectCommand = (sql) => {
|
|
241
242
|
const [w0] = peekKeywords(sql, 1);
|
|
242
|
-
return w0 ===
|
|
243
|
+
return (w0 === "SELECT" || w0 === "VALUES" || w0 === "TABLE" || w0 === "WITH");
|
|
243
244
|
};
|
|
244
245
|
/**
|
|
245
246
|
* Does this statement effectively destroy / replace the temporary savepoint?
|
|
@@ -249,10 +250,10 @@ const isSelectCommand = (sql) => {
|
|
|
249
250
|
*/
|
|
250
251
|
const destroysSavepoint = (sql) => {
|
|
251
252
|
const [w0] = peekKeywords(sql, 1);
|
|
252
|
-
return (w0 ===
|
|
253
|
-
w0 ===
|
|
254
|
-
w0 ===
|
|
255
|
-
w0 ===
|
|
253
|
+
return (w0 === "COMMIT" ||
|
|
254
|
+
w0 === "ROLLBACK" ||
|
|
255
|
+
w0 === "SAVEPOINT" ||
|
|
256
|
+
w0 === "RELEASE");
|
|
256
257
|
};
|
|
257
258
|
// ---------------------------------------------------------------------------
|
|
258
259
|
// Printer selection. Routes to the printer for the active output format —
|
|
@@ -262,24 +263,24 @@ const destroysSavepoint = (sql) => {
|
|
|
262
263
|
// ---------------------------------------------------------------------------
|
|
263
264
|
const pickPrinter = (settings) => {
|
|
264
265
|
switch (settings.popt.topt.format) {
|
|
265
|
-
case
|
|
266
|
-
case
|
|
266
|
+
case "aligned":
|
|
267
|
+
case "wrapped":
|
|
267
268
|
return alignedPrinter;
|
|
268
|
-
case
|
|
269
|
+
case "unaligned":
|
|
269
270
|
return unalignedPrinter;
|
|
270
|
-
case
|
|
271
|
+
case "csv":
|
|
271
272
|
return csvPrinter;
|
|
272
|
-
case
|
|
273
|
+
case "json":
|
|
273
274
|
return jsonPrinter;
|
|
274
|
-
case
|
|
275
|
+
case "html":
|
|
275
276
|
return htmlPrinter;
|
|
276
|
-
case
|
|
277
|
+
case "asciidoc":
|
|
277
278
|
return asciidocPrinter;
|
|
278
|
-
case
|
|
279
|
+
case "latex":
|
|
279
280
|
return latexPrinter;
|
|
280
|
-
case
|
|
281
|
+
case "latex-longtable":
|
|
281
282
|
return latexLongtablePrinter;
|
|
282
|
-
case
|
|
283
|
+
case "troff-ms":
|
|
283
284
|
return troffMsPrinter;
|
|
284
285
|
default:
|
|
285
286
|
return alignedPrinter;
|
|
@@ -303,32 +304,32 @@ export const pickOut = (ctx, oneShot) => {
|
|
|
303
304
|
// Upstream reads them once at the start of SendQuery; we do the same so a
|
|
304
305
|
// hook that mutates them mid-query doesn't reshape our logic underneath us.
|
|
305
306
|
// ---------------------------------------------------------------------------
|
|
306
|
-
const readAutocommit = (settings) => settings.vars.asBool(
|
|
307
|
+
const readAutocommit = (settings) => settings.vars.asBool("AUTOCOMMIT", true);
|
|
307
308
|
const readOnErrorRollback = (settings) => {
|
|
308
|
-
const raw = settings.vars.get(
|
|
309
|
+
const raw = settings.vars.get("ON_ERROR_ROLLBACK");
|
|
309
310
|
if (raw === undefined)
|
|
310
311
|
return settings.onErrorRollback;
|
|
311
312
|
const v = raw.toLowerCase();
|
|
312
|
-
if (v ===
|
|
313
|
-
return
|
|
314
|
-
if (v ===
|
|
315
|
-
return
|
|
316
|
-
return
|
|
313
|
+
if (v === "interactive")
|
|
314
|
+
return "interactive";
|
|
315
|
+
if (v === "on" || v === "true" || v === "yes" || v === "1")
|
|
316
|
+
return "on";
|
|
317
|
+
return "off";
|
|
317
318
|
};
|
|
318
319
|
const readFetchCount = (settings) => {
|
|
319
|
-
const v = settings.vars.asInt(
|
|
320
|
-
if (typeof v !==
|
|
320
|
+
const v = settings.vars.asInt("FETCH_COUNT", settings.fetchCount);
|
|
321
|
+
if (typeof v !== "number")
|
|
321
322
|
return 0;
|
|
322
323
|
return Math.max(0, v | 0);
|
|
323
324
|
};
|
|
324
|
-
const readSinglestep = (settings) => settings.singlestep || settings.vars.asBool(
|
|
325
|
+
const readSinglestep = (settings) => settings.singlestep || settings.vars.asBool("SINGLESTEP", false);
|
|
325
326
|
/**
|
|
326
327
|
* SHOW_ALL_RESULTS controls multi-statement `\;` printing. Default 'on' —
|
|
327
328
|
* every result set is rendered. When 'off' / '0', only the LAST result set
|
|
328
329
|
* is printed (upstream's `pset.show_all_results` flag, consulted by
|
|
329
330
|
* `PrintQueryResults` in common.c).
|
|
330
331
|
*/
|
|
331
|
-
const readShowAllResults = (settings) => settings.vars.asBool(
|
|
332
|
+
const readShowAllResults = (settings) => settings.vars.asBool("SHOW_ALL_RESULTS", true);
|
|
332
333
|
// ---------------------------------------------------------------------------
|
|
333
334
|
// Error printing — mirrors mainloop's `writeError` format. We keep it local
|
|
334
335
|
// so callers other than the mainloop can still emit consistent errors.
|
|
@@ -371,7 +372,7 @@ export const writeQueryError = (ctx, fallbackMessage) => {
|
|
|
371
372
|
const lines = formatErrorReport(e, ctx.settings.verbosity, ctx.settings.showContext);
|
|
372
373
|
const prefix = psqlErrorPrefix(ctx.settings);
|
|
373
374
|
const prefixed = [prefix + lines[0], ...lines.slice(1)];
|
|
374
|
-
ctx.stderr.write(prefixed.join(
|
|
375
|
+
ctx.stderr.write(prefixed.join("\n") + "\n");
|
|
375
376
|
};
|
|
376
377
|
/**
|
|
377
378
|
* Strip leading whitespace from a query and rebase a 1-based server position
|
|
@@ -404,7 +405,7 @@ const normaliseSqlAndPosition = (sqlText, position) => {
|
|
|
404
405
|
if (leading === 0)
|
|
405
406
|
return { sqlText, position };
|
|
406
407
|
const trimmed = sqlText.slice(leading);
|
|
407
|
-
if (typeof position !==
|
|
408
|
+
if (typeof position !== "string")
|
|
408
409
|
return { sqlText: trimmed, position };
|
|
409
410
|
const original = parseInt(position, 10);
|
|
410
411
|
if (!Number.isFinite(original) || original <= 0) {
|
|
@@ -461,7 +462,7 @@ export const captureLastError = (settings, err, sqlText) => {
|
|
|
461
462
|
};
|
|
462
463
|
return settings.lastErrorResult.message ?? fallbackMessage;
|
|
463
464
|
};
|
|
464
|
-
const recordError = (ctx, err, sqlText =
|
|
465
|
+
const recordError = (ctx, err, sqlText = "") => captureLastError(ctx.settings, err, sqlText);
|
|
465
466
|
/**
|
|
466
467
|
* Update the per-statement diagnostic psql variables that upstream's
|
|
467
468
|
* `SetResultVariables` / `SetErrorVariables` in `src/bin/psql/common.c`
|
|
@@ -487,21 +488,21 @@ const recordError = (ctx, err, sqlText = '') => captureLastError(ctx.settings, e
|
|
|
487
488
|
*/
|
|
488
489
|
export const refreshErrorVars = (settings, outcome) => {
|
|
489
490
|
const { vars } = settings;
|
|
490
|
-
if (outcome.kind ===
|
|
491
|
+
if (outcome.kind === "error") {
|
|
491
492
|
const last = settings.lastErrorResult;
|
|
492
|
-
const code = last?.code ?? last?.sqlstate ??
|
|
493
|
-
const message = last?.message ??
|
|
494
|
-
vars.set(
|
|
495
|
-
vars.set(
|
|
496
|
-
vars.set(
|
|
497
|
-
vars.set(
|
|
498
|
-
vars.set(
|
|
493
|
+
const code = last?.code ?? last?.sqlstate ?? "XX000";
|
|
494
|
+
const message = last?.message ?? "";
|
|
495
|
+
vars.set("LAST_ERROR_MESSAGE", message);
|
|
496
|
+
vars.set("LAST_ERROR_SQLSTATE", code);
|
|
497
|
+
vars.set("SQLSTATE", code);
|
|
498
|
+
vars.set("ERROR", "true");
|
|
499
|
+
vars.set("ROW_COUNT", "0");
|
|
499
500
|
return;
|
|
500
501
|
}
|
|
501
|
-
vars.set(
|
|
502
|
-
vars.set(
|
|
502
|
+
vars.set("SQLSTATE", "00000");
|
|
503
|
+
vars.set("ERROR", "false");
|
|
503
504
|
const rc = outcome.rowCount ?? 0;
|
|
504
|
-
vars.set(
|
|
505
|
+
vars.set("ROW_COUNT", String(rc));
|
|
505
506
|
};
|
|
506
507
|
// ---------------------------------------------------------------------------
|
|
507
508
|
// SINGLESTEP confirmation.
|
|
@@ -511,11 +512,11 @@ export const refreshErrorVars = (settings, outcome) => {
|
|
|
511
512
|
// output) and read one line from ctx.stdin. Returns true to proceed.
|
|
512
513
|
// ---------------------------------------------------------------------------
|
|
513
514
|
const readOneLine = (stdin) => new Promise((resolve) => {
|
|
514
|
-
let buf =
|
|
515
|
+
let buf = "";
|
|
515
516
|
let resolved = false;
|
|
516
517
|
const onData = (chunk) => {
|
|
517
518
|
buf += chunk.toString();
|
|
518
|
-
const nl = buf.indexOf(
|
|
519
|
+
const nl = buf.indexOf("\n");
|
|
519
520
|
if (nl !== -1) {
|
|
520
521
|
const line = buf.slice(0, nl);
|
|
521
522
|
cleanup();
|
|
@@ -533,20 +534,20 @@ const readOneLine = (stdin) => new Promise((resolve) => {
|
|
|
533
534
|
}
|
|
534
535
|
};
|
|
535
536
|
const cleanup = () => {
|
|
536
|
-
stdin.off(
|
|
537
|
-
stdin.off(
|
|
538
|
-
stdin.off(
|
|
537
|
+
stdin.off("data", onData);
|
|
538
|
+
stdin.off("end", onEnd);
|
|
539
|
+
stdin.off("close", onEnd);
|
|
539
540
|
};
|
|
540
|
-
stdin.on(
|
|
541
|
-
stdin.once(
|
|
542
|
-
stdin.once(
|
|
541
|
+
stdin.on("data", onData);
|
|
542
|
+
stdin.once("end", onEnd);
|
|
543
|
+
stdin.once("close", onEnd);
|
|
543
544
|
});
|
|
544
545
|
const confirmSinglestep = async (ctx, sql) => {
|
|
545
546
|
ctx.stderr.write(`***(Single step mode: verify command)*******************************************\n` +
|
|
546
547
|
`${sql}\n` +
|
|
547
548
|
`***(press return to proceed or enter x and return to cancel)********************\n`);
|
|
548
549
|
const line = await readOneLine(ctx.stdin);
|
|
549
|
-
return !line.trim().toLowerCase().startsWith(
|
|
550
|
+
return !line.trim().toLowerCase().startsWith("x");
|
|
550
551
|
};
|
|
551
552
|
// ---------------------------------------------------------------------------
|
|
552
553
|
// Result rendering. We tally rows printed / rows affected for the QueryStats
|
|
@@ -563,10 +564,10 @@ const confirmSinglestep = async (ctx, sql) => {
|
|
|
563
564
|
* the server sent — we round-trip it through our parser).
|
|
564
565
|
*/
|
|
565
566
|
const formatCommandTag = (rs) => {
|
|
566
|
-
const command = (rs.command ||
|
|
567
|
+
const command = (rs.command || "").trim();
|
|
567
568
|
if (command.length === 0)
|
|
568
|
-
return
|
|
569
|
-
if (command ===
|
|
569
|
+
return "";
|
|
570
|
+
if (command === "INSERT") {
|
|
570
571
|
// INSERT is the only tag with the legacy oid in front of rowCount.
|
|
571
572
|
return `INSERT ${rs.oid ?? 0} ${rs.rowCount ?? 0}`;
|
|
572
573
|
}
|
|
@@ -585,7 +586,7 @@ const formatCommandTag = (rs) => {
|
|
|
585
586
|
const pickPagerDecision = (ctx, results, out) => {
|
|
586
587
|
const popt = ctx.settings.popt.topt;
|
|
587
588
|
// Pager off → never page (cheap exit, no looping needed).
|
|
588
|
-
if (popt.pager ===
|
|
589
|
+
if (popt.pager === "off")
|
|
589
590
|
return false;
|
|
590
591
|
// `\o FILE` (or `\g FILE`) wins over pager. If the queryFout is set, the
|
|
591
592
|
// pager must not activate even when popt.pager === 'always'.
|
|
@@ -755,10 +756,10 @@ export const renderResultSet = (ctx, rs, out) => {
|
|
|
755
756
|
* strip it rather than render a caret pointing past end-of-line.
|
|
756
757
|
*/
|
|
757
758
|
const rebasePositionForCursor = (err, wrapper, userSql) => {
|
|
758
|
-
if (!err || typeof err !==
|
|
759
|
+
if (!err || typeof err !== "object")
|
|
759
760
|
return;
|
|
760
761
|
const e = err;
|
|
761
|
-
if (typeof e.position !==
|
|
762
|
+
if (typeof e.position !== "string")
|
|
762
763
|
return;
|
|
763
764
|
const original = parseInt(e.position, 10);
|
|
764
765
|
if (!Number.isFinite(original) || original <= 0)
|
|
@@ -767,7 +768,7 @@ const rebasePositionForCursor = (err, wrapper, userSql) => {
|
|
|
767
768
|
// user's SQL verbatim (the DECLARE case), the prefix length tells us how
|
|
768
769
|
// far to shift. The trailing `;` is stripped before wrapping, so we
|
|
769
770
|
// search for the stripped form.
|
|
770
|
-
const stripped = userSql.replace(/;\s*$/u,
|
|
771
|
+
const stripped = userSql.replace(/;\s*$/u, "");
|
|
771
772
|
const offset = wrapper.indexOf(stripped);
|
|
772
773
|
if (offset === -1) {
|
|
773
774
|
// FETCH-leg failures: the wrapper is `FETCH FORWARD …` and the server
|
|
@@ -789,16 +790,16 @@ const rebasePositionForCursor = (err, wrapper, userSql) => {
|
|
|
789
790
|
};
|
|
790
791
|
const runCursorLoop = async (ctx, sql, fetchCount, out) => {
|
|
791
792
|
if (!ctx.settings.db)
|
|
792
|
-
throw new Error(
|
|
793
|
+
throw new Error("no connection to the server");
|
|
793
794
|
const db = ctx.settings.db;
|
|
794
795
|
// Make sure we're in a transaction so the cursor survives between FETCH
|
|
795
796
|
// calls. If we're idle, open one here and remember to close it.
|
|
796
|
-
const initiallyIdle = readTxStatus(db) ===
|
|
797
|
+
const initiallyIdle = readTxStatus(db) === "I";
|
|
797
798
|
if (initiallyIdle) {
|
|
798
|
-
await db.execSimple(
|
|
799
|
+
await db.execSimple("BEGIN");
|
|
799
800
|
}
|
|
800
801
|
// Strip trailing ';' from the user SQL so DECLARE CURSOR FOR <stmt> parses.
|
|
801
|
-
const stripped = sql.replace(/;\s*$/u,
|
|
802
|
+
const stripped = sql.replace(/;\s*$/u, "");
|
|
802
803
|
const declared = `DECLARE ${CURSOR_NAME} NO SCROLL CURSOR FOR ${stripped}`;
|
|
803
804
|
const fetchSql = `FETCH FORWARD ${String(fetchCount)} FROM ${CURSOR_NAME}`;
|
|
804
805
|
const rowsAffected = 0;
|
|
@@ -859,7 +860,7 @@ const runCursorLoop = async (ctx, sql, fetchCount, out) => {
|
|
|
859
860
|
await db.execSimple(`CLOSE ${CURSOR_NAME}`);
|
|
860
861
|
cursorOpen = false;
|
|
861
862
|
if (initiallyIdle) {
|
|
862
|
-
await db.execSimple(
|
|
863
|
+
await db.execSimple("COMMIT");
|
|
863
864
|
}
|
|
864
865
|
return { rowsAffected, rowsPrinted, lastRowCount: rowsPrinted };
|
|
865
866
|
}
|
|
@@ -913,7 +914,7 @@ const runCursorLoop = async (ctx, sql, fetchCount, out) => {
|
|
|
913
914
|
}
|
|
914
915
|
if (initiallyIdle) {
|
|
915
916
|
try {
|
|
916
|
-
await db.execSimple(
|
|
917
|
+
await db.execSimple("ROLLBACK");
|
|
917
918
|
}
|
|
918
919
|
catch {
|
|
919
920
|
// ignore
|
|
@@ -942,7 +943,7 @@ export const executeAndPrint = async (ctx, sqlRaw, opts = {}) => {
|
|
|
942
943
|
durationMs: 0,
|
|
943
944
|
};
|
|
944
945
|
if (!ctx.settings.db) {
|
|
945
|
-
writeError(ctx,
|
|
946
|
+
writeError(ctx, "no connection to the server");
|
|
946
947
|
stats.hadError = true;
|
|
947
948
|
return stats;
|
|
948
949
|
}
|
|
@@ -990,7 +991,7 @@ export const executeAndPrint = async (ctx, sqlRaw, opts = {}) => {
|
|
|
990
991
|
finally {
|
|
991
992
|
if (ctx.settings.timing) {
|
|
992
993
|
stats.durationMs = performance.now() - started;
|
|
993
|
-
ctx.stdout.write(
|
|
994
|
+
ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
|
|
994
995
|
}
|
|
995
996
|
}
|
|
996
997
|
// Mirror upstream's `SetResultVariables` / `SetErrorVariables` call at the
|
|
@@ -998,8 +999,8 @@ export const executeAndPrint = async (ctx, sqlRaw, opts = {}) => {
|
|
|
998
999
|
// `\echo :SQLSTATE` and friends see the most recent outcome. ROW_COUNT
|
|
999
1000
|
// tracks libpq's `PQcmdTuples` on the LAST result of a `\;` batch.
|
|
1000
1001
|
refreshErrorVars(ctx.settings, stats.hadError
|
|
1001
|
-
? { kind:
|
|
1002
|
-
: { kind:
|
|
1002
|
+
? { kind: "error" }
|
|
1003
|
+
: { kind: "success", rowCount: lastRowCount });
|
|
1003
1004
|
return stats;
|
|
1004
1005
|
};
|
|
1005
1006
|
// ---------------------------------------------------------------------------
|
|
@@ -1029,7 +1030,7 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1029
1030
|
// `PSQLexec` and leaves it set on error.
|
|
1030
1031
|
ctx.settings.lastQuery = sql;
|
|
1031
1032
|
if (!ctx.settings.db) {
|
|
1032
|
-
writeError(ctx,
|
|
1033
|
+
writeError(ctx, "no connection to the server");
|
|
1033
1034
|
stats.hadError = true;
|
|
1034
1035
|
return stats;
|
|
1035
1036
|
}
|
|
@@ -1040,7 +1041,9 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1040
1041
|
// Upstream marks the statement as failed when the user cancels. We
|
|
1041
1042
|
// mirror that so ON_ERROR_STOP halts a script.
|
|
1042
1043
|
stats.hadError = true;
|
|
1043
|
-
ctx.settings.lastErrorResult = {
|
|
1044
|
+
ctx.settings.lastErrorResult = {
|
|
1045
|
+
message: "command cancelled by user",
|
|
1046
|
+
};
|
|
1044
1047
|
return stats;
|
|
1045
1048
|
}
|
|
1046
1049
|
}
|
|
@@ -1056,9 +1059,9 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1056
1059
|
const started = ctx.settings.timing ? performance.now() : 0;
|
|
1057
1060
|
// ----- AUTOCOMMIT: implicit BEGIN ----------------------------------------
|
|
1058
1061
|
let implicitBeginIssued = false;
|
|
1059
|
-
if (!autocommit && readTxStatus(db) ===
|
|
1062
|
+
if (!autocommit && readTxStatus(db) === "I" && !commandNoBegin(sql)) {
|
|
1060
1063
|
try {
|
|
1061
|
-
await db.execSimple(
|
|
1064
|
+
await db.execSimple("BEGIN");
|
|
1062
1065
|
implicitBeginIssued = true;
|
|
1063
1066
|
}
|
|
1064
1067
|
catch (err) {
|
|
@@ -1067,16 +1070,16 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1067
1070
|
stats.hadError = true;
|
|
1068
1071
|
if (ctx.settings.timing) {
|
|
1069
1072
|
stats.durationMs = performance.now() - started;
|
|
1070
|
-
ctx.stdout.write(
|
|
1073
|
+
ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
|
|
1071
1074
|
}
|
|
1072
1075
|
return stats;
|
|
1073
1076
|
}
|
|
1074
1077
|
}
|
|
1075
1078
|
// ----- ON_ERROR_ROLLBACK: SAVEPOINT --------------------------------------
|
|
1076
|
-
const savepointActive = onErrorRollback !==
|
|
1077
|
-
(onErrorRollback ===
|
|
1078
|
-
(onErrorRollback ===
|
|
1079
|
-
readTxStatus(db) ===
|
|
1079
|
+
const savepointActive = onErrorRollback !== "off" &&
|
|
1080
|
+
(onErrorRollback === "on" ||
|
|
1081
|
+
(onErrorRollback === "interactive" && interactive)) &&
|
|
1082
|
+
readTxStatus(db) === "T";
|
|
1080
1083
|
let savepointIssued = false;
|
|
1081
1084
|
if (savepointActive) {
|
|
1082
1085
|
try {
|
|
@@ -1090,7 +1093,7 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1090
1093
|
stats.hadError = true;
|
|
1091
1094
|
if (ctx.settings.timing) {
|
|
1092
1095
|
stats.durationMs = performance.now() - started;
|
|
1093
|
-
ctx.stdout.write(
|
|
1096
|
+
ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
|
|
1094
1097
|
}
|
|
1095
1098
|
return stats;
|
|
1096
1099
|
}
|
|
@@ -1145,7 +1148,7 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1145
1148
|
// Release the now-empty savepoint too, matching upstream.
|
|
1146
1149
|
await db.execSimple(`RELEASE SAVEPOINT ${SAVEPOINT_NAME}`);
|
|
1147
1150
|
}
|
|
1148
|
-
else if (!destroysSavepoint(sql) && readTxStatus(db) ===
|
|
1151
|
+
else if (!destroysSavepoint(sql) && readTxStatus(db) === "T") {
|
|
1149
1152
|
await db.execSimple(`RELEASE SAVEPOINT ${SAVEPOINT_NAME}`);
|
|
1150
1153
|
}
|
|
1151
1154
|
}
|
|
@@ -1175,7 +1178,7 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1175
1178
|
// `pg_log_info` writes to stderr in upstream and strips one trailing
|
|
1176
1179
|
// newline before tacking its own `\n` on the message — we mirror by
|
|
1177
1180
|
// going through ctx.stderr and the explicit trim.
|
|
1178
|
-
if (stats.hadError && ctx.settings.echo ===
|
|
1181
|
+
if (stats.hadError && ctx.settings.echo === "errors") {
|
|
1179
1182
|
// Strip leading whitespace + `--`-style comments from queryBuf so the
|
|
1180
1183
|
// STATEMENT echo matches upstream's shape. Upstream `psqlscan.l`'s
|
|
1181
1184
|
// `{whitespace}` rule (which includes line comments) SUPPRESSES
|
|
@@ -1191,15 +1194,15 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1191
1194
|
const before = stmt.length;
|
|
1192
1195
|
// Leading whitespace including form-feed (matches psqlscan's
|
|
1193
1196
|
// {space} = [ \t\n\r\f]).
|
|
1194
|
-
stmt = stmt.replace(/^[ \t\n\r\f]+/,
|
|
1197
|
+
stmt = stmt.replace(/^[ \t\n\r\f]+/, "");
|
|
1195
1198
|
// Leading `--`-style line comment, up to (but not including) the
|
|
1196
1199
|
// next newline. The trailing newline is then eaten by the next
|
|
1197
1200
|
// whitespace pass.
|
|
1198
|
-
stmt = stmt.replace(/^--[^\n\r]*/,
|
|
1201
|
+
stmt = stmt.replace(/^--[^\n\r]*/, "");
|
|
1199
1202
|
if (stmt.length === before)
|
|
1200
1203
|
break;
|
|
1201
1204
|
}
|
|
1202
|
-
if (stmt.endsWith(
|
|
1205
|
+
if (stmt.endsWith("\n"))
|
|
1203
1206
|
stmt = stmt.slice(0, -1);
|
|
1204
1207
|
ctx.stderr.write(`STATEMENT: ${stmt}\n`);
|
|
1205
1208
|
}
|
|
@@ -1208,11 +1211,11 @@ export const sendQuery = async (ctx, sqlRaw, opts = {}) => {
|
|
|
1208
1211
|
// result of a `\;` batch; SQLSTATE / ERROR reset every statement; the
|
|
1209
1212
|
// LAST_ERROR_* pair only changes on failure (sticky on success).
|
|
1210
1213
|
refreshErrorVars(ctx.settings, stats.hadError
|
|
1211
|
-
? { kind:
|
|
1212
|
-
: { kind:
|
|
1214
|
+
? { kind: "error" }
|
|
1215
|
+
: { kind: "success", rowCount: lastRowCount });
|
|
1213
1216
|
if (ctx.settings.timing) {
|
|
1214
1217
|
stats.durationMs = performance.now() - started;
|
|
1215
|
-
ctx.stdout.write(
|
|
1218
|
+
ctx.stdout.write("\n" + formatDurationMs(stats.durationMs) + "\n");
|
|
1216
1219
|
}
|
|
1217
1220
|
return stats;
|
|
1218
1221
|
};
|