> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wirekite.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Install the Sumatra toolchain, connect to a Firebolt database, and write and run your first two programs.

This page takes you from an empty Linux host to two working Sumatra
programs against your own Firebolt database: first the smallest possible
program (pure set-DML), then the canonical read → compute → write shape
that most real programs use.

## Prerequisites

* A **Linux x86-64** host. This is the only packaged platform today.
* A **Firebolt** database you can write to, plus a service account
  (client id and secret) and your account name. Firebolt is the first
  supported backend — and the only one in the current release.
* A C++ toolchain. Sumatra compiles each program into a small native
  binary **on your host**, so it needs `g++` (C++17), `pkg-config`, and
  the libcurl development headers:

<CodeGroup>
  ```bash Debian / Ubuntu theme={null}
  sudo apt-get install g++ pkg-config libcurl4-openssl-dev
  ```

  ```bash RHEL / Fedora theme={null}
  sudo dnf install gcc-c++ pkgconf-pkg-config libcurl-devel
  ```
</CodeGroup>

<Note>
  The toolchain is only exercised when something actually compiles. A host
  that only runs already-compiled stored procedures from its cache never
  needs `g++` installed.
</Note>

## Install

Sumatra ships as a single tarball, `sumatra-<version>-linux-amd64.tar.gz`.
Unpack it and run it in place:

```bash theme={null}
tar xzf sumatra-0.2.0-linux-amd64.tar.gz
cd sumatra-0.2.0-linux-amd64
./sumatra --version
# sumatra 0.2.0
```

To put `sumatra` on your PATH, **symlink** it — do not copy the binary
out of the directory:

```bash theme={null}
mkdir -p ~/.local/bin
ln -s "$PWD/sumatra" ~/.local/bin/sumatra
```

<Warning>
  The `runtime/` and `exec/` directories must stay next to the real
  `sumatra` binary — they hold the headers and the I/O layer that every
  compiled program is built with. The tool resolves symlinks to find them,
  which is why a symlink works and a copied binary does not.
</Warning>

## Create a DSN file

The shell connects with a **DSN file**: a plain text file containing one
connection string. Create `my.dsn`:

```text my.dsn theme={null}
firebolt:///<database>?account_name=<account>&client_id=<id>&client_secret=<secret>&engine=<engine>
```

* `<database>` — the Firebolt database to run against. Required: programs
  and stored procedures live per-database.
* `account_name`, `client_id`, `client_secret` — your Firebolt account
  and service-account credentials.
* `engine` — optional; omit it to use the system engine.

The file holds a credential, so protect it:

```bash theme={null}
chmod 600 my.dsn
```

## Create a table to work on

Run this in your Firebolt database (SQL workspace or any client) — the
same table the programs in [Learning by example](/sumatra/examples)
build on:

```sql theme={null}
CREATE TABLE sumatra_sessions (
  session_id BIGINT NOT NULL,
  state      TEXT,
  last_seen  TIMESTAMP
) PRIMARY INDEX session_id;

INSERT INTO sumatra_sessions VALUES
  (1, 'active',  CURRENT_TIMESTAMP - INTERVAL '30' HOUR),
  (2, 'active',  CURRENT_TIMESTAMP - INTERVAL '1' HOUR),
  (3, 'expired', CURRENT_TIMESTAMP - INTERVAL '72' HOUR);
```

## Your first program

Sumatra source lives in files, conventionally with a `.sumatra`
extension. Create `expire.sumatra`:

```sql expire.sumatra theme={null}
-- Expire sessions that have been idle for more than 24 hours.

BEGIN
  UPDATE sumatra_sessions
    SET state = 'expired'
    WHERE state = 'active'
      AND last_seen < CURRENT_TIMESTAMP - INTERVAL '24' HOUR;

  COMMIT;
END;
```

Reading it line by line:

* `--` starts a comment, to the end of the line.
* `BEGIN ... END;` brackets the program body. There are no variables
  here, so no `DECLARE` section is needed.
* The `UPDATE` is plain SQL in Firebolt's own dialect. A write that is
  already set-shaped stays set-shaped — Sumatra runs it server-side as-is
  and adds no machinery around it.
* `COMMIT;` ends the transaction. Commits are always explicit in
  Sumatra — where you place `COMMIT` defines exactly what becomes
  durable together.
* Every statement ends with `;`, including the final `END;`.

## Run it

Point the shell at your DSN file and submit the file with `@`:

```text theme={null}
$ sumatra --dsn my.dsn
sumatra shell — @prog.sumatra [args…] · EXEC name(lits…) · DROP PROCEDURE name · help · exit
sumatra> @expire.sumatra
Completed (exit 0)
sumatra> exit
```

Or skip the prompt entirely — the one-shot form runs a single statement
and exits with its outcome, which is the form you use from scripts and
cron:

```bash theme={null}
sumatra --dsn my.dsn @expire.sumatra
echo $?   # 0
```

Behind the scenes, the shell just did all of this:

