Skip to Content
We are live but in Staging 🎉

Reads

Write literal table names. Joins, CTEs, derived tables and WHERE subqueries all resolve on their own — there is no lookup-table map to fill.

Query

rpc Query(QueryRequest) returns (QueryResponse); message QueryRequest { string db_id = 1; string sql = 2; map<string, Value> params = 3; // @name placeholders — never interpolate map<string, Type> param_types = 4; // NUMERIC / TIMESTAMP / DATE / JSON binds uint32 limit = 5; // 0 = server default optional string min_ulid = 6; // read-your-writes watermark ResultEncoding result_encoding = 7; // TYPED (default) | ARROW uint32 knn_ef_search = 8; // pgvector: SET hnsw.ef_search uint32 knn_probes = 9; // pgvector: SET ivfflat.probes bool knn_exact_scan = 10; // pgvector: exact re-rank bool knn_eventual = 11; // pgvector: allow an eventual index read } message QueryResponse { RowSet rows = 1; // EMPTY when the result spooled — read manifest ServedBy served_by = 2; string max_wal_ulid = 3; // watermark this read observed repeated string warnings = 4; ResultManifest manifest = 5; // set above the inline size threshold map<string, string> table_watermarks = 6; // one token per table read }
{ "db_id": "kb-prod", "sql": "SELECT * FROM events WHERE user_id = @uid LIMIT 100", "params": { "uid": { "string_value": "u-101" } } }

Execute’s SELECT arm and Query reach the same engine; prefer Query on read-only paths because its request cannot write. The HTTP twin is POST /v1/databases/{db}/sql/query with {"sql": "…"} only.

manifest means rows is empty

Above a size threshold the plane spools the result to object storage and returns presigned GETs (format is arrow or parquet, with ttl_seconds). Fetch the parts directly, or re-issue with a LIMIT. An empty rows alongside a set manifest is not a zero-row result.

A strong read that cannot complete

When compaction is far behind, a read that would have to merge more pending writes than the engine can hold aborts instead of returning a partial answer:

FAILED_PRECONDITION: strong read aborted: the WAL overlay truncated at N entries, so the result would be incomplete — compaction is catching up; retry shortly

Retry after a moment; a drain is already scheduled by the time this can happen. To force it, call Compact.

QueryStream

Same QueryRequest; the response is a stream of frames.

rpc QueryStream(QueryRequest) returns (stream QueryStreamResponse); message QueryStreamResponse { RowSet chunk = 1; bool last = 2; // final frame ServedBy served_by = 3; repeated string warnings = 4; }

Column metadata travels only in the first frame — TypedRows.columns (or the Arrow schema message) is empty on continuation frames. Streaming is a transport choice, not a semantic one; use it when a result would exceed the inline cap and you do not want to fetch spooled parts.

GetRow

A primary-key point read. Always answered at strong consistency.

rpc GetRow(GetRowRequest) returns (GetRowResponse); message GetRowRequest { string db_id = 1; string table = 2; RowSet key = 3; // the PK columns, typed, one row repeated string columns = 4; // projection (empty = all) optional int64 as_of_version = 5; // time travel: a pure historical snapshot optional string min_ulid = 6; ResultEncoding result_encoding = 7; } message GetRowResponse { bool found = 1; RowSet row = 2; // absent when not found ServedBy served_by = 3; string max_wal_ulid = 4; int64 version = 5; // the table version this read observed }

With as_of_version set the read is a snapshot of that committed version and does not include writes committed since. This is the only read-side time travel on the surface.

BatchGetRows

rpc BatchGetRows(BatchGetRowsRequest) returns (BatchGetRowsResponse); message BatchGetRowsRequest { string db_id = 1; string table = 2; RowSet keys = 3; // one row per key repeated string columns = 4; optional int64 as_of_version = 5; ResultEncoding result_encoding = 6; } message BatchGetRowsResponse { RowSet rows = 1; int64 version = 2; RowSet unprocessed_keys = 3; // not attempted this call — retry exactly these }
RuleDetail
CapsAt most 100 keys and 16 MiB per request; over either is INVALID_ARGUMENT. Split the batch.
AbsenceA key missing from rows and not in unprocessed_keys does not exist. Absence is not an error.
unprocessed_keysKeys the plane could not serve this call (a shard moved mid-fetch). Retry exactly those.
Column setEvery found row carries every requested column, NULL where the row has no value. A requested column is never silently absent. since 0.1.43

InferParams

Read-only parameter-type inference for a prepared statement. Given SQL with @pN (or $n) placeholders, returns the type the engine will bind each one as, resolved from the query and the catalog. Executes nothing. The Postgres adapter calls it at Parse so that Describe and Bind agree; you need it only if you are building a driver.

ServedBy

enum ServedBy { SERVED_BY_UNSPECIFIED = 0; SERVED_BY_HOT = 1; // served from a reader's RAM SERVED_BY_WARM = 2; // served from a reader's local disk SERVED_BY_MERGED = 3; // recent writes merged over the table snapshot SERVED_BY_WAREHOUSE = 4; // table snapshot scan, nothing pending SERVED_BY_WAL = 5; // pending writes only (internal) SERVED_BY_HOT_SHARDED = 6; // sharded in-memory fan-out }

HOT / WARM / HOT_SHARDED describe residency, which you influence with LOAD TABLE / RELEASE TABLE (Performance & layout). MERGED versus WAREHOUSE tells you whether pending writes existed at read time. None of them changes what you see: every read observes every acknowledged write. How the plane decides is on How it works.

RowSet

Two encodings, chosen per request with result_encoding:

EncodingShapeUse when
TypedRows (default)columns[] with a type code per column (INT16 / INT32 / INT64, FLOAT32 / FLOAT64, STRING, UUID, BOOL, BYTES, NUMERIC, DATE, TIMESTAMP, JSON, array / struct / vector); rows[].values[] with int64_value, string_value, … NUMERIC, TIMESTAMP, DATE and UUID ride as canonical strings, exactly.You want typed scalars without an Arrow dependency.
Arrow IPC streamOne schema message then record batches.Bulk transfer into a dataframe.

A SMALLINT column arrives as INT16, an INTEGER/BIGINT column as INT64 (storage is 64-bit), a UUID column as UUID. The storage vocabulary and pg wire OIDs are listed on Data types.