Skip to content

Reads

Read full models by scan, predicate, or primary key.

Updated View as Markdown

Integrated reads are expressions. Every executed read returns Result[T, QueryError]; postfix ? unwraps the success value and propagates a query failure.

bookings := FROM Booking?

The query execution call may suspend. Its error and suspension effects propagate through the enclosing function just like other typed host operations.

Result shapes

Query Unwrapped success type
FROM Booking []Booking
ONE FROM Booking Booking?
GET Booking(id) Booking?
GET Booking(ids) Map[int, Booking]
projected FROM list of projection rows
projected ONE FROM optional projection row
... AS FRAME Frame[Row]

Without ?, wrap each success type in Result[..., QueryError].

The distinction between “no row” and “query failed” is preserved:

match GET Booking(booking_id) {
    Ok(Some(booking)) => use(booking)
    Ok(None) => report_missing(booking_id)
    Err(error) => report_query_error(error)
}

Do not convert both cases into one empty sentinel when the caller needs to respond differently.

Scans

FROM loads every matching row:

recent := FROM Booking
    WHERE created_at >= cutoff
    ORDER BY created_at DESC
    LIMIT 25?

ONE FROM requests at most one row and returns None when no row matches:

booking := ONE FROM Booking
    WHERE reference == wanted?

Ordering is important when several rows could match.

Without ORDER BY, the engine can choose any matching row. Use ONE FROM only when uniqueness is guaranteed by a key/constraint or when an explicit order defines which row is wanted.

Primary keys

GET is primary-key lookup sugar:

booking := GET Booking(booking_id)?

Batch lookup accepts a list and returns a map. Missing keys are absent:

by_id := GET Booking(ids)?
match by_id.get(wanted_id) {
    Some(booking) => use(booking)
    None => report_missing(wanted_id)
}

For a composite primary key, pass each component positionally. A batch uses a list of key tuples or compatible records.

Batch results are keyed for lookup, not source-order preservation. If output must follow the input key order, iterate the original key list and query the result map. Duplicate input keys correspond to one map entry.

Query boundaries

The query begins at its verb and consumes following query clauses. The postfix ? applies to the entire query:

rows := FROM Booking WHERE active?
count := rows.size()

Parenthesize when continuing with normal Atoll member calls or embedding a query in a larger expression.

Query clauses belong to the parser’s integrated query expression, so formatting one clause per line makes its boundary and final ? much easier to review.

Parameters

Source locals used inside a query become prepared parameters:

fn for_customer(customer_id: int): []Booking ! QueryError {
    return FROM Booking
        WHERE Booking.customer_id == customer_id?
}

Qualifying the field makes a same-named local unambiguous. Parameters retain their Atoll types and are encoded through the SQL ABI; they are never pasted into SQL text.

Failures

QueryError provides portable classifications and helpers such as is_constraint(), is_conflict(), is_retryable(), and is_not_found(). The runtime does not generally retry application transactions; retry only when your operation is safe to repeat.

QueryError.Engine retains a portable SqlErrorCode, the engine’s native code, and a human message. Encoding reports a typed result-carrier problem. Backend reports missing or malformed runtime execution support. Branch on portable codes first and reserve native codes for engine-specific policy.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close