1. Compiled `expire.sumatra` against the **live catalog** of the
   connected database — the database itself is the schema; there are no
   schema files to maintain.
2. Generated C++ for the program and built it with `g++` in a temporary
   directory. (The first build in a session also checks that `g++`,
   `pkg-config`, and libcurl headers are present, and fails loudly with
   an install hint if not.)
3. Ran the binary, which connected using the same DSN file and executed
   the transaction.
4. Discarded the binary. A file submitted with `@` is compile-and-run,
   never stored — [stored procedures](/sumatra/procedures) are the
   persistent form.

Verify the result in Firebolt: session 1 flipped to `expired`, session 2
is untouched.

```sql theme={null}
SELECT session_id, state FROM sumatra_sessions ORDER BY session_id;
-- 1  expired
-- 2  active
-- 3  expired
```

## A program with per-row logic

Set-DML is the trivial case. Sumatra earns its keep when the logic is
procedural — when each row needs branching, state, or arithmetic that is
awkward to express in one SQL statement. Create an orders table and a
few rows:

```sql theme={null}
CREATE TABLE sumatra_orders (
  order_id    BIGINT NOT NULL,
  customer_id INT,
  amount      NUMERIC(12,2),
  status      TEXT,
  order_date  DATE,
  region      TEXT
) PRIMARY INDEX order_id;

INSERT INTO sumatra_orders VALUES
  (101, 1, 120.00, 'pending', DATE '2026-07-20', 'US'),
  (102, 2, 850.00, 'pending', DATE '2026-07-21', 'DE'),
  (103, 1,  35.50, 'pending', DATE '2026-07-22', 'US'),
  (104, 3, 640.00, 'shipped', DATE '2026-07-19', 'FR');
```

Now a program that classifies every pending order, `prioritize.sumatra`:

```sql prioritize.sumatra theme={null}
-- Classify pending orders by size.

BEGIN
  FOR batch IN (SELECT order_id, customer_id, amount, status, order_date, region
                FROM sumatra_orders
                WHERE status = 'pending')
      ORDER KEY (order_id)
      BATCH SIZE 10000
  LOOP
    FOR rec IN batch LOOP
      IF rec.amount >= 500 THEN
        rec.status := 'priority';
      ELSE
        rec.status := 'standard';
      END IF;

      UPDATE sumatra_orders SET ROW = rec WHERE order_id = rec.order_id;
    END LOOP;
    COMMIT;
  END LOOP;
END;
```

The new pieces:

* `FOR batch IN (SELECT ...)` opens a **read block**. The parenthesized
  query is ordinary Firebolt SQL — filter, join, aggregate as you like.
  `batch` is a name you choose.
* `ORDER KEY (order_id)` declares a strict total order for the read.
  It is required whenever the read is fetched in chunks, so results are
  deterministic and a re-run walks rows in the same order.
* `BATCH SIZE 10000` fetches the result in chunks of up to 10,000 rows.
  Each batch is processed, written, and committed as a unit.
* `FOR rec IN batch LOOP` iterates the current batch row by row, in
  memory. `rec`'s fields are the columns of your SELECT, and they are
  assignable.
* `:=` is Sumatra assignment (plain `=` is SQL's comparison/SET, used
  only inside SQL statements).
* `UPDATE ... SET ROW = rec WHERE order_id = rec.order_id;` writes the
  whole record back — every column except the key it matches on. You
  never write `MERGE` or staging tables yourself; the compiler batches
  the captured row writes and emits one set-based statement per batch.
* `COMMIT;` inside the batch loop makes each batch a durable checkpoint
  as soon as it is written.

Run it and check:

```bash theme={null}
sumatra --dsn my.dsn @prioritize.sumatra
```

```sql theme={null}
SELECT order_id, amount, status FROM sumatra_orders ORDER BY order_id;
-- 101  120.00  standard
-- 102  850.00  priority
-- 103   35.50  standard
-- 104  640.00  shipped     <- untouched: filtered out by the read
```

## Exit codes

Every program run finishes in exactly one of three ways:

| Exit code | Meaning                                                                                                                          |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `0`       | Completed — the program ran to the end (or a clean `RETURN;`).                                                                   |
| `2`       | Stopped — a fault was caught by an exception handler whose action was `STOP`. Batches committed before the fault remain durable. |
| `1`       | Error — an unhandled fault, a `RAISE`, a compile error, or an infrastructure failure.                                            |

The distinction between `2` and `1` is deliberate: `2` means the program
anticipated the fault and shut down gracefully with a valid committed
prefix; `1` means something needs a human. Scripts and schedulers can
branch on it directly.

## Where next

* [Programs and batches](/sumatra/programs) — the full anatomy of a
  program: read blocks, write routing, and commit grains.
* [Learning by example](/sumatra/examples) — small annotated programs,
  from a running balance to exception handling.
* [Stored procedures](/sumatra/procedures) — name a program, store it in
  the database, and run it with `EXEC` from anywhere.
* [The sumatra shell](/sumatra/shell) — the complete command-line
  reference.
