A PostgreSQL driver for Koja, speaking the v3 wire protocol over TCP. No C dependencies beyond the Koja stdlib.
- Trust, cleartext password, and SCRAM-SHA-256 authentication
- Simple query protocol (
query) for plain SQL - Extended query protocol (
execute) with$1-style text parameters - Structured server errors carrying severity, SQLSTATE code, and message
Add the package as a path dependency in your koja.toml:
[dependencies]
Postgres = { path = "../postgres" }alias Postgres.Config
alias Postgres.Connection
config = Config.new("127.0.0.1", 5432, "app_user", "app_db")
.with_password("secret")
conn =
match Connection.connect(config)
Result.Ok(c) -> c
Result.Err(e) -> return Result.Err(e.message())
end
# Simple query. The connection is a value: rebind the returned copy.
outcome = conn.query("SELECT id, name FROM users")
conn = outcome.first
match outcome.second
Result.Ok(result) ->
# result.fields: List<String>
# result.rows: List<List<Option<String>>> (None = SQL NULL)
# result.tag: command tag, e.g. "SELECT 2"
result.rows.print()
Result.Err(e) ->
IO.puts(e.message())
end
# Parameterized query via the extended protocol. Parameters are text;
# cast them in SQL. Option.None binds SQL NULL.
params: List<Option<String>> = [Option.Some("42")]
outcome = conn.execute("SELECT name FROM users WHERE id = $1::int", params)
conn = outcome.first
_ = conn.close()
All result values are text-format strings as sent by the server
(Option.None for NULL). Interpret them with to_int() / to_float()
as needed.
Every failure is a Postgres.Error:
| Variant | Meaning |
|---|---|
ConnectFailed(String) |
TCP connection could not be established |
AuthenticationFailed(String) |
Password missing, SCRAM proof/verification error |
UnsupportedAuthentication |
Server demands a method the driver lacks (MD5) |
Server(ServerError) |
Server-reported error with SQLSTATE + severity |
Protocol(String) |
Malformed or unexpected wire data |
IO(String) |
Socket read/write failure |
ConnectionClosed |
Server closed the connection |
error.message() renders any variant as a human-readable string.
- Typed row decoding (values are text)
- TLS (
sslmode) — connect over trusted networks or a local socket proxy - MD5 password authentication
- Connection pooling
- Binary parameter/result formats
- SASLprep normalization of exotic Unicode passwords
Unit tests are pure; integration tests expect the bundled Postgres:
docker compose up -d
koja testThe container (Postgres 16 on host port 5434, database koja_test)
provisions one user per auth path: koja_trust (trust), koja_password
(cleartext), and koja_scram (SCRAM-SHA-256).
Copyright (c) 2026 Henry Popp
This project is MIT licensed. See the LICENSE for details.