Getting started
A short walk through wiring up a connection and running your first query.
Install
Add saga_pgo to your project.toml:
[deps]
saga_pgo = { git = "https://github.com/dylantf/saga_pgo" }Then saga install to fetch it.
Configure a connection
A Config describes how to reach the database. default_config fills
in sensible defaults (localhost, port 5432, postgres user, no
password) and you override what you need with a record update:
import SagaPgo (default_config, connect)
let config =
{ default_config "mydb" |
user: "postgres",
password: "postgres",
port: 5432,
}
let conn = connect configconnect starts a pgo pool and gives you back a Connection
handle. The handle is what you pass to every query, execute, and
transaction call. By default, the pool name is the database name.
Each concurrently running pool needs a distinct pool_name. This lets a primary
and replica target the same PostgreSQL database without colliding:
let primary_config =
{ default_config "mydb" |
pool_name: "mydb_primary",
host: "primary.internal",
}
let replica_config =
{ default_config "mydb" |
pool_name: "mydb_replica",
host: "replica.internal",
}
let primary = connect primary_config
let replica = connect replica_configPool names become registered Erlang atoms. Treat them as a bounded set of names from application configuration; do not generate an unbounded name per request.
Handlers
The Postgres effect describes how SQL gets executed. The library
ships a real handler — pg — that talks to pgo. Install it at the
top of your effectful block with with:
import SagaPgo (pg, query)
import SagaPgo.Types as T
main () = {
let conn = connect config
let _ = query conn "INSERT INTO users (name) VALUES ($1)" [T.pg_text "Alice"]
println "inserted"
} with {pg, console}For transactions, also install pg_transaction — see the
transactions guide.
Two ways to run a query
The low-level primitive is query. It takes a SQL string and a flat
list of Value parameters:
query conn "SELECT id FROM users WHERE name = $1" [T.pg_text "Alice"]For anything more than a one-liner, prefer the builder. It tracks placeholder numbering for you and composes cleanly:
sql "SELECT id, name FROM users"
|> push "WHERE name ="
|> push_bind (T.pg_text "Alice")
|> execute connBoth return Result (Returned Dynamic) QueryError. Returned carries
the row count and a list of rows; each row is Dynamic, so you
decode columns out of it positionally — see the types and decoding
guide.
What's next
- Queries — the builder in depth, including bulk inserts
with
push_values. - Transactions — wrapping work in a transaction and the rules around it.
- Types and decoding — encoding parameters and decoding columns back into Saga values.