Formatting queries
FORMAT rewrites a FeatureQL query into canonical layout and syntax without executing it. Use it to normalize style, migrate away from legacy aliases, or preview what validate() will produce. Comments are preserved when they attach to a recognized anchor; everything else is collected at the end of the output.
This page is the long form. The short version sits with the other statement tools: Statement tools .
Basic usage
Prefix any query with FORMAT:
FORMAT (ESCAPED) RETURN F1 := 1 + 2 * 3;| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := 1 + 2 * 3\n; |
The formatted text is the query result (one VARCHAR column). Add ESCAPED when you need
markers instead of real newlines — useful in tests and programmatic diffs:
FORMAT (ESCAPED) RETURN F1 := 1; Other options combine with FORMAT:
| Option | Purpose |
|---|---|
FIX | Rewrite non-strict constructs into canonical strict syntax (STRUCT → ROW, LIST → ARRAY, move BIND_VALUES from DEFINE to FOR, normalize function aliases like LEN → LENGTH). |
WIDTH n | Set the pretty printer's preferred output width. |
FORMAT is also what powers client.validate() in the Python package (FORMAT (FIX) plus schema extraction). See Statement tools and Python library tools . The FORMAT syntax reference lists the stable statement forms.
Layout and precedence
The formatter applies consistent indentation, breaks long RETURN lists across lines, and inserts parentheses only where operator precedence requires them. It does not change semantics — only surface syntax and spacing.
FIX — migrating to strict syntax
By default FeatureQL is non-strict: legacy forms such as STRUCT[…], LIST(…), and type aliases like INT64 compile, and compiler warnings stay warnings. Add /* STRICT */ to reject those aliases and to turn any remaining warning into UE/STRICT-SYNTAX. Combine FORMAT (FIX) with exploratory syntax to rewrite it into canonical, reviewable FeatureQL:
FORMAT (ESCAPED, FIX) RETURN F1 := STRUCT[1, 2 AS name], F2 := LIST(3, 4);| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := ROW(1, 2 AS name),\n f2 := ARRAY(3, 4)\n; |
FIX also normalizes function aliases when the alias is the only non-strict issue (LEN → LENGTH, MOD → MODULO, and similar).
Type aliases without FIX
Even without FIX, FORMAT rewrites type aliases to canonical names:
FORMAT (ESCAPED) RETURN F1 := CAST(1 AS INT64), F2 := CAST('hi' AS STRING);| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := CAST(1 AS BIGINT),\n f2 := CAST('hi' AS VARCHAR)\n; |
What FIX does not add
The formatter does not synthesize every annotation /* STRICT */ expects. In particular:
- Floating-point comparison precision — under
/* STRICT */, a bareDOUBLEcomparison is a warning (COMPARISON-DIGITS-DEFAULT-SQL) and therefore aUE/STRICT-SYNTAX. Write an explicit suffix (e.g.>.5,=.4).FORMAT (FIX)leaves plain>/=unchanged. - Typed null predicates —
IS NULLmay carry::typematching the expression (e.g.x IS NULL::BIGINT).FORMAT (FIX)does not insert::typefor you.
FORMAT (FIX) RETURN F1 := (1)::BIGINT IS NULL;| OUTPUT VARCHAR |
|---|
| RETURN f1 := (1)::BIGINT IS NULL ; |
FORMAT (FIX) RETURN F1 := 1e0::DOUBLE > 2e0::DOUBLE;| OUTPUT VARCHAR |
|---|
| RETURN f1 := 1e0::DOUBLE > 2e0::DOUBLE ; |
Add those annotations by hand before adding /* STRICT */, or keep the query non-strict until they are in place.
Comments
FORMAT round-trips comments that attach to a syntactic anchor. Attachment is decided only by position — not by guessing intent from text.
Trailing comments (same line as code)
A comment to the right of code attaches to whatever syntactic unit closes immediately to its left:
- Before the terminating comma → the parameter or leaf on the left (often forcing a multiline break for compound expressions).
- After the terminating comma → the whole feature definition.
The comma is a granularity toggle you control directly: keep the comment before the comma to document a sub-expression; move it past the comma to document the feature.
A basic trailing comment:
FORMAT (ESCAPED) RETURN
f1 := 1, -- end-of-line note
f2 := 2
;| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := 1, -- end-of-line note\n f2 := 2\n; |
Before the comma, on a compound expression, the comment attaches to the leaf on its left, and the expression breaks across lines so the comment can sit on that leaf's line:
FORMAT (ESCAPED) RETURN
f1 := a + 1 -- before comma: leaf note
,
f2 := 2
;| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := a\n + 1 -- before comma: leaf note\n ,\n f2 := 2\n; |
After the comma, the same comment attaches to the whole feature and stays on the collapsed line:
FORMAT (ESCAPED) RETURN
f1 := a + 1
, -- after comma: feature note
f2 := 2
;| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := a + 1, -- after comma: feature note\n f2 := 2\n; |
Long comments are never wrapped — they may extend past the usual line width.
Leading comments (own line)
A comment on its own line attaches to the nearest following item, but only in a feature-defining clause — a clause that admits := (DEFINE, FOR, RETURN).
- In
DEFINE/FOR/RETURN, a leading comment before the first feature in that clause stays with that feature. - In
RETURN, a leading comment before a bare reference (no:=) attaches to that reference — even when aDEFINEfeature shares the same name.
Leading on the first feature in RETURN:
FORMAT (ESCAPED) RETURN
-- documents the first output
f1 := 1,
f2 := 2
;| OUTPUT VARCHAR |
|---|
| RETURN\n -- documents the first output\n f1 := 1,\n f2 := 2\n; |
Leading on a bare RETURN reference (not the homonymous DEFINE definition):
FORMAT (ESCAPED)
DEFINE
F1 := INPUT(BIGINT)
RETURN
-- attaches to the bare reference
F1
FOR
F1 := BIND_VALUE(1)
;| OUTPUT VARCHAR |
|---|
| DEFINE\n f1 := INPUT(BIGINT)\nRETURN\n -- attaches to the bare reference\n f1\nFOR\n f1 := BIND_VALUE(1)\n; |
Leading on a FOR binding:
FORMAT (ESCAPED)
DEFINE
INP := INPUT(BIGINT)
RETURN
INP
FOR
-- attaches to the binding
INP := BIND_VALUES(ARRAY(1, 2, 3))
;| OUTPUT VARCHAR |
|---|
| DEFINE\n inp := INPUT(BIGINT)\nRETURN\n inp\nFOR\n -- attaches to the binding\n inp := BIND_VALUES(ARRAY(1, 2, 3))\n; |
Non-feature clauses (WHERE, ORDER BY, LIMIT, …)
These clauses do not define features. A leading comment there has no anchor and becomes unrecognized. Trailing comments on expressions inside those clauses still attach to the leaf on their left.
Unrecognized leading comment before WHERE:
FORMAT (ESCAPED)
RETURN F1 := 1
-- leading in WHERE has no anchor
WHERE F1 > 0
;| OUTPUT VARCHAR |
|---|
| RETURN\n f1 := 1\nWHERE f1 > 0\n -- unrecognized comments\n -- leading in WHERE has no anchor\n; |
Unrecognized comments are emitted in a single block at the end of the formatted query, under -- unrecognized comments. A warning is also returned.
Pre-FORMAT text
Comments and text outside the query passed to FORMAT (for example a -- note above the FORMAT keyword in the original buffer) are discarded — they are not part of the query boundary.
See also
- Statement tools —
EXPLAIN,DESCRIBE,SHOW CREATE,SHOW DOCS,SHOW FUNCTIONS - Python library tools —
validate()usesFORMAT (FIX) - Friendly syntax — stylistic forms the formatter normalizes