Friendly syntax
FeatureQL adds several conveniences on top of standard SQL that make queries shorter and easier to maintain. None of these change what you can compute — they just make it more pleasant to write.
Automatic dependency resolution
In SQL, you must define things before you reference them. In FeatureQL, order doesn't matter — reference a feature before it's defined and the engine resolves the dependency graph for you:
SELECT
NUMBER1 + NUMBER2 as NUMBER3, -- Automatic Dependency Resolution: you can use features not yet defined
1 AS NUMBER1,
2 AS NUMBER2| NUMBER3 BIGINT | NUMBER1 BIGINT | NUMBER2 BIGINT |
|---|---|---|
| 3 | 1 | 2 |
Function chaining
Instead of nesting function calls like LOWER(TRIM(' HELLO ')), you can chain them left-to-right using dot notation. The rule: expr.FUNC(args) becomes FUNC(expr, args) — the expression before the dot is inserted as the first argument.
SELECT
(1+2).MULTIPLY(3), -- = MULTIPLY((1+2), 3)
(1).ADD(2).MULTIPLY(3), -- = MULTIPLY(ADD(1, 2), 3)
(1).ADD(2).MULTIPLY(3)::DOUBLE.SQRT()::BIGINT, -- You can also use casting
' HELLO '.TRIM().LOWER() -- = LOWER(TRIM(' HELLO '))| ?_0 BIGINT | ?_1 BIGINT | ?_2 BIGINT | ?_3 VARCHAR |
|---|---|---|---|
| 9 | 9 | 3 | hello |
Chains read left-to-right: (1).ADD(2).MULTIPLY(3) means MULTIPLY(ADD(1, 2), 3). When chaining after a type cast, wrap the type in parentheses — ::(DOUBLE).SQRT() — otherwise ::DOUBLE.SQRT() would be ambiguous because DOUBLE.SQRT() looks like a namespaced reference.
Chaining on namespaced features
Features can have dotted names like FM.MYFEATURES.FEATURE1. This is never ambiguous: the last segment before the opening parenthesis is always the function call, so FM.MYFEATURES.FEATURE1.ADD(2) can only mean "call ADD on feature FM.MYFEATURES.FEATURE1":
SELECT
FEATURE1 := 1,
FEATURE1.ADD(2), -- = ADD(FEATURE1, 2)
FM.MYFEATURES.FEATURE1 := 1,
FM.MYFEATURES.FEATURE1.ADD(2) -- = ADD(FM.MYFEATURES.FEATURE1, 2)
;| FEATURE1 BIGINT | ?_1 BIGINT | FM.MYFEATURES.FEATURE1 BIGINT | ?_3 BIGINT |
|---|---|---|---|
| 1 | 3 | 1 | 3 |
Chaining with namespaced functions
When the function itself has a dotted name (like macro FM.FUNCTIONS.AREA), wrap it in double-quotes so FeatureQL can tell where the feature ends and the function begins. Without quotes, SOME.LENGTH.FM.FUNCTIONS.AREA(SOME.WIDTH) is genuinely ambiguous — it could be feature SOME.LENGTH.FM.FUNCTIONS calling AREA, or feature SOME.LENGTH calling FM.FUNCTIONS.AREA. Quoting resolves it: SOME.LENGTH."FM.FUNCTIONS.AREA"(SOME.WIDTH).
WITH
PARAM1 := INPUT(BIGINT),
PARAM2 := INPUT(BIGINT),
FM.FUNCTIONS.AREA := MACRO(PARAM1*PARAM2 USING INPUTS PARAM1, PARAM2)
SELECT
SOME.LENGTH := 2,
SOME.WIDTH := 3,
"FM.FUNCTIONS.AREA"(SOME.LENGTH, SOME.WIDTH), -- Standard call
SOME.LENGTH."FM.FUNCTIONS.AREA"(SOME.WIDTH) -- Chained: SOME.LENGTH becomes first arg
;| SOME.LENGTH BIGINT | SOME.WIDTH BIGINT | ?_2 BIGINT | ?_3 BIGINT |
|---|---|---|---|
| 2 | 3 | 6 | 6 |
Syntax flexibility
Alternative assignment syntax
FeatureQL preferred syntax is := for assignment but the SQL-style expr AS name works, too.
The := form puts the name first, which many people find easier to scan:
SELECT
-- Traditionnal SQL way
1 AS NUMBER1,
NUMBER1 + 1 AS NUMBER2,
-- Reversed way
NUMBER3 := 1,
NUMBER4 := NUMBER3 + 1,| NUMBER1 BIGINT | NUMBER2 BIGINT | NUMBER3 BIGINT | NUMBER4 BIGINT |
|---|---|---|---|
| 1 | 2 | 1 | 2 |
Flexible formatting
Trailing commas, arbitrary whitespace, and multi-line expressions are all valid:
select
number1 := 1, -- Arbitrary whitespaces
number2 := nuMbeR1 + 1, -- Case insensitive
number3 := ' 2 '
.TRIM()
::BIGINT
.ADD(1), -- Chaining on multi-lines
array_of_ints := ARRAY[
1,
2,
3, -- Trailing comma in arrays and rows
],
number4 := number2 + 2, -- Trailing comma in feature list| NUMBER1 BIGINT | NUMBER2 BIGINT | NUMBER3 BIGINT | ARRAY_OF_INTS ARRAY | NUMBER4 BIGINT |
|---|---|---|---|---|
| 1 | 2 | 3 | [1, 2, 3] | 4 |
Trailing commas mean you can reorder features without worrying about which one is last. Multi-line chaining (.TRIM() on one line, ::BIGINT on the next) keeps complex expressions readable.
Function aliases
SQL dialects use different names for the same operation. FeatureQL accepts common aliases — ARRAY and LIST, SLICE and ARRAY_SLICE, POWER and POW — so you can use the names you already know:
SELECT
ARRAY[1,2,3,4,5,6] as ARRAY_1,
ARRAY(1,2,3,4,5,6) as ARRAY_2, -- For Array and Row, you can use brackets or parentheses
SLICE(ARRAY_1, 2, 3) as SLICED_1,
LIST_SLICE(ARRAY_1, 2, 3) as SLICED_2, -- You can use functions aliases
ARRAY_SLICE(ARRAY_2, 2, 3) as SLICED_3| ARRAY_1 ARRAY | ARRAY_2 ARRAY | SLICED_1 ARRAY | SLICED_2 ARRAY | SLICED_3 ARRAY |
|---|---|---|---|---|
| [1, 2, 3, 4, 5, 6] | [1, 2, 3, 4, 5, 6] | [2, 3, 4] | [2, 3, 4] | [2, 3, 4] |
Beyond name aliases, constructors also accept both brackets and parentheses: ARRAY[1,2,3] and ARRAY(1,2,3) are equivalent, as are ROW(...) and ROW[...].
FeatureQL normalizes aliases to a single canonical name when features are saved, so stored definitions stay consistent regardless of which alias you used.