const std = @import("std"); const cobalt = @import("root.zig"); const Value = cobalt.row.Value; const Field = cobalt.row.Field; const usage = \\cobalt - LSM key/value store \\ \\usage: cobalt [args] \\ \\commands: \\ put [--ttl ] \\ store a value, optionally set to expire \\ get print a value or (nil); a key past its ttl \\ reads as (nil) and is deleted lazily on this read \\ del delete a key \\ expire set/replace a key's ttl; error if the key does \\ not currently exist (nonexistent keys have no ttl to set) \\ persist remove a key's ttl, making it immortal again \\ ttl print seconds left, -1 if no ttl, -2 if no such key \\ expire-sweep actively delete every key whose ttl is due, \\ not just the next one someone happens to read; \\ prints how many were reaped \\ scan [start] [end] forward range scan, start inclusive, end exclusive \\ rscan [start] [end] reverse range scan \\ prefix scan every key under a prefix \\ compact run full leveled compaction \\ stats [--prom] print database statistics (optionally as Prometheus text) \\ verify write n keys, reopen, check durability and scan order \\ serve [host:port] [--rate-limit ] \\ [--per-conn-rate-limit ] [--auth] \\ [--session-ttl ] [--session-sweep-interval ] \\ [--ttl-sweep-interval ] [--coll-ttl-sweep-interval ] \\ run a TCP server (default 127.0.0.1:6380), \\ optionally capping requests with a shared \\ token bucket and/or a private one per \\ connection (both may be given together); \\ --auth requires an AUTH before any op but \\ ping, refuses to start if no admin user \\ exists yet (see `user add`); --session-ttl \\ (requires --auth) makes AUTH return a \\ token valid for that many seconds - a new \\ connection can present it via AUTH_TOKEN \\ instead of the password, and the role it \\ grants stops working once the ttl elapses \\ even on a connection that never disconnects; \\ --session-sweep-interval (requires \\ --session-ttl) periodically drops sessions \\ that expired but nobody ever presented again; \\ --ttl-sweep-interval periodically reaps every \\ key whose ttl is due, independent of --auth \\ (like local `expire-sweep`, but automatic); \\ --coll-ttl-sweep-interval does the same for \\ whole collections (like local \\ `coll expire-sweep`), independent of \\ --ttl-sweep-interval - either, both, or \\ neither may be given \\ \\cobalt remote-select [expr...] [--limit N] [--offset N] \\ [--order-by field[:desc][,field[:desc]...]] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table select` there (same expr grammar as \\ local `table select`; empty expr selects \\ every row) - no local Db is opened; \\ --user/--pass authenticate first, needed \\ only if the server was started with --auth; \\ --token authenticates with a previously \\ issued session token instead of a password \\ (requires the server to have --session-ttl) \\ \\cobalt remote-explain
[expr...] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table explain` there (same expr grammar as \\ local `table select`) - no local Db is opened; \\ --user/--pass or --token authenticate first, \\ same as `remote-select` \\ \\cobalt remote-update
--set field=value[,field=value...] [expr...] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table update` there (same --set grammar and \\ expr grammar as local `table update`; empty \\ expr updates every row) - no local Db is \\ opened; prints the number of rows updated; \\ --user/--pass or --token authenticate first, \\ same as `remote-select` \\ \\cobalt remote-delete
[expr...] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table delete` there (same expr grammar as \\ local `table delete`; empty expr deletes \\ every row) - no local Db is opened; prints \\ the number of rows deleted; --user/--pass or \\ --token authenticate first, same as \\ `remote-select` \\ \\cobalt remote-export
csv|json \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table export` there (same output shape as \\ local `table export`), writing the result to \\ on THIS machine - no local Db is \\ opened; prints the row count written; \\ --user/--pass or --token authenticate first, \\ same as `remote-select` \\ \\cobalt remote-import
csv|json \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table import` there, reading on THIS \\ machine and sending its bytes in one frame - \\ no local Db is opened; prints the row count \\ imported; --user/--pass or --token \\ authenticate first, same as `remote-select` \\ \\cobalt remote-join [--left|--right|--full] \\ [--left-expr E] [--right-expr E] \\ [--limit N] [--offset N] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table join` there (same 4 modes as local \\ `table join`; --left-expr/--right-expr are \\ the same expr grammar as `table select`, \\ filtering each side before the join runs; \\ --limit/--offset window the already-joined \\ result, same convention as `table select`) - \\ no local Db is opened; --user/--pass or \\ --token authenticate first, same as \\ `remote-select` \\ \\cobalt remote-aggregate
[,...] [expr...] \\ [having ] [order [,field...]] \\ [--limit N] [--offset N] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table aggregate` there (same spec grammar \\ as local `table aggregate`: count | \\ count:field | sum:field | avg:field | \\ min:field | max:field | distinct:field, \\ comma-separated; \\ `*` as groupField means no GROUP BY, always \\ at most one result row and --limit/--offset \\ are ignored; expr filters rows before \\ aggregating, same grammar as `table \\ select`; an optional `having ` after \\ expr filters the result groups after \\ aggregation, same as local `table \\ aggregate`; an optional `order \\ [,field...]` after having \\ sorts the result groups server-side before \\ --limit/--offset window them, same as \\ local `table aggregate`; --limit/--offset \\ window the result groups, same convention \\ as `table select`) - no local Db is opened; \\ --user/--pass or --token authenticate \\ first, same as `remote-select` \\ \\cobalt remote-window
[,...] [expr...] \\ [partition [,field...]] \\ [order [,field...]] \\ [--limit N] [--offset N] [--range] \\ [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and run a \\ `table window` there (same spec-list \\ grammar as local `table window`: row_number | \\ rank | dense_rank | count | count:field | \\ count_distinct:field | sum:field | avg:field | \\ min:field | max:field | first_value:field | \\ last_value:field | nth_value:field:n[:default] | \\ lag:field[:offset[:default]] | \\ lead:field[:offset[:default]], \\ comma-separated for multiple \\ annotation columns sharing one partition/ \\ order/frame; lag/lead read the field's value \\ `offset` rows before/after the current row \\ within the partition's order sequence \\ (default offset 1, default value SQL NULL \\ unless an `i:`/`f:`/`b:`/`s:`-prefixed \\ `default` literal is given); first_value/ \\ last_value/nth_value read the current \\ frame's first/last/n-th row (n is 1-based, \\ required, no SQL default) - UNLIKE lag/lead, \\ these respect --range - see local `table \\ window`'s help for the full frame-end \\ semantics; expr \\ filters rows before annotation, same \\ grammar as `table select`; \\ an optional `partition [,field...]` \\ after expr splits rows into independent \\ groups, same as local `table window`; an \\ optional `order [,field...]` \\ after partition orders rows within each \\ partition for ranking/running-aggregate \\ purposes; every matching row comes back \\ annotated (never collapsed, unlike `table \\ aggregate`); --limit/--offset window WHICH \\ rows are returned, walked in the same \\ partition-then-order sequence used to \\ compute them, not raw scan order; --range \\ switches sum/avg/count/min/max to a RANGE \\ frame (rows tied on `order` share the tie \\ group's combined value instead of each \\ accumulating its own, SQL's actual default; \\ rejected together with row_number/rank/ \\ dense_rank, same as local `table window`) \\ - no local Db is opened; --user/--pass or \\ --token authenticate first, same as \\ `remote-select` \\ \\cobalt remote-publish [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and \\ deliver (joined from every \\ trailing word) to every connection \\ currently subscribed to ; prints \\ how many subscribers it reached (0 is a \\ normal outcome for a channel nobody is \\ listening to, not an error) - no local Db \\ is opened \\ \\cobalt remote-subscribe [,channel...] [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and \\ block, printing "subscribed as " on the \\ first line, then ": " for \\ every message published to any of the \\ listed channels, until the connection \\ closes (Ctrl+C, or the server shutting \\ down) - the connection becomes receive-only \\ once subscribed: to drop some of its \\ channels without closing it, run \\ `remote-unsubscribe ...` from a \\ different terminal \\ \\cobalt remote-unsubscribe [,channel...] [--user NAME --pass PASS] [--token TOKEN] \\ connect to a running `cobalt serve` and \\ drop the listed channels from \\ (printed by \\ `remote-subscribe` when it started); prints \\ how many channels were actually removed \\ (naming one the subscriber was never on is \\ harmless and does not count, not an \\ error); an unknown or already-disconnected \\ is reported as an error, \\ not silently ignored; dropping every \\ channel the subscriber still has ends that \\ subscription outright, same as closing its \\ connection - no local Db is opened \\ \\cobalt remote-user add|passwd|role|del|list [args] [--user NAME --pass PASS] [--token TOKEN] \\ same operations as `user`, but against a \\ running `cobalt serve` over the network \\ instead of a local Db file - requires admin \\ role if the server was started with --auth \\ (--user/--pass or --token authenticate first) \\ \\cobalt remote-ttl expire [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-ttl persist [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-ttl ttl [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-ttl sweep [--user NAME --pass PASS] [--token TOKEN] \\ same four operations as local `expire`/ \\ `persist`/`ttl`/`expire-sweep`, but against a \\ running `cobalt serve` over the network - \\ `ttl` needs only readonly role, the other \\ three need readwrite, if the server was \\ started with --auth \\ \\cobalt remote-coll-ttl expire [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll-ttl persist [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll-ttl ttl [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll-ttl sweep [--user NAME --pass PASS] [--token TOKEN] \\ same operations as local `coll \\ expire|ttl|persist`/`coll expire-sweep`, but \\ against a running `cobalt serve` over the \\ network - `ttl` needs only readonly role, \\ the other three need readwrite, if the \\ server was started with --auth \\ \\cobalt remote-coll counter get|set|incrby [value|delta] [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll hash set|get|del [value] [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll hash len|getall [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll set add|rem|ismember [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll set card|members [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll set inter|union [name...] [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll set diff [other...] [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll list lpush|rpush [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll list lpop|rpop|len [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll list range [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll zset add [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll zset score|rank [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll zset incrby [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll zset rem [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll zset card [--user NAME --pass PASS] [--token TOKEN] \\cobalt remote-coll zset rangebyrank|rangebyscore [--user NAME --pass PASS] [--token TOKEN] \\ same operations as local `coll `, \\ against a running `cobalt serve` over the \\ network - read-only ops (get/len/getall/ \\ ismember/card/members/inter/union/diff/ \\ range/score/rank/rangebyrank/rangebyscore) \\ need only readonly role, every other op \\ needs readwrite, if the server was started \\ with --auth \\ \\cobalt remote-txn [--user NAME --pass PASS] [--token TOKEN] \\ same script format as local `txn`, but \\ committed in one round trip against a \\ running `cobalt serve` over the network - \\ needs readwrite role if the server was \\ started with --auth \\ \\cobalt remote-stats [--user NAME --pass PASS] [--token TOKEN] \\ print a running `cobalt serve` instance's own \\ request/rate-limit/auth/session counters \\ together with its Db stats, as Prometheus \\ text - the network counterpart of local \\ `stats --prom`, since a server's own counters \\ (distinct from its Db's) were otherwise only \\ ever observable in-process \\ \\ user add readonly|readwrite|admin \\ user passwd \\ user role readonly|readwrite|admin \\ user del \\ user list pre-requisite for `serve --auth`: create at \\ least one admin user first \\ backup write a consistent point-in-time backup to \\ restore replay a backup file into this database \\ backup-info validate a backup file and print its metadata \\ \\ coll counter get|set|incrby [value|delta] \\ coll hash set|get|del [value] \\ coll hash len|getall \\ coll set add|rem|ismember \\ coll set card|members \\ coll set inter|union [name...] \\ coll set diff [other...] \\ coll list lpush|rpush \\ coll list lpop|rpop|len \\ coll list range \\ coll zset add \\ coll zset score|rank \\ coll zset incrby \\ coll zset rem \\ coll zset card \\ coll zset rangebyrank|rangebyscore \\ coll expire whole collection expires as one unit \\ coll ttl seconds left, -1 no expiry, -2 no such collection \\ coll persist clears expiry, collection immortal again \\ coll expire-sweep reaps every due collection now, across all kinds \\ \\ table put
[field=value ...] \\ table get
\\ table del
\\ table scan
\\ table select
[expr...] [--limit N] [--offset N] \\ [--order-by field[:desc][,field[:desc]...]] \\ expr is a predicate: eq|ne|lt|lte|gt|gte|like , composed \\ with "and"/"or"/"not" and parentheses, e.g. \\ city eq s:oslo and not (age lt i:18) \\ like matches SQL-style wildcards against a text field: "%" is any run \\ of characters, "_" is exactly one, e.g. name like s:A%; it never uses \\ an index even when the field is indexed (always a value scan) \\ in and between are sugar over the operators above, so they use an \\ index exactly where the desugared form would: \\ in ( ...) is eq v1 or eq v2 or ... \\ (a single value collapses to a bare eq, so it can use an index the \\ same way eq does; two or more never do, same as any other "or") \\ between and is gte and lte \\ (inclusive both ends), e.g. age between i:18 and i:65, \\ city in (s:oslo s:bergen s:trondheim) \\ is_null / is_not_null take no trailing value; is_null \\ matches a row where the field was never set OR set to an explicit \\ none, e.g. city is_null and age gte i:18 (never uses an index, a \\ sparse row's absence is not something a B-tree entry can represent) \\ (empty expr selects every row; --limit/--offset page the result, \\ limit 0 or omitted means unlimited; --order-by sorts by one or more \\ comma-separated fields, applied left to right (later fields only \\ break ties left by earlier ones), ascending by default or ":desc" \\ per field for descending - rows missing a sort field sort first \\ on it) \\ table explain
[expr...] \\ reports which strategy `table select` would use for the same \\
/expr WITHOUT running the query: full scan, a single-field \\ index (equality or range), or a composite index (equality or range, \\ naming how many of its leading columns the expr actually pinned down) \\ table update
--set field=value[,field=value...] [expr...] \\ applies the --set field list to every row matched by expr (empty expr \\ updates every row); each set field overrides an existing field of the \\ same name or is added if the row does not have it; unmentioned fields \\ are left as-is; secondary/composite indexes on a changed field are \\ kept in sync exactly like `table put` would; prints the number of \\ rows updated \\ table delete
[expr...] \\ removes every row matched by expr (empty expr deletes every row, same \\ convention as `table select`/`table update`); secondary/composite \\ indexes on the deleted rows are kept in sync exactly like `table del` \\ would for a single row; prints the number of rows deleted \\ table index create|drop
(multiple fields = composite index) \\ create --unique
: single-field index that also enforces the \\ field holds a distinct value across every row (SQL UNIQUE); refuses to \\ install over data that already has a duplicate; dropping the index drops \\ the constraint too. Not available for composite (multi-field) indexes. \\ table fk create
[restrict|cascade] \\ installs a FOREIGN KEY: every
row's (non-null) value must \\ name an existing on (SQL FOREIGN KEY REFERENCES); \\ must be either the special primary-key name "__pk" or a field \\ already carrying its own --unique index on , or the create is \\ refused; refuses to install over a
row whose current value has \\ no matching target; rows missing entirely are exempt (nullable FK). \\ Last argument picks ON DELETE behavior for a `table delete`/`table_delete` \\ against , default "restrict": restrict refuses the delete while \\ a
row still points at it, cascade removes those rows too (and \\ anything cascading from them, recursively). A `txn