A datasource binds a schema to an engine and connection. The operational
configuration lives in atoll.toml:
[datasources.PRIMARY]
schema = "seaport"
engine = "PostgreSQL"
url = "${DATABASE_URL}"
pool = { min = 5, max = 20 }
[datasources.REPLICA]
schema = "seaport"
engine = "PostgreSQL"
url = "${REPLICA_URL}"
readOnly = trueEnvironment substitutions use ${NAME}. A missing or malformed substitution
is a configuration error.
The loader substitutes datasource URLs while parsing atoll.toml. Pool bounds
are validated, and an empty URL after substitution is rejected.
Engines
The configuration parser recognizes PostgreSQL, MySQL, SQLite, Turso,
DataFusion, and DuckDB. Dialect and executor coverage differs:
- PostgreSQL, MySQL, SQLite, Turso, and DataFusion have distinct render paths.
- DuckDB currently falls back to SQLite rendering and is not a complete dedicated executor contract.
- advanced functions, indexes, arrays, locking, and search features are engine-gated.
When no datasource is configured, query lowering uses the SQLite dialect. With exactly one datasource, that engine becomes the fallback for models with no matching schema. With several datasources, unmatched schemas fall back to SQLite rendering and should be treated as a configuration mistake in a deployable application.
Route prefixes
Place the datasource name between a binding and :=:
bookings REPLICA := FROM Booking
WHERE active?The parser records this prefix on the binding. The current semantic query path,
however, selects dialect and read-only policy from the queried model’s schema;
it does not yet consume the binding’s datasource name. Do not rely on
REPLICA to override routing today.
This syntax remains provisional until named-route validation and runtime selection are wired end to end.
Default routes
Queries use the first configured route whose schema equals the queried
model’s schema. Writes require that matched route to be writable. A write to a
model served by a readOnly route is rejected at compile time.
saved := SAVE Booking {
id: booking_id
reference
}?Route identity also controls transactions: every database operation in one transaction must resolve to the same route, even when model schemas differ.
Two unmatched schemas share the default route identity. Two separately matched schema entries do not, even if both use the same engine and URL.
Source declarations
Old examples use:
const PRIMARY = datasource(PostgreSQL, "...")That parses as an ordinary constant expression but is not wired into current
query routing. Keep credentials and deployment topology in atoll.toml.
Prepared statements
Routing selects a dialect before query lowering. The compiler renders fixed SQL and registers statement identifiers; guest values are bound through the SQL ABI. Runtime data does not choose arbitrary SQL text.
Read-only policy
readOnly is a compile-time write gate for integrated SQL. It is not a
database credential and does not protect raw host integrations outside the
query compiler. Configure database permissions as defense in depth.
Reads, including locking reads where the dialect permits them, still pass through feature-specific checks. A read-only route cannot participate in a write transaction merely because its engine supports transactions.