Namespaces & aliases
Features live in namespaces — hierarchical paths like fm.user_analytics.daily_clicks that organize the registry and prevent naming conflicts. Aliases give you shorthand access to namespaces you reference often.
Persistent vs. local features
Features in FeatureQL are either persistent (stored in the registry with an fm. prefix) or local (existing only for the duration of a query). Local features are the default — define a feature without a namespace and it disappears when the query finishes:
SELECT
FEATURE1 := 1, -- local.feature1
FEATURE2 := 2, -- local.feature2
FEATURE3 := FEATURE1 + FEATURE2 -- local.feature3 := local.feature1 + local.feature2
;| FEATURE1 BIGINT | FEATURE2 BIGINT | FEATURE3 BIGINT |
|---|---|---|
| 1 | 2 | 3 |
Pulling features from a namespace
The FROM clause sets a namespace context. Unqualified feature names resolve against it, so you can reference persistent features without spelling out their full path.
First, let's create some features to work with:
CREATE OR REPLACE FEATURES AS
SELECT
10 AS FM.TUTORIALS.NAMESPACES.FEATURE1,
20 AS FM.TUTORIALS.NAMESPACES.FEATURE2,
100 AS FM.TUTORIALS.NAMESPACES.NS1.FEATURE1,
200 AS FM.TUTORIALS.NAMESPACES.NS1.FEATURE2,
1000 AS FM.TUTORIALS.NAMESPACES.NS2.FEATURE1,
2000 AS FM.TUTORIALS.NAMESPACES.NS2.FEATURE2,
;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.NAMESPACES.FEATURE1 | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.FEATURE2 | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS1.FEATURE1 | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS1.FEATURE2 | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS2.FEATURE1 | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS2.FEATURE2 | CREATED | Feature created as not exists |
Now reference them through the namespace:
SELECT
FEATUREA := FEATURE1 + 1, -- local.featurea := fm.tutorials.namespaces.feature1 + literal
FEATUREB := FEATURE1 + FEATURE2, -- local.featureb := fm.tutorials.namespaces.feature1 + fm.tutorials.namespaces.feature2
FROM FM.TUTORIALS.NAMESPACES
;| FEATUREA BIGINT | FEATUREB BIGINT |
|---|---|
| 11 | 30 |
You can also alias namespace features directly in the SELECT clause:
SELECT
FEATURE1 AS F1, -- fm.tutorials.namespaces.feature1
FEATURE2 AS F2, -- fm.tutorials.namespaces.feature2
FROM FM.TUTORIALS.NAMESPACES
;| F1 BIGINT | F2 BIGINT |
|---|---|
| 10 | 20 |
Mixing local and namespace features
Local definitions and namespace references work together in the same query. When a name collision occurs, local features win — a locally defined feature always takes precedence over a namespace feature with the same name.
SELECT
FEATUREA := FEATURE1 + 1, -- local.featurea := fm.tutorials.namespaces.feature1 + literal
FEATUREB := FEATURE1 + FEATURE2, -- local.featureb := fm.tutorials.namespaces.feature1 + fm.tutorials.namespaces.feature2
FEATUREC := FEATUREB + FEATURE1, -- local.featurec := local.featureb + fm.tutorials.namespaces.feature1
FROM FM.TUTORIALS.NAMESPACES
;| FEATUREA BIGINT | FEATUREB BIGINT | FEATUREC BIGINT |
|---|---|---|
| 11 | 30 | 40 |
Here, FEATURE1 is redefined locally as 1, so FEATUREB uses the local value instead of the namespace value (10):
SELECT
FEATURE1 := 1, -- local.feature1 := literal
FEATUREB := FEATURE1 + FEATURE2, -- local.featureb := local.feature1 + fm.tutorials.namespaces.feature2
FEATUREC := FEATUREB + FEATURE1, -- local.featurec := local.featureb + local.feature1
FROM FM.TUTORIALS.NAMESPACES
;| FEATURE1 BIGINT | FEATUREB BIGINT | FEATUREC BIGINT |
|---|---|---|
| 1 | 21 | 22 |
Aliases
When a query references multiple namespaces, aliases keep things readable. An alias is a short prefix (always starting with _) that maps to a namespace path.
SELECT
FEATURE1 := _a.FEATURE1, -- local.feature1 := fm.tutorials.namespaces.feature1
FEATURE2 := FEATURE1 + _a.NS1.FEATURE1 -- local.feature2 := local.feature1 + fm.tutorials.namespaces.ns1.feature1
FROM FM.TUTORIALS.NAMESPACES AS _a
;| FEATURE1 BIGINT | FEATURE2 BIGINT |
|---|---|
| 10 | 110 |
Multiple aliases
Use multiple aliases to pull features from different namespaces in the same query:
SELECT
FEATURE1 := _a.FEATURE1, -- local.feature1 := fm.tutorials.namespaces.feature1
FEATURE2 := FEATURE1 + _b.FEATURE1 -- local.feature2 := local.feature1 + fm.tutorials.namespaces.ns1.feature1
FROM FM.TUTORIALS.NAMESPACES AS _a, FM.TUTORIALS.NAMESPACES.NS1 AS _b
;| FEATURE1 BIGINT | FEATURE2 BIGINT |
|---|---|
| 10 | 110 |
Combining unnamed namespace, aliases, and local features
A query can have one unnamed namespace (from FROM), multiple aliases, and local features — all at once. FeatureQL resolves each reference using a clear priority: local first, then aliases, then the unnamed namespace.
SELECT
FEATUREA := FEATURE1 + _a.FEATURE1 + _b.FEATURE1, -- local.featurea := fm.tutorials.namespaces.feature1 + fm.tutorials.namespaces.ns1.feature1 + fm.tutorials.namespaces.ns2.feature1
FEATURE2 := 2, -- local.feature2 := literal
FEATUREB := FEATURE2 + _a.FEATURE2 + _b.FEATURE2, -- local.featureb := local.feature2 + fm.tutorials.namespaces.ns1.feature2 + fm.tutorials.namespaces.ns2.feature2
FROM
FM.TUTORIALS.NAMESPACES,
FM.TUTORIALS.NAMESPACES.NS1 AS _a,
FM.TUTORIALS.NAMESPACES.NS2 AS _b
;| FEATUREA BIGINT | FEATURE2 BIGINT | FEATUREB BIGINT |
|---|---|---|
| 1110 | 2 | 2202 |
Creating persistent features
CREATE FEATURES IN stores new features in a specific namespace:
CREATE FEATURES IN FM.TUTORIALS.NAMESPACES AS
SELECT
CREATED_FEATURE1 := 11, -- fm.tutorials.namespaces.created_feature1 := literal
CREATED_FEATURE2 := CREATED_FEATURE1 + 10 -- fm.tutorials.namespaces.created_feature2 := fm.tutorials.namespaces.created_feature1 + literal
;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.NAMESPACES.CREATED_FEATURE2 | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.CREATED_FEATURE1 | CREATED | Feature created as not exists |
You can combine CREATE FEATURES IN with FROM and aliases to build derived features that reference multiple existing namespaces:
CREATE FEATURES IN FM.TUTORIALS.NAMESPACES.NS3 AS
SELECT
FEATUREA := _0.FEATURE1 + _a.FEATURE1 + _b.FEATURE1, -- fm.tutorials.namespaces.ns3.featurea := fm.tutorials.namespaces.feature1 + fm.tutorials.namespaces.ns1.feature1 + fm.tutorials.namespaces.ns2.feature1
FEATURE2 := 2, -- fm.tutorials.namespaces.ns3.feature2 := literal
FEATUREB := _0.FEATURE2 + _a.FEATURE2 + _0.NS2.FEATURE2, -- fm.tutorials.namespaces.ns3.featureb := fm.tutorials.namespaces.feature2 + fm.tutorials.namespaces.ns1.feature2 + fm.tutorials.namespaces.ns2.feature2
FEATUREC := FEATURE2 + FEATUREA + FEATUREB -- fm.tutorials.namespaces.ns3.featurec := fm.tutorials.namespaces.ns3.feature2 + fm.tutorials.namespaces.ns3.featurea + fm.tutorials.namespaces.ns3.featureb
FROM
FM.TUTORIALS.NAMESPACES AS _0,
FM.TUTORIALS.NAMESPACES.NS1 AS _a,
FM.TUTORIALS.NAMESPACES.NS2 AS _b
;| feature_name VARCHAR | status VARCHAR | message VARCHAR |
|---|---|---|
| FM.TUTORIALS.NAMESPACES.NS3.FEATUREA | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS3.FEATUREB | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS3.FEATUREC | CREATED | Feature created as not exists |
| FM.TUTORIALS.NAMESPACES.NS3.FEATURE2 | CREATED | Feature created as not exists |
USE statements
The USE statement sets a default namespace for all subsequent queries in a multi-statement session, replacing the need for an unnamed namespace in FROM:
USE FM.TUTORIALS.NAMESPACES;
SELECT
FEATUREA := FEATURE1 + _a.FEATURE1 + _b.FEATURE1, -- local.featurea := fm.tutorials.namespaces.feature1 + fm.tutorials.namespaces.ns1.feature1 + fm.tutorials.namespaces.ns2.feature1
FEATURE2 := 2, -- local.feature2 := literal
FEATUREB := FEATURE2 + _a.FEATURE2 + _b.FEATURE2, -- local.featureb := local.feature2 + fm.tutorials.namespaces.ns1.feature2 + fm.tutorials.namespaces.ns2.feature2
FROM
FM.TUTORIALS.NAMESPACES.NS1 AS _a,
FM.TUTORIALS.NAMESPACES.NS2 AS _b
;| FEATUREA BIGINT | FEATURE2 BIGINT | FEATUREB BIGINT |
|---|---|---|
| 1110 | 2 | 2202 |
USE also works with WITH clauses, aliases, and qualified references. Features defined in WITH are always local, regardless of any CREATE FEATURES IN context:
USE FM.TUTORIALS.NAMESPACES;
WITH
FEATUREA := FEATURE1 + _a.FEATURE1 + _b.FEATURE1, -- local.featurea := fm.tutorials.namespaces.feature1 + fm.tutorials.namespaces.ns1.feature1 + fm.tutorials.namespaces.ns2.feature1
FEATURE2 := 2, -- local.feature2 := literal (WITH always creates local)
FEATUREB := FEATURE2 + _a.FEATURE2 + NS2.FEATURE2, -- local.featureb := local.feature2 + fm.tutorials.namespaces.ns1.feature2 + fm.tutorials.namespaces.ns2.feature2
SELECT
FEATUREA, -- local.featurea
FEATUREB, -- local.featureb
FEATUREC := FEATURE2 + FEATUREA + FEATUREB -- local.featurec := local.feature2 + local.featurea + local.featureb
FROM
FM.TUTORIALS.NAMESPACES.NS1 AS _a,
FM.TUTORIALS.NAMESPACES.NS2 AS _b
;| FEATUREA BIGINT | FEATUREB BIGINT | FEATUREC BIGINT |
|---|---|---|
| 1110 | 2202 | 3314 |
Name resolution rules
FeatureQL resolves feature names in a predictable order:
- Validation — Names cannot start with
_(reserved for aliases). No duplicate definitions. Only one unnamed namespace per statement. - Fully qualified names — Names starting with
fm.are never modified. - Alias resolution —
_alias.FEATUREexpands to the full namespace path. - Created features — New features go to the
CREATE FEATURES INnamespace, or stay local. - Reference resolution — Check local features first, then the unnamed namespace from
FROM.
Error handling
FeatureQL catches namespace mistakes early with clear error messages.
Feature names cannot start with _ (that's alias syntax):
SELECT _BADFEATURE := 1; -- ERROR: feature names cannot start with underscoreReferencing an undefined alias fails immediately:
SELECT FEATURE1 := _unknown.SOME_FEATURE; -- ERROR: _unknown alias not definedMultiple unnamed namespaces are ambiguous — FeatureQL rejects them when you reference features that need resolution:
SELECT FEATURE1 FROM FM.TUTORIALS.NS1, FM.TUTORIALS.NS2; -- ERROR: ambiguous FEATURE1 resolution (???.feature1)The same rule applies when USE conflicts with an unnamed FROM namespace:
USE FM.TUTORIALS.NAMESPACES;
SELECT
FEATUREA := FEATURE1 + FEATURE2, -- ERROR: ambiguous namespace resolution (???.featurea := ???.feature1 + ???.feature2)
FROM FM.TUTORIALS.NAMESPACES.NS2
;Namespaces must start with fm.:
CREATE FEATURES IN invalid.namespace AS SELECT FEATURE1 := 1; -- ERROR: namespace must start with fm.If all features in a multi-namespace query are locally defined (not referencing the namespace), the ambiguity doesn't trigger — there's nothing to resolve:
SELECT FEATURE1:=1 FROM FM.TUTORIALS.NS1, FM.TUTORIALS.NS2; -- local.feature1 := literal (works because creating local)| FEATURE1 BIGINT |
|---|
| 1 |
Best practices
- Use descriptive hierarchies:
fm.user_analytics.daily_metrics.click_ratebeatsfm.feature1. Organize by domain, team, or project. - Choose meaningful aliases:
_users,_products,_txns— not_a,_b,_c. - Be explicit in complex queries: When multiple namespaces are in play, fully qualified names prevent surprises.
- Group related features: Keep features that belong together in the same namespace subtree.