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