Connection contract
x-tenant-slug is accepted as an alias for x-tenant.
Send the authorization metadata on every call. The Flight Handshake RPC is
accepted and does nothing, so a client cannot authenticate through it.
Pools each role reaches
A query that names a pool the caller’s role does not reach fails with
INVALID_ARGUMENT, the message FROM or JOIN names a source this Workspace cannot read. Choose an available source, or ask a Workspace Administrator to check your data access., and
error code QL-PL-002. Catalog discovery leaves that pool out.
Inside the pools a role reaches, the Workspace’s
Provider Policies decide which rows of each
Provider table it reads. Table metadata leaves out the row count and
validation rates of a table they narrow for the caller. A caller whose
Workspace roles hold All data access
(tenant.data.unrestricted), as Workspace Administrator does, reads every
row of every Provider table, and each such call is recorded in the
Workspace’s audit events.
Tokens
A SQL tool authenticates with apat_ token
(API tokens). A SQL token a
member creates in Protection → Access Tokens reaches that Workspace only
and queries with its owner’s Workspace roles at the time of each query; a
staff role never counts. A Parable token queries any Workspace its owner can,
with the owner’s roles, staff roles included, at the time of each query.
Flight SQL refuses a call with:
Connect with ADBC
PARABLE_FLIGHT_URL to grpc+tls://sql.parable.work:443. The public
endpoints use TLS.
Request lifecycle
For a statement query:GetFlightInfovalidates and plans the SQL, then returns an opaque ticket.DoGetexecutes from that ticket and streams Arrow record batches.- The client releases the result stream when it has consumed or abandoned it.
Limits
A query may run for up to one hour. The clock starts when Parable admits the query and covers planning and the whole result stream, including the time your client spends reading it. A query that runs longer fails withDEADLINE_EXCEEDED, Parable error code QL-TO-002 (QUERY_TIMEOUT), and the
message query <id> exceeded timeout of 3600s.
The server sets this limit. A longer deadline on your client does not extend
it, and a shorter one ends the call early, so set your driver’s timeout to at
least an hour before running a long query.
Row limit
A query may return up to 2,000,000 rows.DoGet streams them batch by batch, without
collecting the result first.
A query whose result has more rows fails. Nothing is truncated: the stream
ends with RESOURCE_EXHAUSTED, Parable error code QL-SQ-005
(RESULT_ROW_CAP_EXCEEDED), and this message:
LIMIT, filter with WHERE, or
aggregate in SQL. To read more rows than the limit, run one query per range of
a column, such as one month at a time.
AI call limits
ponder(), embed() and the Jev functions (ask, noul, choice and
score) send paid calls to an AI Provider. By default, one query may send
up to 1,000,000 of these calls and spend up to $1,000 on them. What counts as
one call:
ponder()and a Jev function send one call for each row.embed()sends up to 32 rows in one call.- A Jev call that TypeSafe or a decision model throttles and Parable sends again counts again. A decision model answers one Jev call with one chat request for each question, and the call counts once. The cost limit counts each question that still has no answer.
RESOURCE_EXHAUSTED
and one of these error codes:
QL-SQ-010(AI_CALL_LIMIT_EXCEEDED): the query would send more calls than the limit.QL-SQ-011(AI_COST_LIMIT_EXCEEDED): the query’s calls would cost more than the limit. Parable knows the cost of a call only after the call answers, so the calls already sent can pass the limit by a small amount.
FAILED_PRECONDITION and
QL-SQ-012 (AI_MODEL_UNPRICED). The message names the function, the model
and its processor. Most refusals come before any call. On OpenRouter, and for
a Jev alias, the price shows in the answer, so the call is refused after that
answer and no further call is sent. Do not retry: name a model that has a
price.
The message names the limit and the function. The error can arrive after some
record batches: discard them, because they are not the result. Do not retry
the same query. Call the function over fewer rows: filter with WHERE, sample
or LIMIT the rows before the function, or aggregate fewer rows. To call a
function over every row of a large table, publish the SQL as a Plot. A
Refresh keeps each answer and runs under its own limits.
Each IP address may send 600 calls a minute. A query takes at least two calls,
GetFlightInfo and DoGet. Calls over the limit are refused with HTTP status
429 at Parable’s edge, which gRPC clients report as UNAVAILABLE.
Metadata discovery
Flight SQL supports catalog, schema, table, table-type, SQL capability, and logical key discovery.GetTables can include an Arrow IPC schema for every
matching table, including optional Parable semantic metadata.
Discover tables and columns
shows how to use those operations.
Semantic types in comparisons
A column can carry a semantic scalar, such asContact.Email or
Contact.PhoneNumber, in its parable.scalar.canonical_name field metadata.
Parable uses it when a query plans:
- Two columns with different scalars can be compared only when scalar-lib
declares them comparable, such as
Temporal.DateandTemporal.DateTime.contacts.email = people.phonefails withINVALID_ARGUMENTand error codeQL-PL-007(INCOMPARABLE_SCALARS). The message names both columns and both scalars. The check covers comparison operators,BETWEEN,IN, subquery comparisons, join keys, andUNION,INTERSECTandEXCEPTcolumns. - To compare across scalars on purpose, cast one side where it is compared:
CAST(contacts.email AS VARCHAR) = people.phone. A column without a scalar compares with any column. - A value you supply is never refused and needs no cast. A string literal or
bound parameter compared with an
Identity.UUIDorContact.Emailcolumn is converted to that scalar’s canonical form, soemail = 'Jane@Example.com'matches the storedjane@example.com. A value that is not valid for the scalar is compared as written:email = ''returns no rows, andemail <> ''returns every row with an email.
Embedding vectors in comparisons
embed() stamps its model identity (provider, name, variant, version,
dimensions, normalization) in its output field’s parable.model_* metadata.
Vectors are comparable only within one identity, so array_distance over two
vectors stamped with different identities fails with
INVALID_ARGUMENT and error code QL-PL-008
(INCOMPARABLE_EMBEDDING_MODELS), even when their dimensions match. The
message names the function and both models. A Plot’s embed() column carries
the identity of the model that wrote it. A vector without an identity, such as
a literal vector, is compared on its dimensions alone; see
Embed.
Recursive queries
WITH RECURSIVE walks a hierarchy, such as everyone below each manager at any
depth. Join its two terms with UNION: it stops when an iteration adds no new
row, so a loop in the data, such as a person listed as their own manager,
ends. UNION ALL fails with INVALID_ARGUMENT and error code QL-PL-014
(RECURSIVE_UNION_ALL), because nothing else would stop it.
Errors and retries
Unsupported commands return gRPCUNIMPLEMENTED. Invalid authentication and
Workspace selection return gRPC authentication, permission, or argument
statuses rather than HTTP JSON errors.
A Parable error code, such as QL-PL-002, arrives in the
parable-error-code response metadata beside the gRPC status. Facts a
refusal names for you, when it names any, arrive as a JSON object of strings in
the binary parable-error-details-bin metadata.
When query concurrency is saturated, Parable returns RESOURCE_EXHAUSTED with
error code QL-IN-008. A client can retry a
read-only query with bounded exponential backoff. Do not retry invalid SQL,
authentication failures, or permission failures without changing the request.
QL-PL-007 (INCOMPARABLE_SCALARS) is deterministic: change the comparison or
add a CAST rather than retrying. QL-PL-008 (INCOMPARABLE_EMBEDDING_MODELS)
is too: embed both sides with the same model. So is QL-PL-015
(FILTER_POLICY_COLUMN_MISSING, FAILED_PRECONDITION): a Plot that reads
Provider data lacks a column a Parable filter Policy that binds you reads,
usually identity_account_id; the Plot’s author adds the column.
QL-TN-009 (SOURCE_PERMISSIONS_UNAVAILABLE) arrives with
FAILED_PRECONDITION. Source permissions
decide which rows of a table you read, and Parable can’t tell who may open
its rows. Nothing is read: Parable never returns every row instead. The
message says why, and the table detail names the table. When the message
says that the access projection has not been built for its Provider, Parable
has not read that Provider’s sharing yet. Parable reads a Provider’s sharing
after each sync of it, so run the query again after the Provider’s next sync.
SQL the query engine cannot run is the query’s to fix. It returns
INVALID_ARGUMENT with one of these codes, unless a row names another status.
The message names at most the
columns, functions and types involved, never the rest of the SQL or its
values. Change the query rather than retrying:
GetFlightInfo and GetSchema run the engine’s type checks on the statement
they plan, so QL-PL-019 arrives there, and a DoGet of the same statement
answers it too. A prepared statement is checked at the GetFlightInfo after
its parameters are bound, not at CreatePreparedStatement. Casts and some
unsupported expressions are checked when the result is read, so QL-PL-009
and QL-PL-012 can arrive from DoGet after GetFlightInfo succeeded. Other
engine failures keep INTERNAL with QL-IN-001
(DATAFUSION_ERROR) and the message Query execution failed. That includes
an error raised by a row while the query runs, such as a string value that
does not cast to a number, because its description carries the value. A Jev
argument read from a column, rather than written in the query, is checked on
each row, so a row it refuses fails this way under the default
on_error = fail, unless its request is too large to send (QL-SQ-006,
below). A row a Plot returns that its declared contract does not
allow, read through parables.*, fails with FAILED_PRECONDITION and error
code PA-AU-047. The message names the Plot, the column that holds the value
and what is wrong with it, never the value.
A Jev function’s request (ask, noul, choice or score) that gets no
answer fails the query with the limit it hit. The request goes to TypeSafe,
or, when the Workspace’s AI policy resolves the call to one, to a decision
model that Google Vertex AI serves. A Workspace that switches
TypeSafe off runs its Jev calls on the decision model. The message names the
function, the service, and the limit or the service’s HTTP status, never the
state or the questions.
They can arrive from DoGet, after GetFlightInfo succeeded:
Under
on_error = null, a row whose request fails with QL-SQ-006 or
QL-SQ-007 reads NULL instead, as a row with an invalid argument or an
unusable answer does. That failure belongs to the row. A QL-TO-003 or
QL-IN-010 failure belongs to the endpoint, so it always fails the query,
including under on_error = null.
When a workspace table is briefly unavailable, Parable returns UNAVAILABLE
with error code QL-IN-005 (UPSTREAM_UNAVAILABLE), and the message names the
table. Retry the query with the same backoff.
Five error codes arrive with RESOURCE_EXHAUSTED, and each one needs a
different response. Read the error code before you retry:
RESOURCE_EXHAUSTED can also arrive with no error code, for example when a
workspace table returns more rows than one query may read: do not retry
it, because the message says why the query was refused.
Like QL-SQ-005, QL-IN-009, QL-SQ-010 and QL-SQ-011 can arrive after
some record batches. Discard the rows your driver returned before the error:
they are not the result.
QL-TO-002 (QUERY_TIMEOUT) is a query past the time limit. Filter,
aggregate, or split it before running it again.
QL-TO-004 (ROW_SEARCH_REREAD_TIMEOUT) arrives with DEADLINE_EXCEEDED
when search_everything
could not read its matching records again from any source table in time. The
message names the time limit. Run the search again. If it fails again, narrow
its scope to fewer tables.