Query tools

FeatureQL includes built-in tools for inspecting, debugging, and exploring your features — all accessible from within the query language itself.

Inspecting queries with EXPLAIN

Prefix any query with EXPLAIN to see a JSON plan: stage, output schema, features, layers, and related diagnostics — without executing the query:

FeatureQL
EXPLAIN (FIELDS (STAGE, OUTPUT_SCHEMA, FEATURES)) SELECT feature1 := 1, feature2 := feature1 + 1;
Result
OUTPUT VARCHAR
stage
output_schema
features[?name == 'FEATURE1'].function
features[?name == 'FEATURE2'].function

Use EXPLAIN (FIELDS (…)) to keep only the top-level keys you need (for example STAGE, OUTPUT_SCHEMA, FEATURES, FINAL_NODES). The result stays one OUTPUT VARCHAR cell containing JSON; unknown field names raise a stable user error.

Inspecting features with DESCRIBE

DESCRIBE shows metadata about a specific feature — its function, parameters, dependencies, and type information:

FeatureQL
CREATE TEMPORARY FEATURES AS SELECT feature1 := 1, feature2 := feature1 + 1;
DESCRIBE (FIELDS (NAME, FUNCTION)) FEATURE2;
Result
OUTPUT VARCHAR
[0].name
[0].function
length(@)

Use DESCRIBE (FIELDS (NAME, FUNCTION, …)) the same way to project each feature object before serialization. It works with both temporary and persisted features.

Formatting queries with FORMAT

FORMAT rewrites a query into canonical layout and syntax without executing it — useful for code review, diffing, and the validate() client method. Options include ESCAPED (newline markers for tests), FIX (migrate STRUCT/LIST and other legacy forms to strict syntax), and REORDER (dependency order within clauses). Comments are preserved when they attach to a recognized anchor.

See Formatting queries for FIX caveats, comment attachment rules (leading vs trailing, comma granularity, unrecognized comments), and worked examples.

Recreating features with SHOW CREATE

SHOW CREATE FEATURES returns the FeatureQL code needed to recreate one or more features. This is useful for understanding how features are stored internally and for exporting definitions.

For temporary features:

FeatureQL
START TRANSACTION;
CREATE TEMPORARY FEATURES AS SELECT feature1 := 1, feature2 := feature1 + 1;
SHOW CREATE FEATURES FEATURE1, FEATURE2;
COMMIT;
Result
OUTPUT VARCHAR
CREATE FEATURES AS SELECT FEATURE1 := 1, FEATURE2 := FEATURE1 + 1 ;

Notice that FeatureQL reorders features by dependency layer and may rewrite syntax — 1 becomes VALUE(1), for example. The semantics are preserved even when the surface syntax changes.

For persisted features, the output includes full namespace paths:

FeatureQL
CREATE OR REPLACE FEATURES AS
SELECT
    fm.tutorials.querytools.feature1 := 1,
    fm.tutorials.querytools.feature2 := fm.tutorials.querytools.feature1 + 1
;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.QUERYTOOLS.FEATURE1CREATEDFeature created as not exists
FM.TUTORIALS.QUERYTOOLS.FEATURE2CREATEDFeature created as not exists

FeatureQL
SHOW CREATE FEATURES FM.TUTORIALS.QUERYTOOLS.FEATURE2;
Result
OUTPUT VARCHAR
CREATE FEATURES AS SELECT FM.TUTORIALS.QUERYTOOLS.FEATURE1 := 1, FM.TUTORIALS.QUERYTOOLS.FEATURE2 := FM.TUTORIALS.QUERYTOOLS.FEATURE1 + 1 ;

Features are topologically ordered by their dependency graph, making the output self-contained — you can run it directly to recreate the features.

Dialect option on SHOW CREATE

Without LANGUAGE, SHOW CREATE FEATURES returns FeatureQL recreate text. With (LANGUAGE DUCKDB) (or TRINO, BIGQUERY, DATAFUSION), it returns the SQL that dialect would run for SELECT of those features — useful for inspecting generated SQL without a separate translate() call:

