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:

FeatureQL
CREATE OR REPLACE FEATURE FM.TUTORIALS.CAD.FEATURE1 AS 1 + 5;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.CAD.FEATURE1CREATEDFeature created as not exists

Confirm the stored name, type, and formula with SHOW FEATURES:

FeatureQL
SHOW FEATURES (COLUMNS (NAME, DATATYPE, FORMULA)) LIKE 'FM.TUTORIALS.CAD.FEATURE1';
Result
NAME VARCHARDATATYPE VARCHARFORMULA VARCHAR
FM.TUTORIALS.CAD.FEATURE1BIGINT1 + 5

The feature is now available to later queries:

FeatureQL
RETURN
    fm.tutorials.cad.feature1
;
Result
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:

FeatureQL
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
;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.CAD.FEATURE2CREATEDFeature created as not exists
FM.TUTORIALS.CAD.FEATURE3CREATEDFeature created as not exists
FM.TUTORIALS.CAD.FEATURE4CREATEDFeature 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:

FeatureQL
CREATE OR REPLACE FEATURES IN FM.TUTORIALS.CAD AS
PERSIST
    feature5 := 5
;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.CAD.FEATURE5CREATEDFeature 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:

FeatureQL
CREATE OR REPLACE FEATURES AS
PERSIST
    fm.tutorials.cad.feature3 := fm.tutorials.cad.feature1 + 10
;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.CAD.FEATURE3REPLACEDFeature 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:

FeatureQL
ALTER FEATURE FM.TUTORIALS.CAD.FEATURE5
SET description = 'Feature containing the literal 5';
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.CAD.FEATURE5ALTEREDFeature 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 .