@batadata/cli 0.2.5 → 0.2.6
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.js +94 -4
- package/package.json +1 -1
package/dist/commands/powdb.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFileSync } from "node:fs";
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { api, apiError } from "../api.js";
|
|
4
4
|
import { requireToken, isJsonMode, loadConfig } from "../config.js";
|
|
@@ -683,12 +683,12 @@ async function powdbQuery(args) {
|
|
|
683
683
|
const projectId = resolvePowdbProject(args);
|
|
684
684
|
const res = await api.post(`/v1/powdb/${encodeURIComponent(projectId)}/query`, { query }, token);
|
|
685
685
|
if (!res.ok) {
|
|
686
|
-
const code = res.status === 503 ? "
|
|
686
|
+
const code = res.status === 503 ? "COMPUTE_STARTING"
|
|
687
687
|
: res.status === 401 || res.status === 403 ? "INVALID_KEY"
|
|
688
688
|
: res.status === 404 ? "NOT_FOUND"
|
|
689
689
|
: res.status >= 500 || res.status === 0 ? "API_UNAVAILABLE"
|
|
690
690
|
: "CLI_ERROR";
|
|
691
|
-
emitError(code, apiError(res, "PowQL query failed"), code === "
|
|
691
|
+
emitError(code, apiError(res, "PowQL query failed"), code === "COMPUTE_STARTING" ? "The database is starting — retry in a few seconds." : "");
|
|
692
692
|
return;
|
|
693
693
|
}
|
|
694
694
|
const data = res.data;
|
|
@@ -729,6 +729,91 @@ async function powdbQuery(args) {
|
|
|
729
729
|
}
|
|
730
730
|
log();
|
|
731
731
|
}
|
|
732
|
+
function truncateStatement(statement) {
|
|
733
|
+
const flat = statement.replace(/\s+/g, " ").trim();
|
|
734
|
+
return flat.length > 60 ? `${flat.slice(0, 57)}...` : flat;
|
|
735
|
+
}
|
|
736
|
+
async function powdbExec(args) {
|
|
737
|
+
const token = requireToken();
|
|
738
|
+
const jsonMode = isJsonMode();
|
|
739
|
+
const transactional = args.includes("--transactional");
|
|
740
|
+
const continueOnError = args.includes("--continue-on-error");
|
|
741
|
+
if (transactional && continueOnError) {
|
|
742
|
+
emitError("INVALID_ARG", "--transactional and --continue-on-error are mutually exclusive.", "Transactional scripts stop (and roll back) on the first failure by definition.");
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
let filePath;
|
|
746
|
+
for (let i = 0; i < args.length; i++) {
|
|
747
|
+
if (args[i] === "--file" && args[i + 1])
|
|
748
|
+
filePath = args[i + 1];
|
|
749
|
+
if (args[i].startsWith("--file="))
|
|
750
|
+
filePath = args[i].slice("--file=".length);
|
|
751
|
+
}
|
|
752
|
+
let script;
|
|
753
|
+
if (filePath) {
|
|
754
|
+
try {
|
|
755
|
+
// `--file -` reads stdin (fd 0) — the pipe-friendly agent path.
|
|
756
|
+
script = readFileSync(filePath === "-" ? 0 : filePath, "utf8");
|
|
757
|
+
}
|
|
758
|
+
catch (err) {
|
|
759
|
+
emitError("FILE_ERROR", `Could not read ${filePath}: ${err instanceof Error ? err.message : String(err)}`, "");
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
script = args.find((a) => !a.startsWith("-") && a !== powdbProjectFlag(args) && a !== filePath);
|
|
765
|
+
}
|
|
766
|
+
if (!script || script.trim().length === 0) {
|
|
767
|
+
emitError("MISSING_ARG", 'Usage: bata powdb exec "<PowQL script>" | --file <path|-> [--transactional | --continue-on-error] [--project <id>]', "Statements are ;-separated (string/#-comment aware) and run pipelined on one connection.");
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const projectId = resolvePowdbProject(args);
|
|
771
|
+
const res = await api.post(`/v1/powdb/${encodeURIComponent(projectId)}/exec`, {
|
|
772
|
+
script,
|
|
773
|
+
...(transactional ? { transactional: true } : {}),
|
|
774
|
+
...(continueOnError ? { continue_on_error: true } : {}),
|
|
775
|
+
}, token);
|
|
776
|
+
if (!res.ok) {
|
|
777
|
+
const code = res.status === 503 ? "COMPUTE_STARTING"
|
|
778
|
+
: res.status === 401 || res.status === 403 ? "INVALID_KEY"
|
|
779
|
+
: res.status === 404 ? "NOT_FOUND"
|
|
780
|
+
: res.status >= 500 || res.status === 0 ? "API_UNAVAILABLE"
|
|
781
|
+
: "CLI_ERROR";
|
|
782
|
+
emitError(code, apiError(res, "PowQL script failed"), code === "COMPUTE_STARTING" ? "The database is starting — retry in a few seconds." : "");
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
const data = res.data;
|
|
786
|
+
if (jsonMode) {
|
|
787
|
+
json(data);
|
|
788
|
+
if (("error" in data && data.error) || ("failed_count" in data && data.failed_count > 0)) {
|
|
789
|
+
process.exitCode = 1;
|
|
790
|
+
}
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if ("error" in data && data.error) {
|
|
794
|
+
const where = data.statement_index !== undefined
|
|
795
|
+
? ` (statement ${data.statement_index + 1}${data.rolled_back ? ", rolled back — nothing persisted" : ""})`
|
|
796
|
+
: "";
|
|
797
|
+
emitError("QUERY_ERROR", `${data.error}${where}`, data.statement ?? "");
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
log();
|
|
801
|
+
if ("outcomes" in data) {
|
|
802
|
+
for (const o of data.outcomes) {
|
|
803
|
+
if (o.ok)
|
|
804
|
+
log(` ${colors.green(">")} ok ${colors.dim(truncateStatement(o.statement))}`);
|
|
805
|
+
else
|
|
806
|
+
log(` ${colors.red("x")} ${o.error} ${colors.dim(truncateStatement(o.statement))}`);
|
|
807
|
+
}
|
|
808
|
+
log(` ${colors.dim(`${data.statement_count} statement${data.statement_count === 1 ? "" : "s"}, ${data.failed_count} failed, ${data.duration_ms}ms`)}`);
|
|
809
|
+
if (data.failed_count > 0)
|
|
810
|
+
process.exitCode = 1;
|
|
811
|
+
}
|
|
812
|
+
else if ("results" in data) {
|
|
813
|
+
log(` ${colors.green(">")} ${data.statement_count} statement${data.statement_count === 1 ? "" : "s"} executed${transactional ? " (transactional)" : ""} ${colors.dim(`${data.duration_ms}ms`)}`);
|
|
814
|
+
}
|
|
815
|
+
log();
|
|
816
|
+
}
|
|
732
817
|
async function powdbLifecycle(action, args) {
|
|
733
818
|
const token = requireToken();
|
|
734
819
|
const jsonMode = isJsonMode();
|
|
@@ -766,6 +851,9 @@ function powdbHelp() {
|
|
|
766
851
|
log();
|
|
767
852
|
log(` ${colors.dim("Hosted PowDB projects (create one with: bata projects create --engine powdb):")}`);
|
|
768
853
|
log(` ${colors.cyan("query")} Run a PowQL statement: bata powdb query "Note" [--project <id>]`);
|
|
854
|
+
log(` ${colors.cyan("exec")} Run a multi-statement script, pipelined (bulk load / seed / migrate):`);
|
|
855
|
+
log(` ${colors.dim(' bata powdb exec --file seed.powql [--transactional | --continue-on-error]')}`);
|
|
856
|
+
log(` ${colors.dim(' bata powdb pull --out - | bata powdb exec --file - (pipe-friendly)')}`);
|
|
769
857
|
log(` ${colors.cyan("status")} Show the server state (running | parked) [--project <id>]`);
|
|
770
858
|
log(` ${colors.cyan("park")} Scale the server to zero (WAL is durable) [--project <id>]`);
|
|
771
859
|
log(` ${colors.cyan("wake")} Start a parked server (~tens of ms) [--project <id>]`);
|
|
@@ -796,6 +884,8 @@ export async function handlePowdb(args) {
|
|
|
796
884
|
return powdbPull(args.slice(1));
|
|
797
885
|
case "query":
|
|
798
886
|
return powdbQuery(args.slice(1));
|
|
887
|
+
case "exec":
|
|
888
|
+
return powdbExec(args.slice(1));
|
|
799
889
|
case "status":
|
|
800
890
|
return powdbLifecycle("status", args.slice(1));
|
|
801
891
|
case "park":
|
|
@@ -806,6 +896,6 @@ export async function handlePowdb(args) {
|
|
|
806
896
|
powdbHelp();
|
|
807
897
|
return;
|
|
808
898
|
default:
|
|
809
|
-
emitError("INVALID_FLAG", `Unknown subcommand: powdb ${sub}`, "Available: pull, query, status, park, wake. Run `bata powdb --help`.");
|
|
899
|
+
emitError("INVALID_FLAG", `Unknown subcommand: powdb ${sub}`, "Available: pull, query, exec, status, park, wake. Run `bata powdb --help`.");
|
|
810
900
|
}
|
|
811
901
|
}
|