Skip to content

SQL commands

wabesql query app < /migrations/001.sql executes a virtual file as a SQL script and streams its results. app selects a named SQLite database in the current disk. Create it with wabesql database create app before the first query.

The command works in the HTTP shell, the embedded CLI and wabe Disk JavaScript through bash(). Database names are independent of file paths and filesystem branches. HTTP SQL and wabesql access the same databases.

wabesql database list
wabesql database create app
wabesql --help
wabesql query --help

database create creates the database when it is missing. Repeating the command succeeds and returns {"name":"app"}. database list emits one JSON object with a name field per SQLite database. It reads stored names without opening the databases. An empty list produces no output.

query requires an existing database. A missing database returns an error with a creation hint. HTTP SQL connections create missing databases automatically. Names contain 1–128 ASCII letters, digits, _ or -.

wabesql query app 'SELECT id, body FROM notes;'
wabesql query app < /migrations/001.sql

Supply SQL as one argument or read a UTF-8 script from stdin through EOF. Stdin accepts up to 4 MiB; an inline argument also counts toward the shell’s 64 KiB source limit. Use a virtual file for larger scripts. SQLite parses strings, comments and trigger bodies. Built-in FTS5 is supported. Interactive prompts and sqlite3 dot commands are unsupported.

This HTTP example creates a database and runs a migration stored in a virtual file:

Run wabesql through HTTP
curl --fail-with-body --no-buffer \
"https://api.wabe.sh/v1/disks/demo/exec?kind=bash" \
-H "Authorization: Bearer YOUR_PROJECT_API_KEY" \
--data-binary @- <<'WABE'
wabesql database create shell_example
mkdir -p /migrations
put /migrations/001.sql <<'SQL'
BEGIN;
CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY, body TEXT);
INSERT INTO notes(body) VALUES('Hello from SQL');
COMMIT;
SELECT id, body FROM notes ORDER BY id;
SQL
wabesql query shell_example < /migrations/001.sql
wabesql database list
WABE

Inside wabe Disk JavaScript, the same command is available through bash():

Run wabesql inside JavaScript
for await (const event of bash("wabesql database create js_sql_example; wabesql query js_sql_example 'SELECT 1;'")) {
if (event.channel === 'stdout') io.stdout.write(event.bytes);
if (event.channel === 'stderr') io.stderr.write(event.bytes);
if (event.exit !== undefined && event.exit !== 0) throw Error(`SQL shell exited ${event.exit}`);
}

Query stdout contains one JSON event per line, a format called NDJSON. SELECT 7 AS id, 'Hello' AS body; produces:

{"type":"statement","statement":0,"columns":["id","body"]}
{"type":"row","statement":0,"values":[{"type":"integer","value":"7"},"Hello"]}
{"type":"result","statement":0,"rows":1,"changes":"0"}
{"type":"done"}

Statement indexes start at zero. Each statement event names the columns; its row events contain values in the same order. This preserves duplicate column names. Integers use signed decimal strings inside integer objects, BLOBs use base64 objects, and SQL NULL becomes JSON null. Text and finite real values use JSON strings and numbers. The changes field is a decimal string and includes trigger writes.

HTTP exec wraps stdout chunks as base64 stdout records and ends with an exit record. Streaming output describes this outer envelope. Virtual pipes and redirects receive the command’s raw NDJSON stdout.

Statements execute in order with ordinary SQLite autocommit. Explicit BEGIN, COMMIT, ROLLBACK and SAVEPOINT statements work within one invocation. A transaction cannot continue across invocations. An explicit transaction still open at the end rolls back when the connection closes.

The first SQL error stops later statements. Earlier autocommitted changes remain; a pending explicit transaction rolls back. When stdout remains writable, the command emits an error event, writes a diagnostic to stderr and returns status 1. Read through the final event and check the exit status: rows can arrive before a later statement fails.

Status 0 and done confirm that execution and connection cleanup finished. A transaction left open still rolls back. To save a group of changes, include COMMIT in the script.

SQL acknowledgements confirm buffered state. The default background WAL flush interval is 10 seconds; storage failures can delay persistence. Use explicit disk flush to wait for preceding writes to reach storage. Pipe closure, shell timeout and cancellation retain the disk lease until SQL cleanup finishes. An interrupted response does not establish whether a COMMIT completed.

Shell and HTTP SQL requests share a concurrency limit. The default is 16 active requests, with a 30-second SQL deadline that includes admission and result backpressure. The script limit is 128 statements, 100,000 returned rows and 8 MiB of encoded results. Individual values are limited to 1 MiB. See runtime limits for the surrounding shell and JavaScript limits.

The result limit includes the final done or error event. Scripts reserve up to 512 bytes for that event; diagnostic messages can be shortened to fit. An embedded configuration uses half its result budget when the budget is below 1 KiB and rejects budgets below 256 bytes before execution. Stderr repeats the bounded error message separately.