A pure MoonBit PostgreSQL client, wire protocol from scratch.
moon add jaredzhou/moonpglet conn = @moonpg.connect("postgres://user:pw@localhost:5432/db")
// Simple query
let rows = conn.query("SELECT id, name FROM users")
while rows.has_next() {
let row = rows.get_row()
let id : Int = row.get(0)
let name : String = row.get_by_name("name")
println("\{id}: \{name}")
}
rows.close()
// Parameterised — extended query protocol
conn.execute(
"UPDATE users SET email = $1 WHERE id = $2",
params=[None, 42], // Option[T] encodes NULL
) |> ignore
conn.close()let pool = Pool::new(PoolConfig::new(
"postgres://user:pw@localhost:5432/db",
max_conns=10,
min_idle=2,
))
// Implicit acquire — rows.close() returns conn to pool
let rows = pool.query("SELECT 1")
while rows.has_next() {
let row = rows.get_row()
// ...
}
rows.close()
let row = pool.query_one("SELECT 42")
pool.execute("INSERT INTO t (x) VALUES ($1)", params=[1]) |> ignore
// Explicit acquire — caller controls release
let pc = pool.acquire()
let r = pc.execute("INSERT INTO t (x) VALUES ($1)", params=[99])
r.close()
pc.release()// Auto-commit / rollback
///|
let result = begin_func(conn, async fn(tx) {
tx.execute("INSERT INTO users (name) VALUES ($1)", params=["alice"]) |> ignore
tx.query_one("SELECT id FROM users WHERE name = $1", params=["alice"]).get(0)
}) // exception → rollback; success → commit///|
let id : Int = row.get(0) // raises on NULL
///|
let email : String? = row.get_by_name("email") // NULL → None
///|
let ts : Timestamp = row.get(2) // pg timestamp → Unix µs| user | password | auth method |
|---|---|---|
| moonpg_plain | plain_pass | password |
| moonpg_md5 | md5_pass | md5 |
| moonpg_scram | scram_pass | scram-sha-256 |
# Use env var or defaults (see conn_test.mbt)
MOONPG_CONNINFO="postgres://moonpg_plain:plain_pass@localhost:5432/moonpg_test" \
moon test --target nativewhile rows.has_next() {
let row = rows.get_row()
let v : Int = row.get(0)
}
rows.close()pub(open) trait Tx : QueryExecutor {
async fn commit(Self) -> Unit raise PgError
async fn rollback(Self) -> Unit raise PgError
}impl QueryExecutor for Connectionasync fn execute(self : Connection, sql : String, params? : Array[&ToValue]) -> ExecResult raise PgErrorimpl TxBeginner for Connectionasync fn Connection::copy_in(self : Connection, sql : String, rows : Array[String]) -> ExecResult raise PgErrorpub(all) enum IsolationLevel {
ReadCommitted
RepeatableRead
Serializable
}pub(all) struct PoolConfig {
conninfo : String
max_conns : Int
min_idle : Int
max_idle_sec : Int
max_lifetime_sec : Int
health_check : Bool
}let pool = Pool::new(PoolConfig::new("postgres://...", max_conns=10))
// Option A: implicit acquire — Pool auto-manages lifecycle
let rows = pool.query("SELECT 1")
while rows.has_next() { let row = rows.get_row() }
rows.close() // PoolRows.close() returns conn to pool
let row = pool.query_one("SELECT 42")
pool.execute("INSERT ...") |> ignore
let tx = pool.begin_tx()
tx.commit() // PoolDbTx.commit() returns conn to pool
// Option B: explicit acquire — caller manages lifecycle
let pc = pool.acquire()
defer pc.release()
let rows = pc.query("SELECT 1")
rows.close() // PoolRows.close() returns conn to pool
let row = pc.query_one("SELECT 42")
pc.release() // caller releasesfn PoolConfig::new(conninfo : String, max_conns? : Int, min_idle? : Int, max_idle_sec? : Int, max_lifetime_sec? : Int, health_check? : Bool) -> PoolConfigimpl QueryExecutor for PoolDbTxasync fn execute(self : PoolDbTx, sql : String, params? : Array[&ToValue]) -> ExecResult raise PgErrorpub(all) struct Timestamp(Int64)pub enum Value {
Null
Bool(Bool)
Int(Int)
Int64(Int64)
Float(Double)
String(String)
Bytes(Bytes)
}let new_id = begin_func(conn, fn(tx) {
let row = tx.query_one("INSERT INTO users (name) VALUES ($1) RETURNING id", params=["alice"])
row.get(0)
})Install
Download zipA pure MoonBit PostgreSQL client, wire protocol from scratch.