Persisted views
CREATE VIEW compiles a relational FeatureQL query for DuckDB, Trino, and serving, then stores the SQL artifacts as a read-only relation named FMV.schema.table. SQL clients scan that relation. FeatureQL does not. The three artifacts are meant to compute the same FeatureQL result; Same answer across backends lists what the compiler rewrites and what still differs by engine.
Create a view
The name is exactly three identifiers and must start with FMV.. The body is any relational FeatureQL query — literals, persisted FM. features, KEYSETs, and physical tables. @(FEATURES IN …) is one body form, not a special path.
CREATE OR REPLACE VIEW FMV.DOCS.ORDERS AS
DEFINE
ORDER_ID := 101,
AMOUNT := 10
RETURN
ORDER_ID,
AMOUNT
;| message VARCHAR |
|---|
| ok |
The authored FeatureQL is stored. Metaprogramming (@(FEATURES IN …), @BIND_KEYSET) freezes only at create, replace, or refresh. Current table data is still read at scan time.
Scan it as SQL
On :8101, mark the request /* SQL */. JDBC and Flight SQL are already SQL.
SELECT
ORDER_ID,
AMOUNT
FROM fmv.docs.orders
;| ORDER_ID BIGINT | AMOUNT BIGINT |
|---|---|
| 101 | 10 |
A persisted view is not a FeatureQL source. FROM FMV.…, KEYSET SQL that names fmv.…, and EXTERNAL_COLUMNS / BIND_COLUMNS that name fmv.… raise UE/VIEW-NOT-A-FEATUREQL-SOURCE. Compose two views in SQL after substitution (/* SQL */ SELECT … FROM fmv.a JOIN fmv.b).
RETURN
amount
FROM FMV.DOCS.ORDERS
;Refresh and drop
REFRESH VIEW FMV.schema.table recompiles the stored body. It never materializes rows. Use it after you change a feature that the compiled snapshot depends on, or after @(FEATURES IN …) should pick up a newly persisted match.
DROP VIEW / DROP VIEW IF EXISTS removes the view feature. Failed replace or refresh leaves the prior artifacts in place.
Warnings
Create, replace, and refresh fail on compiler warnings unless the view body contains the usual ACK-XXXX comment — the same token FeatureQL prints as (acknowledge with ACK-XXXX). Acknowledgments never suppress errors. REFRESH VIEW recompiles that stored body, so the same comment still applies.
CREATE OR REPLACE VIEW FMV.DOCS.WARNED AS
-- ACK-MW54
DEFINE
X := 1e0
RETURN
FLAG := X > 0e0
;| message VARCHAR |
|---|
| ok |
See Tools in the query for how ACK-XXXX works on ordinary queries.
Inspect
DESCRIBE is the ordinary statement. FUNCTION is "VIEW". View-only fields are VIEW_* so they do not collide with feature PARAMS.
DESCRIBE (FIELDS (NAME, FUNCTION, VIEW_COLUMNS, VIEW_BACKENDS))
FMV.DOCS.ORDERS
;| JMESPATH VARCHAR | EXPECTED_VALUE VARCHAR |
|---|---|
| [0].name | "FMV.DOCS.ORDERS" |
| [0].function | "VIEW" |
| [0].view_columns | ["ORDER_ID", "AMOUNT"] |
| [0].view_backends | ["duckdb", "trino", "serving"] |
SHOW FEATURES IN FMV.schema lists the view as a normal persisted feature.
Protection, briefly
Protection uses the compiled dependency snapshot, not the live graph and not authored @(). Drop or type-incompatible replace of a protected feature is refused (UE/VIEW-FEATURE-IN-USE, UE/VIEW-INCOMPATIBLE-FEATURE). Type-compatible replace is allowed; stored SQL stays until refresh. Bound INPUT / KEYSET / ENTITY stay in use (UE/VIEW-BINDING-IN-USE, UE/VIEW-KEYSET-IN-USE, UE/VIEW-ENTITY-IN-USE). An incompatible KEYSET entity or arity is UE/VIEW-INCOMPATIBLE-KEYSET.
Next
Point a SQL tool at the JDBC catalog , or read the full VIEW statement syntax . For live FeatureQL inside SQL instead of a frozen table, use hybrid queries .