Create features
Persisting a feature publishes its definition and dependency graph to the registry, where other queries and teams can discover and reuse it.
Before creating features, choose a namespace that communicates ownership or business domain. Namespaces and aliases explains the naming rules.
Create one feature
Use singular CREATE FEATURE when one expression produces one persisted feature:
CREATE [OR REPLACE] FEATURE
<fully_qualified_name>
AS <expression>;
CREATE TEMPORARY FEATURE
<feature_name>
AS <expression>;
The smallest useful form gives the feature a fully qualified FM. name:
CREATE OR REPLACE FEATURE FM.TUTORIALS.CAD.FEATURE1 AS 1 + 5;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.CAD.FEATURE1 | CREATED | Feature created as not exists |
Confirm the stored name, type, and formula with SHOW FEATURES:
SHOW FEATURES (COLUMNS (NAME, DATATYPE, FORMULA)) LIKE 'FM.TUTORIALS.CAD.FEATURE1';| NAME VARCHAR | DATATYPE VARCHAR | FORMULA VARCHAR |
|---|---|---|
| FM.TUTORIALS.CAD.FEATURE1 | BIGINT | 1 + 5 |
The feature is now available to later queries:
RETURN
fm.tutorials.cad.feature1
;| FM.TUTORIALS.CAD.FEATURE1 BIGINT |
|---|
| 6 |
Create a related set
Use plural CREATE FEATURES with PERSIST when several definitions belong together:
CREATE [OR REPLACE] FEATURES
[IN <namespace>]
AS PERSIST
<feature definitions>;
CREATE TEMPORARY FEATURES
[IN <namespace>]
AS PERSIST
<feature definitions>;
Features in the same statement can reference one another in any order. FeatureQL resolves the dependency graph before writing the definitions:
CREATE OR REPLACE FEATURES AS
PERSIST
fm.tutorials.cad.feature2 := INPUT(BIGINT),
fm.tutorials.cad.feature3 := fm.tutorials.cad.feature2 + 1,
fm.tutorials.cad.feature4 := CASE
WHEN fm.tutorials.cad.feature3 > 5 THEN 'BIG'
ELSE 'SMALL'
END
;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.CAD.FEATURE2 | CREATED | Feature created as not exists |
| FM.TUTORIALS.CAD.FEATURE3 | CREATED | Feature created as not exists |
| FM.TUTORIALS.CAD.FEATURE4 | CREATED | Feature created as not exists |
Definitions in PERSIST are addressable from later queries. Definitions in an optional DEFINE clause are persisted as internal dependencies: they remain inspectable but cannot be referenced directly by consumers.
Put every feature in one namespace
CREATE FEATURES IN sets one target namespace, so each selected feature can use a short name:
CREATE OR REPLACE FEATURES IN FM.TUTORIALS.CAD AS
PERSIST
feature5 := 5
;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.CAD.FEATURE5 | CREATED | Feature created as not exists |
Choose how existing names are handled
Plain CREATE is the safest default for a new name because it fails rather than changing an existing definition.
Use CREATE OR REPLACE for an idempotent deployment that may update an existing definition:
CREATE OR REPLACE FEATURES AS
PERSIST
fm.tutorials.cad.feature3 := fm.tutorials.cad.feature1 + 10
;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.CAD.FEATURE3 | REPLACED | Feature was replaced |
Before applying a replacement, FeatureQL checks that the resulting registry remains valid. A change that would break a dependent feature is rejected without modifying the registry. See Registry consistency .
IF NOT EXISTS is not supported. To leave an existing definition untouched, use plain CREATE — it fails if the name is already present.
Keep definitions request-local
CREATE TEMPORARY FEATURE and CREATE TEMPORARY FEATURES add names to this request only. They do not write the registry, and they do not last for a SQL-style session. The output clause is still PERSIST. Use them to try a definition, then inspect it with Statement tools .
Unqualified CREATE TEMPORARY names stay unqualified unless you wrote USE. A sibling SELECT … FROM FM.… does not move them into that namespace.
Coordinate several persist statements
One persist statement is already atomic. Wrap several persist statements when a migration must leave either the old graph or the complete new graph:
START TRANSACTION;
DROP FEATURES IF EXISTS
FM.SALES.OLD_NET_REVENUE;
CREATE OR REPLACE FEATURES IN FM.SALES AS
PERSIST
NET_REVENUE := GROSS_REVENUE - REFUNDS;
COMMIT;
The response is the net applied diff, not a per-statement log. CREATE A; DROP A of a new name writes nothing. DROP A; CREATE A of an existing name is one REPLACED. A failure rolls the whole request back; ROLLBACK is not a statement.
The wrapper is one HTTP request, not a DuckDB or warehouse transaction. Persist mutations and TEMPORARY mutations cannot share a script. On the managed registry, START TRANSACTION 'add weekly revenue' stores that string as the revision message — Catalog revisions . To try a change without writing HEAD, see Branch overlays .
See Drop features and Registry consistency . Syntax and the other two script shapes are on Transactions .
Change metadata without changing the formula
ALTER FEATURE changes catalog metadata without replacing the computation:
ALTER FEATURE FM.TUTORIALS.CAD.FEATURE5
SET description = 'Feature containing the literal 5';| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.CAD.FEATURE5 | ALTERED | Feature successfully altered |
For descriptions, custom attributes, display formats, and field metadata, continue with Feature metadata .
Next
Use Explore features to inspect what you published and understand its dependencies. For every modifier and accepted body form, see the CREATE FEATURE(S) syntax reference . The transaction wrapper itself is on Transactions . To compile a FeatureQL query into a SQL table for BI tools, see Persisted views .