Skip to main content
Sumatra’s design stance is that a limitation should be a loud compile-time rejection with a named reason, not a silent performance cliff or a wrong value discovered in production. This page collects the boundaries: permanent ones (rejected on principle), release ones (not in 0.2.0), and operational ones (true today, worth planning around).

Permanent, by design

These are not roadmap items — they define the execution model.
  • No per-row server operations. A per-row server query or write — the cursor-style pattern from OLTP databases — is inexpressible by grammar. All server interaction is batched.
  • No nested read blocks, and no read inside a compute loop. Decorrelate with a JOIN in the read, or load the inner table as a keyed collection and probe it.
  • No unbounded server-dependent loops. A WHILE that needs fresh server data each iteration to decide termination does not fit the read → compute → write model and is rejected.
  • ORDER KEY columns must be INT, BIGINT, or TIMESTAMP. Floats are excluded on principle (float equality is unsound as a cursor); DATE is excluded because keyset pagination over a one-value-per-day key cannot make progress within a day.
  • No text-crossing casts in compute. TEXT → number or number → TEXT conversions belong in the read’s SQL, where the engine’s full conversion machinery (and error surface) applies.
  • A write cannot change its own merge key. SET of a key column, or a SET ROW matched on anything other than the record’s own key field, is a compile error.
  • One change per key per batch. Duplicate writes to one merge key in a flush unit are a program error, surfaced loudly (repeated DELETEs excepted).
  • Bare table names only. No schema.table qualification, and engine identifiers must be usable unquoted — reach exotic names by aliasing them inside the read’s SQL. Identifiers are ASCII.

Not in this release

Absent from 0.2.0; some are roadmapped, none are promised here:
  • User-defined functions (FUNCTION / RETURN expr are reserved words today).
  • User-defined exceptions — the catchable set is ZERO_DIVIDE, OVERFLOW, NO_DATA_FOUND, OTHERS.
  • Iterating or aggregating a collection. Keyed maps are probe-only lookups: no FOR x IN map, no .COUNT, no in-memory SUM over a map. Aggregate in the read instead.
  • BYTEA and ARRAY in compute. Both types carry fine through server-side statements; neither can enter an in-memory record.
  • Regex, format masks, and transcendental math in compute position (REGEXP_*, TO_CHAR/TO_DATE, POWER/EXP/LN/trig — SQRT is the exception). Use them in the read’s SQL.
  • Loop labels (EXIT always exits the innermost loop), procedure calling procedure, and the != spelling (use <>).
  • EXEC literal escaping — a text argument containing a single quote cannot be passed.

PL/SQL features deliberately not carried over

For readers arriving from Oracle: explicit cursors and cursor attributes (%FOUND, %NOTFOUND, %ROWCOUNT), FORALL, GOTO, packages, overloading, autonomous transactions, SAVEPOINT, explicit ROLLBACK (a fault’s rollback is automatic; graceful stops are the EXCEPTION block’s job), dynamic SQL (EXECUTE IMMEDIATE), %TYPE, CONSTANT, and declaration DEFAULT/NOT NULL modifiers. VARRAY, nested tables, and MULTISET are replaced by the single keyed-map collection.

Operational boundaries

  • Firebolt is the only live backend in this release. The toolchain is engine-neutral by construction — the DSN scheme declares the backend, and everything engine-specific lives behind per-backend profiles — but firebolt is the only scheme that binds today.
  • Linux x86-64 only, and compiling hosts need g++, pkg-config, and libcurl headers. (Hosts that only run cached stored procedures need none of that.)
  • Compiling requires a live connection — the connected database is the schema. The offline sumatra-compile tool works only against its built-in sample schema.
  • BATCH SIZE ALL on a compute path holds the whole result in client memory. A result that does not fit is a loud fault, not a spill. Chunk with BATCH SIZE N where results are large.
  • Aggregating reads re-evaluate per chunk under BATCH SIZE N — use BATCH SIZE ALL for GROUP BY sources.
  • The sumatra_ prefix is reserved in target databases: the toolchain creates and drops sumatra_stage_* and sumatra_stage_lock_* tables and owns sumatra_source. It also reserves one staging column name on its own staging tables. There is no collision guard — keep your own objects off the prefix.
  • DSN files are plaintext — protect them with file permissions (chmod 600). There is no encrypted-DSN mechanism in this release.
  • Concurrent identical runs are refused. The same procedure with the same argument values will not start twice at once (a stale lock from a crashed run clears itself after a timeout).
  • The shell is one statement per line — no multi-line input, no SQL passthrough.
  • Shared host cache across accounts. Two same-named databases on different Firebolt accounts share one per-host binary-cache directory; prefer distinct database names.
  • One exotic cursor edge: a row whose ORDER KEY value equals the exact type minimum (INT/BIGINT minimum, or timestamp 0001-01-01 00:00:00) is never fetched by a chunked read. Real keys never live there; synthetic sentinel keys should not.

Reading an error you did not expect

Compile-time rejections name the rule that fired and, where one exists, the supported alternative — the error text is the documentation of first resort. If a program you believe should compile is rejected, check this page’s permanent list first, then Programs and batches for the structural rules (commit placement, ORDER KEY requirements, write shapes).