FeatureQL
SHOW CREATE FEATURES (LANGUAGE DUCKDB)
    FM.TUTORIALS.QUERYTOOLS.FEATURE1,
    FM.TUTORIALS.QUERYTOOLS.FEATURE2
;
Result
OUTPUT VARCHAR
AS "FM.TUTORIALS.QUERYTOOLS.FEATURE1"
AS "FM.TUTORIALS.QUERYTOOLS.FEATURE2"

Use # match: contains in SLT for stable SQL fragments (aliases, expressions) without pinning CTE wrappers.

Exploring documentation

SHOW DOCS lets you search the built-in documentation without leaving the query environment:

FeatureQL
SHOW DOCS (
    EXCLUDE (content),
    INCLUDE (LENGTH(name) as lname)
)
WHERE lname>30 and category='DOC_PAGE' and name like 'docs%'
ORDER BY name ASC
LIMIT 5
Result
NAME VARCHARCATEGORY VARCHARDISPLAY_ORDER ARRAYTITLE VARCHARTAGS ARRAYRELATED_TAGS ARRAYLNAME BIGINT
docs/1-concepts/1-why_featuremesh.mdDOC_PAGE[1, 1]Why FeatureMesh?NULLNULL36
docs/1-concepts/2-design_philosophy.mdDOC_PAGE[1, 2]Design philosophyNULLNULL38
docs/1-concepts/3-in_your_organisation.mdDOC_PAGE[1, 3]FeatureQL in your organizationNULLNULL41
docs/1-concepts/4-batch_analytics.mdDOC_PAGE[1, 4]Batch analyticsNULLNULL36
docs/1-concepts/5-real_time_serving.mdDOC_PAGE[1, 5]Real-time servingNULLNULL38

Filter by category, search content with LIKE, and sort results to find relevant pages, code samples, and function references. Combined with SHOW FUNCTIONS and SHOW SIGNATURES (covered in Operators & Functions ), these tools make FeatureQL largely self-documenting.

Warnings

FeatureQL returns warnings alongside successful translation or execution (for example UNUSED-FEATURE when a WITH feature is not reachable from the SELECT 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.

Client-side productivity tools

The Python client (BatchClient) wraps the query tools above into higher-level methods designed for iterative development and LLM-assisted workflows. Each method orchestrates one or more FeatureQL queries and returns structured results.

MethodWhat it doesKey FeatureQL under the hood
client.validate(query)Format to strict mode, extract output schema, list features in topological order, surface warnings — without executing SQLFORMAT (FIX), EXPLAIN (FORMAT JSON), translate()
client.diagnose(query)Execute features one at a time in topological order, stop at the first failure, return intermediate dataframesEXPLAIN (FORMAT JSON)incremental_queries → sequential query()
client.help("tag1", "tag2")Search documentation pages, code samples, function signatures, and tests by tag or keywordSHOW DOCS, SHOW SIGNATURES, SHOW TESTS
client.describe("fm.ns")List persisted features under a prefix with their types, formulas, and dependency lineageSHOW FEATURES, SHOW CREATE FEATURES, EXPLAIN (FORMAT JSON)

All four return a result object with .text (markdown) and structured fields. Call .display() in a notebook to render the markdown directly.

These methods are also exposed as HTTP endpoints (POST /validate, /diagnose, /help, /describe on port 8101) and as MCP tools for LLM agents.

Code samples

They represent end to end tests for capabilities. They use the SLT formalism to describe the test.

# name_of_the_test
query FEATURE1:I,FEATURE2:I,?:I [rowsort]
SELECT
    FEATURE1 := 1,
    FEATURE2 := 2,
    FEATURE1 + FEATURE2
;
----
1   2   3
null

The line query represent the expected output of the query: column names and types.

Types in SLT queries are represented as:

TypeCode
IntegerI
DecimalD
StringV
BooleanB
TemporalT
JSONJ
RowR
Array of scalarsA
Array of rowsQ