QQL scripts are for repeatable provisioning, migrations, fixtures, and batched operations. Parse a script once, retain its statements, apply any trusted rewrite, and then plan or execute the result.
Write a readable script
Section titled “Write a readable script”Statements are separated by one semicolon; a final semicolon is optional. Empty separators, adjacent unseparated statements, and scripts over 256 statements are rejected.
CREATE COLLECTION catalog (dense VECTOR(384, COSINE));
CREATE INDEX ON COLLECTION catalog FOR category TYPE keyword;
UPSERT INTO catalog VALUES {id: 1, text: 'red chair', category: 'furniture'};
QUERY 'chair' FROM catalog USING dense LIMIT 5;Batch independent operations
Section titled “Batch independent operations”BATCH { ... } runs homogeneous same-collection members as one Qdrant batch RPC. Members are full statements: all queries or all mutations. Query batches carry shared read opts in PARAMS; mutation batches carry WAIT.
BATCH { QUERY [0.1] FROM docs LIMIT 1; QUERY [0.2] FROM docs LIMIT 3; };
BATCH { QUERY [0.1] FROM docs LIMIT 1; QUERY [0.2] FROM docs LIMIT 3; } PARAMS (timeout = 30, consistency = majority);
BATCH { UPSERT INTO docs VALUES {id: 1, vector: [0.1]}; DELETE FROM docs WHERE id = 2; } WAIT false;Empty blocks, nested blocks, mixed families, mixed collections, DDL, SHOW, COUNT, SCROLL, FACET, and SET QUOTA members fail closed. WAIT false sends ?wait=false explicitly. Edge fans out per member instead of sending a native batch RPC.
Use the right host API
Section titled “Use the right host API”| API | Contract |
|---|---|
Parser::parse | One complete statement only |
Parser::parse_all | One complete semicolon-delimited script |
SDK parse | Host-language statement handles or objects |
compile / explain | Offline plan inspection |
Executor or client execute | One statement, a script, or host array input |
Host arrays are independent inputs. A semicolon-delimited script is one input that the runtime parses together, which matters when batching compatible operations.
Surface structured failures
Section titled “Surface structured failures”Errors carry a stable code, kind, message, and optional zero-based UTF-8 byte span. Message text is not normative: match on the code. The stable codes are grouped by their prefix; see Error codes for the full list with meanings.
| Code prefix | Family | Examples |
|---|---|---|
QQL-LEX-* | Lexical failures | QQL-LEX-STRING |
QQL-PARSE-* | Syntax, clause order, and value-range failures | QQL-PARSE-STATEMENT, QQL-PARSE-CLAUSE-ORDER, QQL-PARSE-DUPLICATE-KEY, QQL-PARSE-POSITIVE-INTEGER |
QQL-VALIDATION-* | Semantic validation of a parsed program | QQL-VALIDATION-FROM, QQL-VALIDATION-UPSERT-ID, QQL-VALIDATION-SLICE, QQL-VALIDATION-IDF |
QQL-PLAN-*, QQL-MISSING-*, QQL-UNKNOWN-*, QQL-VECTOR-KIND | Planning and schema resolution | QQL-PLAN-VECTOR-KIND, QQL-PLAN-QUOTA, QQL-PLAN-IDF, QQL-MISSING-USING |
QQL-EMBEDDING-* | UPSERT embedding inference | QQL-EMBEDDING-TOPOLOGY, QQL-EMBEDDING-TARGET |
QQL-EDGE-* | In-process edge backend capability and runtime failures | QQL-EDGE-UNSUPPORTED-GROUP-LOOKUP, QQL-EDGE-UNSUPPORTED-QUOTA, QQL-EDGE-INVALID-POINT-ID |
| Transport / backend | Network and Qdrant response failures during execution | QQL-GRPC-QUOTA, other transport-layer QQL-* |
A code already asserted by a v1 invalid fixture cannot change before v2. New codes may only refine previously unspecified cases.
| Situation | Example code family | What to do |
|---|---|---|
| Syntax or clause order | QQL-PARSE-* | Show the span and preserve the user source |
| Missing required query structure | QQL-VALIDATION-* | Add the required FROM, PREFETCH, or valid parameter |
| Vector-role conflict | QQL-PLAN-VECTOR-KIND | Align the query role with the collection topology |
| Service policy cannot be applied | policy error | Reject the request rather than weakening the predicate |
The Playground shows the same source, AST, compiled route, and explanation without connecting to Qdrant until you choose to run it.
Offline linting with qql lint
Section titled “Offline linting with qql lint”Before deploying or committing scripts, use qql lint for static syntax, plan, and style verification:
qql lint scripts/queries.qql qql lint --fixqql lint is completely offline: it parses statements with error recovery, compiles them through the query planner, flags redundant WITH PAYLOAD true clauses, and checks canonical formatting without connecting to a cluster.
Live query triage with qql doctor / qql check
Section titled “Live query triage with qql doctor / qql check”When troubleshooting live execution against a running cluster, run qql doctor "<statement>" (or qql check "<statement>"). It runs five stages in order and prints one line per stage with the QQL-* code on failure:
- Format check of the input.
- Offline explain (no backend needed).
- Embed endpoint dim probe when the statement needs text embeddings (skipped for literal-vector statements).
- USING and vector-name topology check against the live collection when reachable.
- Backend doctor (Qdrant reachability, with unreachable and auth failures reported distinctly).
qql doctor "QUERY [0.1, 0.2, 0.3] FROM docs LIMIT 1" qql doctor "QUERY 'typed query language' FROM docs USING dense LIMIT 5"Fix the first failing stage and rerun. Literal-vector statements pass the embed stage as skipped, so they are the fastest way to confirm the backend path is healthy.