Skip to main content
Arrow Flight SQL is the protocol Parable’s SQL endpoint speaks. Parable authenticates and authorizes each gRPC call, then streams the Arrow results back unchanged. To connect DBeaver or Python step by step, start with Query Provider Pools with SQL. For language clients, see Query with SDKs. Use this page when you configure an ADBC, JDBC, or custom Flight SQL client.

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 Query references an unknown relation, 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.

Tokens

A SQL tool authenticates with a pat_ 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. Flight SQL refuses a call with:

Connect with ADBC

Set PARABLE_FLIGHT_URL to grpc+tls://sql.parable.work:443. The public endpoints use TLS. A local development stack can serve cleartext HTTP/2 at a grpc:// URL.

Request lifecycle

For a statement query:
  1. GetFlightInfo validates and plans the SQL, then returns an opaque ticket.
  2. DoGet executes from that ticket and streams Arrow record batches.
  3. The client releases the result stream when it has consumed or abandoned it.
Prepared statements follow the same read path with a server-issued handle. See supported operations for the complete command matrix.

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 with DEADLINE_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 sends them batch by batch as Query Layer produces them, and the Workspace API forwards each batch without collecting the result. 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:
The error can arrive after some record batches. Your driver raises it when you read past them, so discard the rows it returned before the error: they are not the result. To stay under the limit, add 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. 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 as Contact.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.Date and Temporal.DateTime. contacts.email = people.phone fails with INVALID_ARGUMENT and error code QL-PL-007 (INCOMPARABLE_SCALARS). The message names both columns and both scalars. The check covers comparison operators, BETWEEN, IN, subquery comparisons, join keys, and UNION, INTERSECT and EXCEPT columns.
  • 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.UUID or Contact.Email column is converted to that scalar’s canonical form, so email = 'Jane@Example.com' matches the stored jane@example.com. A value that is not valid for the scalar is compared as written: email = '' returns no rows, and email <> '' 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.
A recursive query works the same way in SQL over Flight, in a Plot, in a Pipeline and in SQL over a Parable’s Plots. A large hierarchy is cheaper as a Pipeline: it is computed once per Refresh, and readers read its stored rows.

Errors and retries

Unsupported commands return gRPC UNIMPLEMENTED. 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. SQL the query engine cannot run is the query’s to fix. It returns INVALID_ARGUMENT with one of these codes, and the message never repeats the SQL or its values. Change the query rather than retrying: 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. 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, naming the Plot and never the value. 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. Three 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 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.