Types
Prefer the spellings
DOUBLE and REAL. Firebolt’s own FLOAT
keyword is a 64-bit alias, so writing FLOAT invites confusion about
width; the corpus convention is DOUBLE for 64-bit and REAL for
32-bit.INSERT ... SELECT FROM batch). Only the
scalar types above the line participate in compute — in-memory
expressions and variables. BYTEA and ARRAY cannot enter a row
loop’s in-memory record, and arithmetic on a carry-only type is a
compile error, not a silent coercion.
Literals
- Integer literals are typed by magnitude: a value that fits in 32 bits
is
INT, otherwiseBIGINT. Beyond 64 bits, write it in decimal form (9300000000000000000.0). Scientific notation is not accepted. - A literal with a decimal point is
NUMERICwith the written scale (4.00is NUMERIC scale 2). - Strings are single-quoted:
'shipped'. - Booleans are
TRUEandFALSE. - Dates and timestamps:
DATE '2026-07-01',TIMESTAMP '2026-07-01 12:30:00'. - A NULL that needs a type is written
CAST(NULL AS T).
Variables and assignment
Assignment is:=; plain = is SQL’s comparison and SET operator,
used only inside SQL statements.
2.5 → 2, 3.5 → 4).
Operators
Precedence is conventional (
OR < AND < NOT < comparison <
additive < multiplicative); parenthesize anything you would parenthesize
in SQL.
A CASE expression selects a value and works anywhere a value
does:
Numeric semantics
Sumatra’s arithmetic is the bound engine’s arithmetic, bit for bit — today, Firebolt’s:- Integers are width-checked at every
+ - *and every cast:INTmath staysINTand faults withOVERFLOWpast 32 bits; mixing widths promotes (INT * BIGINTisBIGINT). - Decimals follow the engine’s strict typing: the result scale of
+ - * /is the widest operand scale, precision grows to fit and is capped at 38, and exceeding it is a loud overflow. Multiplication and division are computed in a wide intermediate and rounded half-up to that scale. - Floats are IEEE 754 with no fused-multiply-add surprises. Mixing
DOUBLEwithNUMERICor integers promotes toDOUBLE. A result that would be NaN or Infinity faults instead. - Division by zero faults with
ZERO_DIVIDE— never NULL, never Infinity.
CASE arms, GREATEST, and
LEAST must share a type family. Mixing TEXT with a numeric or
temporal operand is a compile error asking for an explicit CAST,
where the engine alone would coerce at runtime and fault only on bad
data.
Casts that cross into or out of TEXT are not available in compute
position — do text parsing and formatting in the read’s SQL, where the
engine’s full conversion machinery applies.
NULL semantics
Sumatra uses SQL’s three-valued logic, unchanged:- NULL propagates through operators and functions:
NULL + 1is NULL,NULL || 'x'is NULL, comparisons with NULL are NULL. - Test with
IS NULL, or compare null-safely withIS [NOT] DISTINCT FROM. - A condition that evaluates to NULL is not TRUE: an
IFtakes its ELSE path, and a capture filter skips the row.
ORDER KEY columns (a
NULL key is a loud runtime error), keyed-collection keys (NULL never
matches; a NULL-key probe raises NO_DATA_FOUND), and merge-key
matching (a NULL key component never matches a target row).
Control flow
CONTINUE WHEN pred;(or bareCONTINUE;) skips to the next iteration.EXIT WHEN pred;(or bareEXIT;) leaves the innermost loop.RETURN;ends the whole program cleanly (exit 0) from anywhere; writes persist only up to the lastCOMMITthat was reached.
END LOOP; — batch reads, row loops, counted
loops, and WHILE alike. Compute loops are in-memory constructs: a
read block or a COMMIT inside one is a compile error by design.
Records
Declare a record type explicitly, or derive one from a table:rec in FOR rec IN batch) are records too —
their fields are the SELECT’s columns, and they are assignable.
Collections
Sumatra’s collection is the keyed map: a whole table (or filtered subset) loaded into memory once and probed by key — the lookup-table pattern.INDEX BYnames the key column(s) of the element type — composite keys are supported (INDEX BY wh, sku), and the probe key can be a runtime-computed value, the one correlation no JOIN can express.- A probe on a missing key (or a NULL key) raises
NO_DATA_FOUND— handleable like any fault. - The map is a read-only lookup: there is no iteration over a
collection, and no
.COUNT-style methods. It is resident in memory, so load dimensions, not fact tables. - Key columns may be integer, decimal, text, date, or timestamp — not float.
SELECT ... INTO fetches
scalars directly; zero rows raises NO_DATA_FOUND, and more than one
row is a loud error:
Identifiers and names
- Identifiers are ASCII: letters, digits, underscore, not starting with a digit. Case is not significant; the convention is UPPERCASE keywords and lowercase identifiers.
- Table names are single bare identifiers — there is no
schema.tablequalification. A table or column whose name needs quoting in the engine can be reached by aliasing it inside the read’s SQL. - A few words are reserved and cannot name variables, types, or
parameters:
MERGE,FUNCTION,PROCEDURE,RETURN,BULK COLLECT,INDEX BY,COLLAPSE,SCAN,FOLD,MAP,FILTER.
