@batadata/cli 0.2.8 → 0.2.10
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/dist/commands/powdb.d.ts +15 -1
- package/dist/commands/powdb.js +172 -12
- package/package.json +1 -1
package/dist/commands/powdb.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface TableSchema {
|
|
|
45
45
|
constraints: ConstraintInfo[];
|
|
46
46
|
}
|
|
47
47
|
/** The Stage-A PowDB types a static PowQL artifact can carry. */
|
|
48
|
-
export type PowType = "int" | "float" | "str" | "bool" | "datetime";
|
|
48
|
+
export type PowType = "int" | "float" | "str" | "bool" | "datetime" | "json";
|
|
49
49
|
export interface TypeMapping {
|
|
50
50
|
/** null → PowDB has no Stage-A representation; the column is DROPPED. */
|
|
51
51
|
powType: PowType | null;
|
|
@@ -133,8 +133,16 @@ export interface TablePlan {
|
|
|
133
133
|
pgName: string;
|
|
134
134
|
powName: string;
|
|
135
135
|
columns: ColumnPlan[];
|
|
136
|
+
/** powNames of carried single-column secondary (non-unique) indexes. */
|
|
137
|
+
secondaryIndexes: string[];
|
|
136
138
|
warnings: string[];
|
|
137
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Extract the single plain column of a non-unique btree index definition, or
|
|
142
|
+
* null when the index can't be carried (multi-column, expression, partial,
|
|
143
|
+
* non-btree). Exported for unit testing.
|
|
144
|
+
*/
|
|
145
|
+
export declare function parseSingleColumnIndex(definition: string): string | null;
|
|
138
146
|
/**
|
|
139
147
|
* Build the per-table PowQL `type` plan from a Postgres table's schema,
|
|
140
148
|
* accumulating an honesty note for every column/constraint that can't be
|
|
@@ -143,6 +151,12 @@ export interface TablePlan {
|
|
|
143
151
|
export declare function planTable(t: TableSchema): TablePlan;
|
|
144
152
|
/** Render a table plan as a PowQL `type` DDL block. */
|
|
145
153
|
export declare function renderTypeDdl(plan: TablePlan): string;
|
|
154
|
+
/**
|
|
155
|
+
* Render a plan's carried secondary indexes as `alter T add index .col;`
|
|
156
|
+
* statements (PowQL DDL, valid since 0.1.1; keyword columns backtick-quote
|
|
157
|
+
* inside the path, e.g. `alter Post add index .\`order\``).
|
|
158
|
+
*/
|
|
159
|
+
export declare function renderIndexDdl(plan: TablePlan): string[];
|
|
146
160
|
export interface InsertResult {
|
|
147
161
|
statements: string[];
|
|
148
162
|
warnings: string[];
|
package/dist/commands/powdb.js
CHANGED
|
@@ -3,11 +3,12 @@ import * as path from "node:path";
|
|
|
3
3
|
import { api, apiError } from "../api.js";
|
|
4
4
|
import { requireToken, isJsonMode, loadConfig } from "../config.js";
|
|
5
5
|
import { colors, log, json, spinner, heading, table } from "../utils/logger.js";
|
|
6
|
+
import { confirmDestructive } from "../utils/prompts.js";
|
|
6
7
|
import { emitError, isRetryable } from "../utils/errors.js";
|
|
7
8
|
import { resolveProjectId, resolveBranchId } from "../link.js";
|
|
8
9
|
// PowQL reserved words a column/table name must not collide with. Mirrors the
|
|
9
10
|
// FULL engine lexer keyword set (crates/query/src/lexer.rs POWQL_KEYWORDS as
|
|
10
|
-
// of PowDB 0.
|
|
11
|
+
// of PowDB 0.13.0 — which added `raw`, `json_type`, `json_text`). A colliding name is
|
|
11
12
|
// emitted backtick-quoted (0.10+ syntax) rather than renamed, so the exported
|
|
12
13
|
// column keeps its source name. Keep this list in sync on PowDB upgrades: a
|
|
13
14
|
// missing word here means a bare identifier that fails to parse on load.
|
|
@@ -20,7 +21,8 @@ const POWQL_KEYWORDS = new Set([
|
|
|
20
21
|
"having", "in", "index", "inner", "insert", "is", "join", "left", "length",
|
|
21
22
|
"let", "like", "limit", "link", "lower", "match", "materialize",
|
|
22
23
|
"materialized", "max", "min", "multi", "not", "now", "null", "offset", "on",
|
|
23
|
-
"
|
|
24
|
+
"json_text", "json_type", "or", "order", "outer", "over", "partition",
|
|
25
|
+
"pow", "rank", "raw", "refresh",
|
|
24
26
|
"required", "returning", "right", "rollback", "round", "row_number",
|
|
25
27
|
"schema", "select", "sqrt", "substring", "sum", "then", "transaction",
|
|
26
28
|
"trim", "true", "type", "union", "unique", "update", "upper", "upsert",
|
|
@@ -74,9 +76,14 @@ export function mapPgType(dataType) {
|
|
|
74
76
|
if (t === "bytea") {
|
|
75
77
|
return { powType: "str", note: "bytea carried as hex text (str) — PowDB has no static bytes literal for a script load" };
|
|
76
78
|
}
|
|
77
|
-
// json/jsonb
|
|
79
|
+
// json/jsonb → native PowDB `json` (0.12+): canonical binary (PJ1), `->`
|
|
80
|
+
// path querying. Canonicalization is honest-but-lossy for exact text: object
|
|
81
|
+
// keys re-sort bytewise and duplicate keys collapse last-wins (same as pg jsonb).
|
|
78
82
|
if (t === "json" || t === "jsonb") {
|
|
79
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
powType: "json",
|
|
85
|
+
note: `${t} stored as native PowDB json (requires PowDB 0.12+; canonical form — key order normalized${t === "json" ? ", duplicate keys last-wins" : ""})`,
|
|
86
|
+
};
|
|
80
87
|
}
|
|
81
88
|
// Everything else (arrays, enums, ranges, geometry, network, tsvector,
|
|
82
89
|
// composite/user-defined) has no honest Stage-A representation.
|
|
@@ -182,6 +189,16 @@ export function powqlLiteral(value, powType) {
|
|
|
182
189
|
const raw = typeof value === "string" ? value : JSON.stringify(value);
|
|
183
190
|
return { literal: `"${escapePowqlString(raw)}"`, risky: isLoadRiskyString(raw) };
|
|
184
191
|
}
|
|
192
|
+
case "json": {
|
|
193
|
+
// PowQL inserts JSON as a string literal (validated + canonicalized by
|
|
194
|
+
// the engine). Rows arrive as parsed JSON over SQL-over-HTTP, so
|
|
195
|
+
// re-stringify the VALUE — a JS string here is a JSON string scalar
|
|
196
|
+
// document and must be emitted quoted-inside-the-document.
|
|
197
|
+
const doc = JSON.stringify(value);
|
|
198
|
+
if (doc === undefined)
|
|
199
|
+
return { skip: true, reason: `value has no JSON representation` };
|
|
200
|
+
return { literal: `"${escapePowqlString(doc)}"`, risky: isLoadRiskyString(doc) };
|
|
201
|
+
}
|
|
185
202
|
}
|
|
186
203
|
}
|
|
187
204
|
/**
|
|
@@ -228,9 +245,49 @@ export function parsePgDefault(defaultValue, powType) {
|
|
|
228
245
|
return { kind: "literal", text: `"${escapePowqlString(m[1].replace(/''/g, "'"))}"` };
|
|
229
246
|
return { kind: "drop", note: `expression default dropped (${raw})` };
|
|
230
247
|
}
|
|
248
|
+
// json: a quoted literal default ('{}'::jsonb) carries as a string-literal
|
|
249
|
+
// default only if its content is valid JSON — the engine would reject the
|
|
250
|
+
// whole type DDL otherwise.
|
|
251
|
+
if (powType === "json") {
|
|
252
|
+
const m = /^'([\s\S]*)'$/.exec(noCast);
|
|
253
|
+
if (m) {
|
|
254
|
+
const text = m[1].replace(/''/g, "'");
|
|
255
|
+
try {
|
|
256
|
+
JSON.parse(text);
|
|
257
|
+
return { kind: "literal", text: `"${escapePowqlString(text)}"` };
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return { kind: "drop", note: `non-JSON default on json column dropped (${raw})` };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return { kind: "drop", note: `expression default dropped (${raw})` };
|
|
264
|
+
}
|
|
231
265
|
// datetime defaults are expressions (now(), CURRENT_TIMESTAMP) — never scalar.
|
|
232
266
|
return { kind: "drop", note: `expression default dropped (${raw})` };
|
|
233
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Extract the single plain column of a non-unique btree index definition, or
|
|
270
|
+
* null when the index can't be carried (multi-column, expression, partial,
|
|
271
|
+
* non-btree). Exported for unit testing.
|
|
272
|
+
*/
|
|
273
|
+
export function parseSingleColumnIndex(definition) {
|
|
274
|
+
if (/\bWHERE\b/i.test(definition))
|
|
275
|
+
return null; // partial
|
|
276
|
+
if (/\bINCLUDE\b/i.test(definition))
|
|
277
|
+
return null; // covering — the trailing parens are the INCLUDE list, not the key
|
|
278
|
+
if (/\bUSING\s+(?!btree\b)/i.test(definition))
|
|
279
|
+
return null; // gin/gist/hash/...
|
|
280
|
+
const m = /\(([^()]*)\)\s*$/.exec(definition);
|
|
281
|
+
if (!m)
|
|
282
|
+
return null; // expression index
|
|
283
|
+
const cols = m[1].split(",");
|
|
284
|
+
if (cols.length !== 1)
|
|
285
|
+
return null; // multi-column
|
|
286
|
+
const col = /^\s*(?:"([^"]+)"|([A-Za-z_][A-Za-z0-9_$]*))\s*(?:ASC|DESC)?\s*(?:NULLS\s+(?:FIRST|LAST))?\s*$/i.exec(cols[0]);
|
|
287
|
+
if (!col)
|
|
288
|
+
return null;
|
|
289
|
+
return col[1] ?? col[2];
|
|
290
|
+
}
|
|
234
291
|
/**
|
|
235
292
|
* Build the per-table PowQL `type` plan from a Postgres table's schema,
|
|
236
293
|
* accumulating an honesty note for every column/constraint that can't be
|
|
@@ -263,14 +320,30 @@ export function planTable(t) {
|
|
|
263
320
|
warnings.push(`foreign key ${c.name} → ${c.foreignTableName ?? "?"} dropped — PowDB Stage A has no foreign keys`);
|
|
264
321
|
}
|
|
265
322
|
else if (type.includes("CHECK")) {
|
|
323
|
+
// PG17+ surfaces NOT NULL as synthetic check constraints named
|
|
324
|
+
// <reloid>_<attnum>_..._not_null (all-numeric prefix). Those ARE carried
|
|
325
|
+
// (as `required`), so warning about them would be noise — but a
|
|
326
|
+
// USER-named check that merely ends in _not_null must still warn.
|
|
327
|
+
if (/^\d+(_\d+)*_not_null$/.test(c.name))
|
|
328
|
+
continue;
|
|
266
329
|
warnings.push(`check constraint ${c.name} dropped — PowDB Stage A has no check constraints`);
|
|
267
330
|
}
|
|
268
331
|
}
|
|
269
|
-
//
|
|
270
|
-
//
|
|
332
|
+
// Single-column plain btree indexes carry as `alter T add index .col`
|
|
333
|
+
// statements (resolved against carried columns after the column loop below);
|
|
334
|
+
// anything else (multi-column, expression, partial, non-btree) is warned.
|
|
335
|
+
const pendingIndexCols = [];
|
|
271
336
|
for (const idx of t.indexes ?? []) {
|
|
272
|
-
|
|
273
|
-
|
|
337
|
+
// Only a leading CREATE UNIQUE INDEX is a unique index — a bare substring
|
|
338
|
+
// match would silently skip an index whose NAME merely contains "unique".
|
|
339
|
+
if (/^\s*CREATE\s+UNIQUE\s+INDEX\b/i.test(idx.definition))
|
|
340
|
+
continue;
|
|
341
|
+
const pgCol = parseSingleColumnIndex(idx.definition);
|
|
342
|
+
if (pgCol) {
|
|
343
|
+
pendingIndexCols.push({ idxName: idx.name, pgCol });
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
warnings.push(`index ${idx.name} not carried — only single-column plain btree indexes map to PowDB \`add index\``);
|
|
274
347
|
}
|
|
275
348
|
}
|
|
276
349
|
const columns = [];
|
|
@@ -311,12 +384,27 @@ export function planTable(t) {
|
|
|
311
384
|
defaultLiteral: auto ? null : defaultLiteral,
|
|
312
385
|
});
|
|
313
386
|
}
|
|
387
|
+
// Resolve carried secondary indexes against the columns that survived the
|
|
388
|
+
// mapping; an index on a dropped column is warned, not silently lost.
|
|
389
|
+
const byPgName = new Map(columns.map((c) => [c.pgName, c]));
|
|
390
|
+
const secondaryIndexes = [];
|
|
391
|
+
for (const { idxName, pgCol } of pendingIndexCols) {
|
|
392
|
+
const col = byPgName.get(pgCol);
|
|
393
|
+
if (!col) {
|
|
394
|
+
warnings.push(`index ${idxName} not carried — its column ${pgCol} was not representable`);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (col.unique)
|
|
398
|
+
continue; // already an index via the unique modifier
|
|
399
|
+
secondaryIndexes.push(col.powName);
|
|
400
|
+
}
|
|
314
401
|
return {
|
|
315
402
|
pgQualified: `"${t.schema}"."${t.name}"`,
|
|
316
403
|
pgSchema: t.schema,
|
|
317
404
|
pgName: t.name,
|
|
318
405
|
powName: tPowName,
|
|
319
406
|
columns,
|
|
407
|
+
secondaryIndexes,
|
|
320
408
|
warnings,
|
|
321
409
|
};
|
|
322
410
|
}
|
|
@@ -339,6 +427,14 @@ export function renderTypeDdl(plan) {
|
|
|
339
427
|
lines.push("}");
|
|
340
428
|
return lines.join("\n");
|
|
341
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Render a plan's carried secondary indexes as `alter T add index .col;`
|
|
432
|
+
* statements (PowQL DDL, valid since 0.1.1; keyword columns backtick-quote
|
|
433
|
+
* inside the path, e.g. `alter Post add index .\`order\``).
|
|
434
|
+
*/
|
|
435
|
+
export function renderIndexDdl(plan) {
|
|
436
|
+
return plan.secondaryIndexes.map((powName) => `alter ${renderPowqlIdent(plan.powName)} add index .${renderPowqlIdent(powName)};`);
|
|
437
|
+
}
|
|
342
438
|
/**
|
|
343
439
|
* Render rows for one table as PowQL `insert` statements against its plan.
|
|
344
440
|
* Values that can't be represented are omitted (the field falls back to
|
|
@@ -498,6 +594,7 @@ async function powdbPull(args) {
|
|
|
498
594
|
const reports = [];
|
|
499
595
|
let totalRows = 0;
|
|
500
596
|
let anyLoadRisk = false;
|
|
597
|
+
let anyJson = false;
|
|
501
598
|
for (const t of tables) {
|
|
502
599
|
s?.update(`Exporting ${t.schema}.${t.name}`);
|
|
503
600
|
const plan = planTable(t);
|
|
@@ -521,6 +618,8 @@ async function powdbPull(args) {
|
|
|
521
618
|
}
|
|
522
619
|
if (inserts.loadRisk)
|
|
523
620
|
anyLoadRisk = true;
|
|
621
|
+
if (plan.columns.some((c) => c.powType === "json"))
|
|
622
|
+
anyJson = true;
|
|
524
623
|
// Emit the type DDL, its honesty notes, and the inserts for this table.
|
|
525
624
|
// Comments are `;`/newline-sanitized so they survive the `--exec` split.
|
|
526
625
|
parts.push(commentSafe(`# ── ${t.schema}.${t.name} → ${plan.powName} (${inserts.statements.length} row(s)) ──`));
|
|
@@ -532,6 +631,9 @@ async function powdbPull(args) {
|
|
|
532
631
|
}
|
|
533
632
|
else {
|
|
534
633
|
parts.push(ddl + ";");
|
|
634
|
+
const indexDdl = renderIndexDdl(plan);
|
|
635
|
+
if (indexDdl.length)
|
|
636
|
+
parts.push(indexDdl.join("\n"));
|
|
535
637
|
parts.push("");
|
|
536
638
|
if (inserts.statements.length) {
|
|
537
639
|
parts.push(inserts.statements.join("\n"));
|
|
@@ -556,6 +658,7 @@ async function powdbPull(args) {
|
|
|
556
658
|
tableCount: tables.length,
|
|
557
659
|
rowCount: totalRows,
|
|
558
660
|
loadRisk: anyLoadRisk,
|
|
661
|
+
usesJson: anyJson,
|
|
559
662
|
limit,
|
|
560
663
|
});
|
|
561
664
|
const artifact = header + "\n" + parts.join("\n") + "\n";
|
|
@@ -628,7 +731,9 @@ function buildArtifactHeader(o) {
|
|
|
628
731
|
// which lexes as two minus signs and fails to parse).
|
|
629
732
|
const lines = [
|
|
630
733
|
"# PowDB Stage A export — generated by `bata powdb pull`",
|
|
631
|
-
|
|
734
|
+
o.usesJson
|
|
735
|
+
? "# Target: PowDB 0.12+ (native json columns; reserved identifiers are backtick-quoted)"
|
|
736
|
+
: "# Target: PowDB 0.10+ (reserved identifiers are backtick-quoted)",
|
|
632
737
|
"#",
|
|
633
738
|
`# Source project : ${o.projectName} (${o.projectId})`,
|
|
634
739
|
`# Source branch : ${o.branchName} (${o.branchId})`,
|
|
@@ -641,7 +746,8 @@ function buildArtifactHeader(o) {
|
|
|
641
746
|
"#",
|
|
642
747
|
"# COMPAT-HONEST: `# WARN:` lines below name every Postgres feature that",
|
|
643
748
|
"# PowDB Stage A cannot carry faithfully (foreign keys, check constraints,",
|
|
644
|
-
"#
|
|
749
|
+
"# multi-column/expression/partial indexes, unmapped types, expression",
|
|
750
|
+
"# defaults, lossy numerics).",
|
|
645
751
|
"# PowDB is NOT Postgres-compatible beyond this mapping; nothing here claims",
|
|
646
752
|
"# otherwise, and no engine-speed comparison is implied.",
|
|
647
753
|
];
|
|
@@ -860,7 +966,57 @@ async function powdbLifecycle(action, args) {
|
|
|
860
966
|
return;
|
|
861
967
|
}
|
|
862
968
|
log();
|
|
863
|
-
|
|
969
|
+
const ver = res.data.server_version;
|
|
970
|
+
log(` ${colors.green(">")} ${colors.cyan(projectId)} ${colors.dim("engine=powdb state=")}${res.data.state}${ver ? colors.dim(" server=") + ver : ""}`);
|
|
971
|
+
log();
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* `bata powdb upgrade --to <version>` — pin the project's server to a new
|
|
975
|
+
* engine version. Server-side this is park→backup→flip→wake→verify with
|
|
976
|
+
* automatic rollback to the pre-upgrade backup on failure. Storage-format
|
|
977
|
+
* releases (0.11+) make a verified upgrade a ONE-WAY door once new-format
|
|
978
|
+
* data is written, so this always confirms unless --yes/--json.
|
|
979
|
+
*/
|
|
980
|
+
async function powdbUpgrade(args) {
|
|
981
|
+
const token = requireToken();
|
|
982
|
+
const jsonMode = isJsonMode();
|
|
983
|
+
let toVersion;
|
|
984
|
+
const rest = [];
|
|
985
|
+
for (let i = 0; i < args.length; i++) {
|
|
986
|
+
const a = args[i];
|
|
987
|
+
if (a === "--to")
|
|
988
|
+
toVersion = args[++i];
|
|
989
|
+
else if (a.startsWith("--to="))
|
|
990
|
+
toVersion = a.slice("--to=".length);
|
|
991
|
+
else
|
|
992
|
+
rest.push(a);
|
|
993
|
+
}
|
|
994
|
+
const positional = rest.find((a) => !a.startsWith("-"));
|
|
995
|
+
const projectId = powdbProjectFlag(rest) ?? positional ?? resolvePowdbProject(rest);
|
|
996
|
+
if (!toVersion || !/^\d+\.\d+\.\d+$/.test(toVersion)) {
|
|
997
|
+
emitError("MISSING_ARG", "A target version is required.", "Usage: bata powdb upgrade --to <x.y.z> [--project <id>]");
|
|
998
|
+
}
|
|
999
|
+
const ok = await confirmDestructive(`Upgrade ${colors.cyan(projectId)} to powdb-server ${colors.cyan(toVersion)}? A format-changing release cannot be downgraded once new data is written.`);
|
|
1000
|
+
if (!ok) {
|
|
1001
|
+
log(" Aborted.");
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
const s = jsonMode ? null : spinner(`Upgrading ${projectId} to ${toVersion} (park -> backup -> flip -> wake -> verify)`);
|
|
1005
|
+
const res = await api.post(`/v1/powdb/${encodeURIComponent(projectId)}/upgrade`, { to_version: toVersion }, token);
|
|
1006
|
+
s?.stop();
|
|
1007
|
+
if (!res.ok) {
|
|
1008
|
+
emitError(res.status === 404 ? "NOT_FOUND"
|
|
1009
|
+
: res.status === 401 || res.status === 403 ? "INVALID_KEY"
|
|
1010
|
+
: res.status >= 500 || res.status === 0 ? "API_UNAVAILABLE"
|
|
1011
|
+
: "CLI_ERROR", apiError(res, "PowDB upgrade failed (instance rolled back to its pre-upgrade state)"), "");
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
if (jsonMode) {
|
|
1015
|
+
json({ project_id: projectId, ...res.data });
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
log();
|
|
1019
|
+
log(` ${colors.green(">")} ${colors.cyan(projectId)} upgraded to powdb-server ${colors.cyan(res.data.server_version)} ${colors.dim("(verified)")}`);
|
|
864
1020
|
log();
|
|
865
1021
|
}
|
|
866
1022
|
// ─── Help + dispatch ──────────────────────────────────────────────────────────
|
|
@@ -883,6 +1039,8 @@ function powdbHelp() {
|
|
|
883
1039
|
log(` ${colors.cyan("status")} Show the server state (running | parked) [--project <id>]`);
|
|
884
1040
|
log(` ${colors.cyan("park")} Scale the server to zero (WAL is durable) [--project <id>]`);
|
|
885
1041
|
log(` ${colors.cyan("wake")} Start a parked server (~tens of ms) [--project <id>]`);
|
|
1042
|
+
log(` ${colors.cyan("upgrade")} Pin the server to a new engine version --to <x.y.z> [--project <id>]`);
|
|
1043
|
+
log(` ${colors.dim(" park -> backup -> flip -> wake -> verify, auto-rollback on failure")}`);
|
|
886
1044
|
log();
|
|
887
1045
|
log(` ${colors.dim("Options (pull):")}`);
|
|
888
1046
|
log(` ${colors.dim("--project <id> override the linked/default project")}`);
|
|
@@ -918,10 +1076,12 @@ export async function handlePowdb(args) {
|
|
|
918
1076
|
return powdbLifecycle("park", args.slice(1));
|
|
919
1077
|
case "wake":
|
|
920
1078
|
return powdbLifecycle("wake", args.slice(1));
|
|
1079
|
+
case "upgrade":
|
|
1080
|
+
return powdbUpgrade(args.slice(1));
|
|
921
1081
|
case undefined:
|
|
922
1082
|
powdbHelp();
|
|
923
1083
|
return;
|
|
924
1084
|
default:
|
|
925
|
-
emitError("INVALID_FLAG", `Unknown subcommand: powdb ${sub}`, "Available: pull, query, exec, status, park, wake. Run `bata powdb --help`.");
|
|
1085
|
+
emitError("INVALID_FLAG", `Unknown subcommand: powdb ${sub}`, "Available: pull, query, exec, status, park, wake, upgrade. Run `bata powdb --help`.");
|
|
926
1086
|
}
|
|
927
1087
|
}
|