Tools in the query
These tools live inside a RETURN (or in a comment on the same query). You do not prefix the statement — you write them as expressions.
Statement-level tools (EXPLAIN, FORMAT, SHOW …) are on Statement tools .
Type inspection
TYPEOF() returns the FeatureQL type. SQLTYPEOF() returns the type in the target backend. These can differ — FeatureQL BIGINT is BIGINT on DuckDB, integer on Trino, and Int64 on serving:
RETURN
n := 1,
fql_type := TYPEOF(n),
backend_type := SQLTYPEOF(n)
;| N BIGINT | FQL_TYPE VARCHAR | BACKEND_TYPE VARCHAR |
|---|---|---|
| 1 | BIGINT | BIGINT |
If a query works on one backend and not another, compare the two. Same playground: Types .
Versions
VERSION() returns the FeatureMesh registry version (and, on a local development registry, the last reload time). SQLVERSION() returns the active dialect name and the backend engine version string — the same FeatureQL-vs-backend split as TYPEOF() / SQLTYPEOF():
RETURN
dialect_known := SQLVERSION()[dialect] IN (
'DUCKDB',
'TRINO',
'BIGQUERY',
'DATAFUSION'
),
has_engine_version := LENGTH(SQLVERSION()[version]) > 0
;| DIALECT_KNOWN BOOLEAN | HAS_ENGINE_VERSION BOOLEAN |
|---|---|
| true | true |
Use bracket access for single fields: VERSION()[version], VERSION()[reloaded_at], SQLVERSION()[dialect], SQLVERSION()[version].
DESCRIBE() — metadata as columns
DESCRIBE(expr) returns a typed metadata row (name, type, entity, unit, description, status, attributes, presentation, inputs, dependencies, formula, literal_status, grain, additivity, determined_by). Nested bags (attributes, presentation, inputs, dependencies) are JSON serialized as VARCHAR when non-empty. grain is JSON direct_grain; additivity and determined_by come from the semantic profile. It works on any expression — not only persisted features — and is the SELECT-friendly counterpart to TYPEOF() when you need catalog fields as well as the type:
DEFINE
amount := 1.5::DOUBLE@CURRENCY.USD
RETURN
DESCRIBE(amount)[name],
DESCRIBE(amount)[type],
DESCRIBE(amount)[entity],
DESCRIBE(amount)[unit],
DESCRIBE(amount)[formula]
;| NAME VARCHAR | TYPE VARCHAR | ENTITY VARCHAR | UNIT VARCHAR | FORMULA VARCHAR |
|---|---|---|---|---|
| AMOUNT | DOUBLE@CURRENCY.USD | NULL | CURRENCY.USD | 1.50::DOUBLE@CURRENCY.USD |
inputs and dependencies expose the local graph for the expression:
DEFINE
x := INPUT(BIGINT),
y := x + 1
RETURN
DESCRIBE(y)[name],
DESCRIBE(y)[inputs],
DESCRIBE(y)[dependencies],
DESCRIBE(y)[literal_status]
FOR
x := BIND_VALUE(1)
;| NAME VARCHAR | INPUTS VARCHAR | DEPENDENCIES VARCHAR | LITERAL_STATUS VARCHAR |
|---|---|---|---|
| Y | ["X"] | {"X":1,"Y":2} | NOT_LITERAL |
literal_status is LITERAL for constants, LITERAL_COMPUTED for foldable expressions, and NOT_LITERAL once an INPUT (or other runtime value) is in the chain:
DEFINE
z := 42,
w := 1 + 2,
x := INPUT(BIGINT),
y := x + 1
RETURN
DESCRIBE(z)[literal_status],
DESCRIBE(w)[literal_status],
DESCRIBE(y)[literal_status]
FOR
x := BIND_VALUE(1)
;| LITERAL VARCHAR | COMPUTED VARCHAR | RUNTIME VARCHAR |
|---|---|---|
| LITERAL | LITERAL_COMPUTED | NOT_LITERAL |
Prefer RETURN DESCRIBE(…) when you want columns you can project with [field] access. The DESCRIBE statement (JSON cell, optional FIELDS) is on Statement tools . Function reference: DESCRIBE() .
RETURN TRACE
RETURN TRACE evaluates a feature and every upstream dependency, returning inputs first and the requested feature last. Use it when intermediate values help debug a computation.
Playgrounds (local and persisted chains) live on Explore features .
Warnings
FeatureQL returns warnings alongside successful translation or execution (for example UNUSED-FEATURE when a DEFINE feature is not reachable from the RETURN list). They do not block the query; they surface in query(), translate(), validate(), and the warnings field on API responses.
Each warning includes a stable acknowledgement token derived from its code and message, appended as (acknowledge with ACK-XXXX). To silence a warning you accept, put that token in any line or block comment in the same query (-- ACK-XXXX or /* … ACK-XXXX … */); placement does not matter. A comment that contains an ACK-XXXX token with no matching warning produces STALE-WARNING-ACK — remove the leftover token from the query. Persisted views use the same tokens in the view body; create, replace, and refresh fail until the warning is acknowledged.