Aller au contenu

SQL Query Stream v1

Ce contenu n’est pas encore disponible dans votre langue.

Version 1 streams bounded, read-only Query Workbench previews. Implementation has landed; local and beta certification remain open. Existing compatibility JSON wire contracts remain unchanged; this is not a bulk-export API.

POST /api/sql/api/query/stream/v1

The request uses Content-Type: application/json. Accept and a successful response use exactly:

application/vnd.rokks.sql-query-stream+json; version=1

Authenticate with the browser’s HttpOnly session cookie or the supported Bearer alternative. Explicit environment and tenant scope are required.

Name Type Description
Session cookie Authentication Automatic browser session; HttpOnly and unreadable by JavaScript.
Authorization Alternative Bearer <session> for supported clients.
x-env-id Header Analytical environment.
x-tenant-id Header Governed tenant.
x-priority Header Deployment query priority.
x-req-id Header Required request UUID; equals body requestId.
x-db-read Optional header primary, strong, replica, or eventual.
Accept Header Exact v1 media type above.
Content-Type Header application/json.
Name Type Description
protocolVersion 1 Required protocol discriminator.
requestId UUID string Equals x-req-id, hello, and terminal.
sql string One safe, read-only SQL statement.
params optional array Positional parameters.
limit positive safe integer Requested retained-row maximum.

The raw JSON body is limited to 256 KiB before generic JSON middleware. SQL is limited to 64 KiB. Serialized parameters are limited to 128 KiB, 1,024 top-level entries, 8,192 structural nodes, depth 16, and 256 UTF-8 bytes per object key. The route rejects non-POST methods, compressed bodies, Expect: 100-continue, duplicate protocol headers, and unsupported media variants. Authentication is checked before the body is read.

The response body is not a JSON envelope. It is a sequence of framed JSON records:

Offset Size Meaning
0 4 bytes Unsigned big-endian length N.
4 N bytes Exactly one UTF-8 JSON payload.

N must be positive; zero is invalid. hello must fit the browser’s 64 KiB bootstrap bound. The browser rejects advertised frame or Worker-batch limits above 16 MiB and a byteLimit above the hard 64 MiB preview ceiling.

maxFrameBytes excludes the prefix. byteLimit covers the entire body—including every prefix, payload, and terminal record.

Record Required fields Contract
hello v, type, requestId, limits, servedBy First and once; id and limit match the request.
schema v, type, fields Once before rows/success. Error-before-schema may omit it. Names are non-empty, case-sensitive unique, and not __proto__; each field has non-negative dataTypeID.
row v, type, seq, values Sequence starts at zero without gaps; width equals schema.
terminal v, type, requestId, outcome, counts, timing, servedBy, error Last and once; nothing follows.

Counts are non-negative safe integers. retainedRowCount equals delivered rows; rowCountLowerBound is at least that count.

Outcome Count and null invariants
complete completeRowCount, rowCountLowerBound, and retainedRowCount are equal; truncationReason and error are null.
truncated completeRowCount is null; truncationReason is row_limit or byte_limit; error is null; the lower bound is at least the retained count.
error completeRowCount and truncationReason are null; error is required; the lower bound is at least the retained count.

Errors expose only code, message, optional position, and optional retryable. Timings are finite and non-negative; terminal servedBy matches hello.

Rows remain provisional. Only valid complete or truncated commits them. Error, cancellation, protocol failure, partial frame, or terminal-less EOF discards them.

Unknown fields/types, mismatched ids or servedBy, invalid sequence/counts, trailing bytes, partial frames, and terminal-less EOF are rejected.

Name Type Description
maxFrameBytes positive integer JSON payload ceiling.
workerBatchBytes positive integer Worker row-batch ceiling.
byteLimit positive integer Whole-body wire ceiling.
servedBy enum primary or replica.
downstreamWaitMs duration Query-service drain wait.

Bounded cursor reads wait for downstream writes. The edge relays incrementally. One browser transfer remains outstanding until the parser Worker acknowledges it; the main thread owns lifecycle, final state, and rendering. Before the Worker reply is structured-cloned, arrays, objects, and bytea cells become small typed preview descriptors; scalar strings are clipped at 2,048 characters. Framing preserves the values already produced by the analytical query service’s PostgreSQL type parsers within the advertised limits; database-driver type conversion happens before framing. Query Workbench intentionally retains only the bounded display projection.

Pre-stream failures use HTTP errors; in-stream query failures use error terminal. Cancellation propagates toward the query. There is no resume, replay, exactly-once, or durable-delivery contract; do not retry blindly. The browser’s five-second cleanup settlement deadline starts only after a body is rejected or aborted; it does not cap a healthy stream, but prevents a broken body-cancel implementation from stranding the UI.

HTTP status Typical v1 meaning
400 Invalid correlation, fields, parameter shape, or ambiguous protocol header.
405 Method other than POST.
406 Exact v1 Accept value missing.
413 Pre-header request/admission limit, or a compatibility JSON response limit, was exceeded. After v1 hello, an oversized row/frame is reported by an error terminal when transport permits, otherwise by incomplete-stream failure.
415 Content type or content encoding is unsupported.
417 Expect is unsupported.
503 Execution/lifecycle service is unavailable; inspect the stable error code.
504 Initial header or bounded database phase timed out.

This supported Bearer example stores the framed response:

Terminal window
curl --no-buffer \
--request POST 'https://host.example.com/api/sql/api/query/stream/v1' \
--header 'Authorization: Bearer <session>' \
--header 'x-env-id: production' \
--header 'x-tenant-id: tenant-a' \
--header 'x-priority: high' \
--header 'x-req-id: 019c2f41-89d5-7a33-a6be-2a7115814f62' \
--header 'Content-Type: application/json' \
--header 'Accept: application/vnd.rokks.sql-query-stream+json; version=1' \
--data '{"protocolVersion":1,"requestId":"019c2f41-89d5-7a33-a6be-2a7115814f62","sql":"SELECT recorded_at, value FROM measurements ORDER BY recorded_at DESC","limit":100}' \
--output preview.stream

The decoded record order is:

hello → schema → row(0) → row(1) → … → terminal