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/print/troff.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatNumericLocale } from
|
|
1
|
+
import { formatNumericLocale } from "./units.js";
|
|
2
2
|
/**
|
|
3
3
|
* Troff MS printer.
|
|
4
4
|
*
|
|
@@ -57,10 +57,10 @@ import { formatNumericLocale } from './units.js';
|
|
|
57
57
|
// INT2, INT4, INT8, FLOAT4, FLOAT8, NUMERIC, INTERVAL.
|
|
58
58
|
const NUMERIC_OIDS = new Set([21, 23, 20, 700, 701, 1700, 1186]);
|
|
59
59
|
export const troffMsPrinter = {
|
|
60
|
-
format:
|
|
60
|
+
format: "troff-ms",
|
|
61
61
|
printQuery(rs, opts, out) {
|
|
62
62
|
const topt = opts.topt;
|
|
63
|
-
if (topt.expanded ===
|
|
63
|
+
if (topt.expanded === "on") {
|
|
64
64
|
return printExpanded(rs, opts, out);
|
|
65
65
|
}
|
|
66
66
|
return printFlat(rs, opts, out);
|
|
@@ -72,55 +72,55 @@ const printFlat = (rs, opts, out) => {
|
|
|
72
72
|
const startTable = topt.startTable;
|
|
73
73
|
const stopTable = topt.stopTable;
|
|
74
74
|
const border = clampBorder(topt.border);
|
|
75
|
-
const nullPrint = opts.nullPrint !==
|
|
75
|
+
const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
|
|
76
76
|
const title = opts.title ?? topt.title;
|
|
77
77
|
const footers = opts.footers ?? topt.footers;
|
|
78
78
|
const headers = rs.fields.map((f) => f.name);
|
|
79
79
|
const ncols = rs.fields.length;
|
|
80
|
-
const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ?
|
|
80
|
+
const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
|
|
81
81
|
const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
|
|
82
|
-
let buf =
|
|
82
|
+
let buf = "";
|
|
83
83
|
if (startTable) {
|
|
84
84
|
if (!tuplesOnly && title) {
|
|
85
|
-
buf +=
|
|
85
|
+
buf += ".LP\n.DS C\n";
|
|
86
86
|
buf += escapeTroff(title);
|
|
87
|
-
buf +=
|
|
87
|
+
buf += "\n.DE\n";
|
|
88
88
|
}
|
|
89
|
-
buf +=
|
|
90
|
-
buf += border === 2 ?
|
|
89
|
+
buf += ".LP\n.TS\n";
|
|
90
|
+
buf += border === 2 ? "center box;\n" : "center;\n";
|
|
91
91
|
aligns.forEach((a, idx) => {
|
|
92
92
|
buf += a;
|
|
93
93
|
if (border > 0 && idx < ncols - 1)
|
|
94
|
-
buf +=
|
|
94
|
+
buf += " | ";
|
|
95
95
|
});
|
|
96
|
-
buf +=
|
|
96
|
+
buf += ".\n";
|
|
97
97
|
if (!tuplesOnly) {
|
|
98
98
|
headers.forEach((h, idx) => {
|
|
99
99
|
if (idx !== 0)
|
|
100
|
-
buf +=
|
|
101
|
-
buf +=
|
|
100
|
+
buf += "\t";
|
|
101
|
+
buf += "\\fI" + escapeTroff(h) + "\\fP";
|
|
102
102
|
});
|
|
103
|
-
buf +=
|
|
103
|
+
buf += "\n_\n";
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
cells.forEach((row) => {
|
|
107
107
|
row.forEach((value, idx) => {
|
|
108
108
|
buf += escapeTroff(value);
|
|
109
109
|
if (idx === ncols - 1)
|
|
110
|
-
buf +=
|
|
110
|
+
buf += "\n";
|
|
111
111
|
else
|
|
112
|
-
buf +=
|
|
112
|
+
buf += "\t";
|
|
113
113
|
});
|
|
114
114
|
});
|
|
115
115
|
if (stopTable) {
|
|
116
|
-
buf +=
|
|
116
|
+
buf += ".TE\n.DS L\n";
|
|
117
117
|
if (!tuplesOnly) {
|
|
118
118
|
const effective = effectiveFooters(rs, topt, footers);
|
|
119
119
|
for (const f of effective) {
|
|
120
|
-
buf += escapeTroff(f) +
|
|
120
|
+
buf += escapeTroff(f) + "\n";
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
-
buf +=
|
|
123
|
+
buf += ".DE\n";
|
|
124
124
|
}
|
|
125
125
|
out.write(buf);
|
|
126
126
|
return Promise.resolve();
|
|
@@ -131,28 +131,28 @@ const printExpanded = (rs, opts, out) => {
|
|
|
131
131
|
const startTable = topt.startTable;
|
|
132
132
|
const stopTable = topt.stopTable;
|
|
133
133
|
const border = clampBorder(topt.border);
|
|
134
|
-
const nullPrint = opts.nullPrint !==
|
|
134
|
+
const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
|
|
135
135
|
const title = opts.title ?? topt.title;
|
|
136
136
|
const footers = opts.footers ?? topt.footers;
|
|
137
137
|
const headers = rs.fields.map((f) => f.name);
|
|
138
138
|
const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
|
|
139
|
-
let buf =
|
|
139
|
+
let buf = "";
|
|
140
140
|
// currentFormat: 0 = none yet, 1 = "Record N" header (c s),
|
|
141
141
|
// 2 = body (c l or c | l). Upstream uses the same tri-state to
|
|
142
142
|
// decide when to emit `.T&` separators.
|
|
143
143
|
let currentFormat = 0;
|
|
144
144
|
if (startTable) {
|
|
145
145
|
if (!tuplesOnly && title) {
|
|
146
|
-
buf +=
|
|
146
|
+
buf += ".LP\n.DS C\n";
|
|
147
147
|
buf += escapeTroff(title);
|
|
148
|
-
buf +=
|
|
148
|
+
buf += "\n.DE\n";
|
|
149
149
|
}
|
|
150
|
-
buf +=
|
|
151
|
-
buf += border === 2 ?
|
|
150
|
+
buf += ".LP\n.TS\n";
|
|
151
|
+
buf += border === 2 ? "center box;\n" : "center;\n";
|
|
152
152
|
// Under tuples-only, upstream emits a one-shot `c l;` body spec
|
|
153
153
|
// here so each Record's first .T& block is omitted.
|
|
154
154
|
if (tuplesOnly) {
|
|
155
|
-
buf +=
|
|
155
|
+
buf += "c l;\n";
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
else {
|
|
@@ -164,41 +164,41 @@ const printExpanded = (rs, opts, out) => {
|
|
|
164
164
|
if (!tuplesOnly) {
|
|
165
165
|
if (currentFormat !== 1) {
|
|
166
166
|
if (border === 2 && rowIdx > 0)
|
|
167
|
-
buf +=
|
|
167
|
+
buf += "_\n";
|
|
168
168
|
if (currentFormat !== 0)
|
|
169
|
-
buf +=
|
|
170
|
-
buf +=
|
|
169
|
+
buf += ".T&\n";
|
|
170
|
+
buf += "c s.\n";
|
|
171
171
|
currentFormat = 1;
|
|
172
172
|
}
|
|
173
173
|
buf += `\\fIRecord ${String(record)}\\fP\n`;
|
|
174
174
|
record += 1;
|
|
175
175
|
}
|
|
176
176
|
if (border >= 1)
|
|
177
|
-
buf +=
|
|
177
|
+
buf += "_\n";
|
|
178
178
|
if (!tuplesOnly) {
|
|
179
179
|
if (currentFormat !== 2) {
|
|
180
180
|
if (currentFormat !== 0)
|
|
181
|
-
buf +=
|
|
182
|
-
buf += border !== 1 ?
|
|
181
|
+
buf += ".T&\n";
|
|
182
|
+
buf += border !== 1 ? "c l.\n" : "c | l.\n";
|
|
183
183
|
currentFormat = 2;
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
row.forEach((value, idx) => {
|
|
187
187
|
buf += escapeTroff(headers[idx]);
|
|
188
|
-
buf +=
|
|
188
|
+
buf += "\t";
|
|
189
189
|
buf += escapeTroff(value);
|
|
190
|
-
buf +=
|
|
190
|
+
buf += "\n";
|
|
191
191
|
});
|
|
192
192
|
});
|
|
193
193
|
if (stopTable) {
|
|
194
|
-
buf +=
|
|
194
|
+
buf += ".TE\n.DS L\n";
|
|
195
195
|
// Expanded mode does NOT emit the default "(N rows)" footer.
|
|
196
196
|
if (!tuplesOnly && footers && footers.length > 0) {
|
|
197
197
|
for (const f of footers) {
|
|
198
|
-
buf += escapeTroff(f) +
|
|
198
|
+
buf += escapeTroff(f) + "\n";
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
buf +=
|
|
201
|
+
buf += ".DE\n";
|
|
202
202
|
}
|
|
203
203
|
out.write(buf);
|
|
204
204
|
return Promise.resolve();
|
|
@@ -215,7 +215,7 @@ const effectiveFooters = (rs, topt, footers) => {
|
|
|
215
215
|
return footers;
|
|
216
216
|
if (topt.defaultFooter) {
|
|
217
217
|
const n = rs.rows.length;
|
|
218
|
-
return [`(${String(n)} ${n === 1 ?
|
|
218
|
+
return [`(${String(n)} ${n === 1 ? "row" : "rows"})`];
|
|
219
219
|
}
|
|
220
220
|
return [];
|
|
221
221
|
};
|
|
@@ -226,10 +226,10 @@ const escapeTroff = (input) => {
|
|
|
226
226
|
// raw newlines inside a cell; that constraint is enforced upstream
|
|
227
227
|
// by the caller, not here. (`renderCell` similarly never emits a raw
|
|
228
228
|
// newline today.)
|
|
229
|
-
let out =
|
|
229
|
+
let out = "";
|
|
230
230
|
for (const ch of input) {
|
|
231
|
-
if (ch ===
|
|
232
|
-
out +=
|
|
231
|
+
if (ch === "\\")
|
|
232
|
+
out += "\\(rs";
|
|
233
233
|
else
|
|
234
234
|
out += ch;
|
|
235
235
|
}
|
|
@@ -238,20 +238,20 @@ const escapeTroff = (input) => {
|
|
|
238
238
|
const renderCell = (cell, nullPrint, numericLocale) => {
|
|
239
239
|
if (cell === null || cell === undefined)
|
|
240
240
|
return nullPrint;
|
|
241
|
-
if (typeof cell ===
|
|
241
|
+
if (typeof cell === "string") {
|
|
242
242
|
return formatNumericLocale(cell, numericLocale);
|
|
243
243
|
}
|
|
244
|
-
if (typeof cell ===
|
|
244
|
+
if (typeof cell === "number" || typeof cell === "bigint") {
|
|
245
245
|
return formatNumericLocale(cell.toString(), numericLocale);
|
|
246
246
|
}
|
|
247
|
-
if (typeof cell ===
|
|
248
|
-
return cell ?
|
|
247
|
+
if (typeof cell === "boolean")
|
|
248
|
+
return cell ? "t" : "f";
|
|
249
249
|
if (cell instanceof Date)
|
|
250
250
|
return cell.toISOString();
|
|
251
251
|
if (cell instanceof Uint8Array) {
|
|
252
|
-
let hex =
|
|
252
|
+
let hex = "\\x";
|
|
253
253
|
for (const b of cell)
|
|
254
|
-
hex += b.toString(16).padStart(2,
|
|
254
|
+
hex += b.toString(16).padStart(2, "0");
|
|
255
255
|
return hex;
|
|
256
256
|
}
|
|
257
257
|
return JSON.stringify(cell);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatNumericLocale } from
|
|
1
|
+
import { formatNumericLocale } from "./units.js";
|
|
2
2
|
/**
|
|
3
3
|
* Unaligned tabular printer.
|
|
4
4
|
*
|
|
@@ -15,17 +15,17 @@ import { formatNumericLocale } from './units.js';
|
|
|
15
15
|
* mode and `topt.defaultFooter` is true; same as upstream psql.
|
|
16
16
|
*/
|
|
17
17
|
export const unalignedPrinter = {
|
|
18
|
-
format:
|
|
18
|
+
format: "unaligned",
|
|
19
19
|
printQuery(rs, opts, out) {
|
|
20
20
|
const topt = opts.topt;
|
|
21
|
-
const fieldSep = topt.fieldSep !==
|
|
22
|
-
const recordSep = topt.recordSep !==
|
|
23
|
-
const nullPrint = opts.nullPrint !==
|
|
24
|
-
const expanded = topt.expanded ===
|
|
21
|
+
const fieldSep = topt.fieldSep !== "" ? topt.fieldSep : "|";
|
|
22
|
+
const recordSep = topt.recordSep !== "" ? topt.recordSep : "\n";
|
|
23
|
+
const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
|
|
24
|
+
const expanded = topt.expanded === "on";
|
|
25
25
|
const tuplesOnly = topt.tuplesOnly;
|
|
26
26
|
const headers = rs.fields.map((f) => f.name);
|
|
27
27
|
const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
|
|
28
|
-
let outBuf =
|
|
28
|
+
let outBuf = "";
|
|
29
29
|
if (expanded) {
|
|
30
30
|
// Vertical mode mirrors print.c `print_unaligned_vertical`: each
|
|
31
31
|
// record gap is a DOUBLE recordSep, the title (if any) emits
|
|
@@ -82,7 +82,8 @@ export const unalignedPrinter = {
|
|
|
82
82
|
opts.footers.length > 0;
|
|
83
83
|
if (!tuplesOnly && topt.defaultFooter && !hasUserFooters) {
|
|
84
84
|
const n = rs.rows.length;
|
|
85
|
-
outBuf +=
|
|
85
|
+
outBuf +=
|
|
86
|
+
`(${String(n)} ${n === 1 ? "row" : "rows"})` + recordSep;
|
|
86
87
|
}
|
|
87
88
|
if (!tuplesOnly && hasUserFooters) {
|
|
88
89
|
for (const footer of opts.footers ?? []) {
|
|
@@ -97,21 +98,21 @@ export const unalignedPrinter = {
|
|
|
97
98
|
const renderCell = (cell, nullPrint, numericLocale) => {
|
|
98
99
|
if (cell === null || cell === undefined)
|
|
99
100
|
return nullPrint;
|
|
100
|
-
if (typeof cell ===
|
|
101
|
+
if (typeof cell === "string") {
|
|
101
102
|
return formatNumericLocale(cell, numericLocale);
|
|
102
103
|
}
|
|
103
|
-
if (typeof cell ===
|
|
104
|
+
if (typeof cell === "number" || typeof cell === "bigint") {
|
|
104
105
|
return formatNumericLocale(cell.toString(), numericLocale);
|
|
105
106
|
}
|
|
106
|
-
if (typeof cell ===
|
|
107
|
-
return cell ?
|
|
107
|
+
if (typeof cell === "boolean")
|
|
108
|
+
return cell ? "t" : "f";
|
|
108
109
|
if (cell instanceof Date)
|
|
109
110
|
return cell.toISOString();
|
|
110
111
|
if (cell instanceof Uint8Array) {
|
|
111
112
|
// Bytea -> hex escape, matching libpq's `\x` form.
|
|
112
|
-
let hex =
|
|
113
|
+
let hex = "\\x";
|
|
113
114
|
for (const b of cell)
|
|
114
|
-
hex += b.toString(16).padStart(2,
|
|
115
|
+
hex += b.toString(16).padStart(2, "0");
|
|
115
116
|
return hex;
|
|
116
117
|
}
|
|
117
118
|
return JSON.stringify(cell);
|
package/dist/psql/print/units.js
CHANGED
|
@@ -21,14 +21,14 @@ export const formatNumericLocale = (value, useLocale, locale) => {
|
|
|
21
21
|
return value;
|
|
22
22
|
if (!NUMERIC_RE.test(value))
|
|
23
23
|
return value;
|
|
24
|
-
const dotIdx = value.indexOf(
|
|
24
|
+
const dotIdx = value.indexOf(".");
|
|
25
25
|
const fractionDigits = dotIdx === -1 ? 0 : value.length - dotIdx - 1;
|
|
26
26
|
// Use BigInt for the integer part to avoid float precision loss on
|
|
27
27
|
// very large integers; format the fractional component as a separate
|
|
28
28
|
// number so we keep the exact digit count the input had.
|
|
29
|
-
const negative = value.startsWith(
|
|
29
|
+
const negative = value.startsWith("-");
|
|
30
30
|
const unsigned = negative ? value.slice(1) : value;
|
|
31
|
-
const [intPart, fracPart =
|
|
31
|
+
const [intPart, fracPart = ""] = unsigned.split(".");
|
|
32
32
|
const intFormatted = new Intl.NumberFormat(locale, {
|
|
33
33
|
useGrouping: true,
|
|
34
34
|
maximumFractionDigits: 0,
|
|
@@ -39,25 +39,25 @@ export const formatNumericLocale = (value, useLocale, locale) => {
|
|
|
39
39
|
// Discover this locale's decimal separator.
|
|
40
40
|
const decimalSep = new Intl.NumberFormat(locale)
|
|
41
41
|
.formatToParts(1.1)
|
|
42
|
-
.find((p) => p.type ===
|
|
42
|
+
.find((p) => p.type === "decimal")?.value ?? ".";
|
|
43
43
|
const out = `${intFormatted}${decimalSep}${fracPart}`;
|
|
44
44
|
return negative ? `-${out}` : out;
|
|
45
45
|
};
|
|
46
46
|
const BYTE_UNITS = [
|
|
47
|
-
{ name:
|
|
48
|
-
{ name:
|
|
49
|
-
{ name:
|
|
50
|
-
{ name:
|
|
51
|
-
{ name:
|
|
52
|
-
{ name:
|
|
47
|
+
{ name: "bytes", bits: 0 },
|
|
48
|
+
{ name: "kB", bits: 10 },
|
|
49
|
+
{ name: "MB", bits: 20 },
|
|
50
|
+
{ name: "GB", bits: 30 },
|
|
51
|
+
{ name: "TB", bits: 40 },
|
|
52
|
+
{ name: "PB", bits: 50 },
|
|
53
53
|
];
|
|
54
54
|
const SI_UNITS = [
|
|
55
|
-
{ name:
|
|
56
|
-
{ name:
|
|
57
|
-
{ name:
|
|
58
|
-
{ name:
|
|
59
|
-
{ name:
|
|
60
|
-
{ name:
|
|
55
|
+
{ name: "B", bits: 0 },
|
|
56
|
+
{ name: "kB", bits: 10 },
|
|
57
|
+
{ name: "MB", bits: 20 },
|
|
58
|
+
{ name: "GB", bits: 30 },
|
|
59
|
+
{ name: "TB", bits: 40 },
|
|
60
|
+
{ name: "PB", bits: 50 },
|
|
61
61
|
];
|
|
62
62
|
/**
|
|
63
63
|
* Pretty-print a byte count.
|
|
@@ -72,7 +72,7 @@ const SI_UNITS = [
|
|
|
72
72
|
*/
|
|
73
73
|
export const formatByteSize = (bytes, opts) => {
|
|
74
74
|
const units = opts?.si ? SI_UNITS : BYTE_UNITS;
|
|
75
|
-
const sign = bytes < 0 ?
|
|
75
|
+
const sign = bytes < 0 ? "-" : "";
|
|
76
76
|
let abs = Math.abs(bytes);
|
|
77
77
|
// Bytes unit: render integer count.
|
|
78
78
|
if (abs < 10 * 1024) {
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
* octal `\ooo`, hex `\xhh`, and `\<other>` as a literal character. We
|
|
59
59
|
* apply them in-line so the returned arg contains the decoded value.
|
|
60
60
|
*/
|
|
61
|
-
import { execSync } from
|
|
62
|
-
import { dequote } from
|
|
63
|
-
const WHITESPACE =
|
|
61
|
+
import { execSync } from "node:child_process";
|
|
62
|
+
import { dequote } from "./stringutils.js";
|
|
63
|
+
const WHITESPACE = " \t\n\r\f\v";
|
|
64
64
|
const VARIABLE_CHAR_RE = /[A-Za-z0-9_\x80-\xff]/;
|
|
65
65
|
const isVarChar = (c) => c !== undefined && VARIABLE_CHAR_RE.test(c);
|
|
66
66
|
const isWhitespace = (c) => c !== undefined && WHITESPACE.includes(c);
|
|
@@ -74,12 +74,12 @@ const isWhitespace = (c) => c !== undefined && WHITESPACE.includes(c);
|
|
|
74
74
|
*/
|
|
75
75
|
const quoteSqlLiteral = (value) => {
|
|
76
76
|
let needsEscape = false;
|
|
77
|
-
let inner =
|
|
77
|
+
let inner = "";
|
|
78
78
|
for (const c of value) {
|
|
79
79
|
if (c === "'")
|
|
80
80
|
inner += "''";
|
|
81
|
-
else if (c ===
|
|
82
|
-
inner +=
|
|
81
|
+
else if (c === "\\") {
|
|
82
|
+
inner += "\\\\";
|
|
83
83
|
needsEscape = true;
|
|
84
84
|
}
|
|
85
85
|
else {
|
|
@@ -93,7 +93,7 @@ const quoteSqlLiteral = (value) => {
|
|
|
93
93
|
* Wraps the value in `"…"` and doubles any embedded `"`.
|
|
94
94
|
*/
|
|
95
95
|
const quoteSqlIdent = (value) => {
|
|
96
|
-
let inner =
|
|
96
|
+
let inner = "";
|
|
97
97
|
for (const c of value) {
|
|
98
98
|
inner += c === '"' ? '""' : c;
|
|
99
99
|
}
|
|
@@ -107,7 +107,7 @@ const quoteSqlIdent = (value) => {
|
|
|
107
107
|
* (otherwise).
|
|
108
108
|
*/
|
|
109
109
|
const dequoteDowncaseIdentifier = (str, downcase) => {
|
|
110
|
-
let out =
|
|
110
|
+
let out = "";
|
|
111
111
|
let inquotes = false;
|
|
112
112
|
let i = 0;
|
|
113
113
|
while (i < str.length) {
|
|
@@ -140,21 +140,21 @@ const dequoteDowncaseIdentifier = (str, downcase) => {
|
|
|
140
140
|
const tryConsumeVarSubstitution = (s, i, varLookup) => {
|
|
141
141
|
if (varLookup === undefined)
|
|
142
142
|
return null;
|
|
143
|
-
if (s[i] !==
|
|
143
|
+
if (s[i] !== ":")
|
|
144
144
|
return null;
|
|
145
145
|
// :{?varname} — defined-variable test, emits literal TRUE / FALSE.
|
|
146
146
|
// Mirrors upstream `psqlscanslash.l`'s `:\{\?{variable_char}+\}` rule
|
|
147
147
|
// (calls `psqlscan_test_variable`). A malformed expression (missing
|
|
148
148
|
// closing `}` or empty name) falls through to `null` so the caller emits
|
|
149
149
|
// the literal `:` and continues.
|
|
150
|
-
if (s[i + 1] ===
|
|
150
|
+
if (s[i + 1] === "{" && s[i + 2] === "?") {
|
|
151
151
|
let j = i + 3;
|
|
152
152
|
while (j < s.length && isVarChar(s[j]))
|
|
153
153
|
j++;
|
|
154
|
-
if (j > i + 3 && s[j] ===
|
|
154
|
+
if (j > i + 3 && s[j] === "}") {
|
|
155
155
|
const name = s.slice(i + 3, j);
|
|
156
156
|
const value = varLookup(name);
|
|
157
|
-
return { end: j + 1, text: value !== undefined ?
|
|
157
|
+
return { end: j + 1, text: value !== undefined ? "TRUE" : "FALSE" };
|
|
158
158
|
}
|
|
159
159
|
return null;
|
|
160
160
|
}
|
|
@@ -216,7 +216,7 @@ const tryConsumeVarSubstitution = (s, i, varLookup) => {
|
|
|
216
216
|
* just past the closing quote).
|
|
217
217
|
*/
|
|
218
218
|
const consumeSingleQuoted = (s, start) => {
|
|
219
|
-
let out =
|
|
219
|
+
let out = "";
|
|
220
220
|
let i = start;
|
|
221
221
|
while (i < s.length) {
|
|
222
222
|
const c = s[i];
|
|
@@ -228,41 +228,41 @@ const consumeSingleQuoted = (s, start) => {
|
|
|
228
228
|
}
|
|
229
229
|
return { end: i + 1, text: out };
|
|
230
230
|
}
|
|
231
|
-
if (c ===
|
|
231
|
+
if (c === "\\" && i + 1 < s.length) {
|
|
232
232
|
const next = s[i + 1];
|
|
233
|
-
if (next ===
|
|
234
|
-
out +=
|
|
233
|
+
if (next === "n") {
|
|
234
|
+
out += "\n";
|
|
235
235
|
i += 2;
|
|
236
236
|
continue;
|
|
237
237
|
}
|
|
238
|
-
if (next ===
|
|
239
|
-
out +=
|
|
238
|
+
if (next === "t") {
|
|
239
|
+
out += "\t";
|
|
240
240
|
i += 2;
|
|
241
241
|
continue;
|
|
242
242
|
}
|
|
243
|
-
if (next ===
|
|
244
|
-
out +=
|
|
243
|
+
if (next === "b") {
|
|
244
|
+
out += "\b";
|
|
245
245
|
i += 2;
|
|
246
246
|
continue;
|
|
247
247
|
}
|
|
248
|
-
if (next ===
|
|
249
|
-
out +=
|
|
248
|
+
if (next === "r") {
|
|
249
|
+
out += "\r";
|
|
250
250
|
i += 2;
|
|
251
251
|
continue;
|
|
252
252
|
}
|
|
253
|
-
if (next ===
|
|
254
|
-
out +=
|
|
253
|
+
if (next === "f") {
|
|
254
|
+
out += "\f";
|
|
255
255
|
i += 2;
|
|
256
256
|
continue;
|
|
257
257
|
}
|
|
258
258
|
// Octal: \ooo (1–3 digits)
|
|
259
|
-
if (next >=
|
|
259
|
+
if (next >= "0" && next <= "7") {
|
|
260
260
|
const j = i + 1;
|
|
261
261
|
let octEnd = j;
|
|
262
262
|
while (octEnd < s.length &&
|
|
263
263
|
octEnd - j < 3 &&
|
|
264
|
-
s[octEnd] >=
|
|
265
|
-
s[octEnd] <=
|
|
264
|
+
s[octEnd] >= "0" &&
|
|
265
|
+
s[octEnd] <= "7") {
|
|
266
266
|
octEnd++;
|
|
267
267
|
}
|
|
268
268
|
const code = parseInt(s.slice(j, octEnd), 8);
|
|
@@ -271,11 +271,13 @@ const consumeSingleQuoted = (s, start) => {
|
|
|
271
271
|
continue;
|
|
272
272
|
}
|
|
273
273
|
// Hex: \xhh (1–2 digits)
|
|
274
|
-
if (next ===
|
|
274
|
+
if (next === "x") {
|
|
275
275
|
const j = i + 2;
|
|
276
276
|
const hexRe = /[0-9a-fA-F]/;
|
|
277
277
|
let hexEnd = j;
|
|
278
|
-
while (hexEnd < s.length &&
|
|
278
|
+
while (hexEnd < s.length &&
|
|
279
|
+
hexEnd - j < 2 &&
|
|
280
|
+
hexRe.test(s[hexEnd])) {
|
|
279
281
|
hexEnd++;
|
|
280
282
|
}
|
|
281
283
|
if (hexEnd > j) {
|
|
@@ -324,12 +326,12 @@ const consumeDoubleQuoted = (s, start) => {
|
|
|
324
326
|
*/
|
|
325
327
|
export const BACKTICK_EXECUTOR = {
|
|
326
328
|
current: (cmd) => execSync(cmd, {
|
|
327
|
-
shell:
|
|
328
|
-
encoding:
|
|
329
|
+
shell: "/bin/sh",
|
|
330
|
+
encoding: "utf8",
|
|
329
331
|
// Children inherit the parent env but get no stdin pipe — matches
|
|
330
332
|
// upstream's `popen(cmd, "r")` semantics. stderr passes through so
|
|
331
333
|
// shell error output is visible to the user.
|
|
332
|
-
stdio: [
|
|
334
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
333
335
|
// Defensive cap: backtick output goes into a slash arg, so a runaway
|
|
334
336
|
// command shouldn't be able to fill arbitrary memory.
|
|
335
337
|
maxBuffer: 1 << 20,
|
|
@@ -348,12 +350,12 @@ export const BACKTICK_EXECUTOR = {
|
|
|
348
350
|
*/
|
|
349
351
|
const runBacktickCommand = (cmd) => {
|
|
350
352
|
if (cmd.length === 0)
|
|
351
|
-
return
|
|
353
|
+
return "";
|
|
352
354
|
try {
|
|
353
355
|
const out = BACKTICK_EXECUTOR.current(cmd);
|
|
354
356
|
// Trim a single trailing newline; preserve interior newlines so multi-line
|
|
355
357
|
// output (e.g. `\set FOO `cat file``) lands as-is.
|
|
356
|
-
return out.endsWith(
|
|
358
|
+
return out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
357
359
|
}
|
|
358
360
|
catch (err) {
|
|
359
361
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -361,7 +363,7 @@ const runBacktickCommand = (cmd) => {
|
|
|
361
363
|
// have file/line context in the scanner; emit the prefix verbatim and
|
|
362
364
|
// include the command for diagnosis.
|
|
363
365
|
process.stderr.write(`psql: error: \\!: ${cmd}: ${msg}\n`);
|
|
364
|
-
return
|
|
366
|
+
return "";
|
|
365
367
|
}
|
|
366
368
|
};
|
|
367
369
|
/**
|
|
@@ -378,11 +380,11 @@ const runBacktickCommand = (cmd) => {
|
|
|
378
380
|
* succeed; tests cover both well-formed and unterminated cases.
|
|
379
381
|
*/
|
|
380
382
|
const consumeBackQuoted = (s, start, varLookup) => {
|
|
381
|
-
let inner =
|
|
383
|
+
let inner = "";
|
|
382
384
|
let i = start;
|
|
383
385
|
while (i < s.length) {
|
|
384
386
|
const c = s[i];
|
|
385
|
-
if (c ===
|
|
387
|
+
if (c === "`") {
|
|
386
388
|
return { end: i + 1, text: runBacktickCommand(inner) };
|
|
387
389
|
}
|
|
388
390
|
const sub = tryConsumeVarSubstitution(s, i, varLookup);
|
|
@@ -410,7 +412,7 @@ const scanOneArg = (s, i, mode, varLookup) => {
|
|
|
410
412
|
if (i >= s.length)
|
|
411
413
|
return null;
|
|
412
414
|
// filepipe special: a leading `|` flips into whole-line mode for this arg.
|
|
413
|
-
if (mode ===
|
|
415
|
+
if (mode === "filepipe" && s[i] === "|") {
|
|
414
416
|
const rest = s.slice(i);
|
|
415
417
|
return { end: s.length, arg: rest };
|
|
416
418
|
}
|
|
@@ -421,12 +423,12 @@ const scanOneArg = (s, i, mode, varLookup) => {
|
|
|
421
423
|
// - a :var / :'var' / :"var" substitution
|
|
422
424
|
// - a literal character (the catch-all)
|
|
423
425
|
// We stop on whitespace or `\` (which begins the next slash command).
|
|
424
|
-
let out =
|
|
426
|
+
let out = "";
|
|
425
427
|
while (i < s.length) {
|
|
426
428
|
const c = s[i];
|
|
427
429
|
if (isWhitespace(c))
|
|
428
430
|
break;
|
|
429
|
-
if (c ===
|
|
431
|
+
if (c === "\\")
|
|
430
432
|
break;
|
|
431
433
|
if (c === "'") {
|
|
432
434
|
const r = consumeSingleQuoted(s, i + 1);
|
|
@@ -440,7 +442,7 @@ const scanOneArg = (s, i, mode, varLookup) => {
|
|
|
440
442
|
i = r.end;
|
|
441
443
|
continue;
|
|
442
444
|
}
|
|
443
|
-
if (c ===
|
|
445
|
+
if (c === "`") {
|
|
444
446
|
const r = consumeBackQuoted(s, i + 1, varLookup);
|
|
445
447
|
out += r.text;
|
|
446
448
|
i = r.end;
|
|
@@ -473,7 +475,7 @@ export const scanSlashArgs = (input, mode, varLookup) => {
|
|
|
473
475
|
// Whole-line: return everything, with leading whitespace suppressed and a
|
|
474
476
|
// single trailing newline (if any) preserved verbatim. Empty (or
|
|
475
477
|
// whitespace-only) input yields no args.
|
|
476
|
-
if (mode ===
|
|
478
|
+
if (mode === "whole-line") {
|
|
477
479
|
let start = 0;
|
|
478
480
|
while (start < input.length && isWhitespace(input[start]))
|
|
479
481
|
start++;
|
|
@@ -481,7 +483,7 @@ export const scanSlashArgs = (input, mode, varLookup) => {
|
|
|
481
483
|
return [];
|
|
482
484
|
return [input.slice(start)];
|
|
483
485
|
}
|
|
484
|
-
const effectiveLookup = mode ===
|
|
486
|
+
const effectiveLookup = mode === "no-vars" ? undefined : varLookup;
|
|
485
487
|
const args = [];
|
|
486
488
|
let i = 0;
|
|
487
489
|
while (i < input.length) {
|
|
@@ -491,10 +493,10 @@ export const scanSlashArgs = (input, mode, varLookup) => {
|
|
|
491
493
|
let arg = result.arg;
|
|
492
494
|
// sql-id / sql-id-keep-case post-process: collapse SQL-identifier
|
|
493
495
|
// quoting, optionally downcasing unquoted letters.
|
|
494
|
-
if (mode ===
|
|
496
|
+
if (mode === "sql-id") {
|
|
495
497
|
arg = dequoteDowncaseIdentifier(arg, true);
|
|
496
498
|
}
|
|
497
|
-
else if (mode ===
|
|
499
|
+
else if (mode === "sql-id-keep-case") {
|
|
498
500
|
arg = dequoteDowncaseIdentifier(arg, false);
|
|
499
501
|
}
|
|
500
502
|
args.push(arg);
|
|
@@ -503,7 +505,7 @@ export const scanSlashArgs = (input, mode, varLookup) => {
|
|
|
503
505
|
while (i < input.length && isWhitespace(input[i]))
|
|
504
506
|
i++;
|
|
505
507
|
// Stop on a `\` — start of the next backslash command.
|
|
506
|
-
if (input[i] ===
|
|
508
|
+
if (input[i] === "\\")
|
|
507
509
|
break;
|
|
508
510
|
}
|
|
509
511
|
return args;
|