///|
async fn _quick_start() -> Unit {
@async.with_task_group(group => {
let config = @client.Config::new(
"localhost",
user="postgres",
database="app",
password="secret",
port=5432,
ssl_mode=VerifyFull,
application_name="my-service",
)
let (client, connection) = @client.connect(config)
group.spawn_bg(() => connection.run())
let current_user : String = client
.query_one("select current_user::text as current_user")
.get_name("current_user")
ignore(current_user)
client.close()
})
}| Need | API | Use it when |
|---|---|---|
| Exactly one row | Client::query_one | A lookup must succeed once and only once, such as fetching by primary key or reading one aggregate row |
| Zero or one row | Client::query_opt | The row may be absent, such as optional profile/settings records |
| Many rows or incremental consumption | Client::query | You want a RowStream and may stop early or process rows as they arrive |
| Affected row count | Client::execute | insert, update, delete, or other parameterized commands where row data is not needed |
| Raw SQL batch with no parameters | Client::batch_execute | Schema setup, BEGIN / COMMIT, temp tables, session settings, or other statement batches |
| Reuse SQL many times | Client::prepare + Client::query_statement / Client::execute_raw | The same SQL text is executed repeatedly and you want PostgreSQL to keep a named prepared statement |
| Fetch rows in chunks | Client::bind + Client::query_portal | Large result sets where you want explicit fetch windows instead of collecting everything |
| Multiple statements with atomicity | Client::transaction or Client::with_transaction | Business logic must commit or roll back as one unit |
| PostgreSQL simple protocol | Client::simple_query | Multiple statements in one SQL string, or direct access to text-format frames |
| Bulk import / export | Client::copy_in / Client::copy_out | High-volume streaming I/O with PostgreSQL COPY |
///|
fn _config_example() -> @client.Config {
@client.Config::new(
"localhost",
user="postgres",
database="app",
password="secret",
port=5432,
ssl_mode=VerifyFull,
application_name="my-service",
)
}///|
fn _tls_examples() -> (@client.Config, @client.Config) {
let direct = @client.Config::new(
"db.example",
user="postgres",
database="app",
password="secret",
ssl_mode=VerifyFull,
application_name="my-service",
)
let routed = @client.Config::new(
"db.example",
hostaddr="10.0.0.15",
user="postgres",
database="app",
password="secret",
ssl_mode=VerifyFull,
ssl_root_cert="/etc/postgres/root.crt",
application_name="my-service",
)
(direct, routed)
}///|
fn _channel_binding_example() -> @client.Config {
@client.Config::new(
"db.example",
user="postgres",
database="app",
password="secret",
ssl_mode=VerifyFull,
channel_binding=Prefer,
application_name="my-service",
)
}///|
async fn _connection_run_example(config : @client.Config) -> Unit {
@async.with_task_group(group => {
let (client, connection) = @client.connect(config)
group.spawn_bg(() => connection.run(on_async=msg => ignore(msg)))
let version = client.parameter("server_version")
let pending_message = connection.next_message()
ignore(version)
ignore(pending_message)
let token = client.cancel_token()
ignore(token.process_id())
ignore(token.secret_key())
client.close()
})
}///|
async fn _listen_notify_example(config : @client.Config) -> Unit {
@async.with_task_group(group => {
let (listener, listener_connection) = @client.connect(config)
let (publisher, publisher_connection) = @client.connect(config)
group.spawn_bg(() => listener_connection.run())
group.spawn_bg(() => publisher_connection.run())
listener.batch_execute("LISTEN jobs")
publisher.batch_execute("NOTIFY jobs, 'ready'")
let message = listener_connection.next_message()
ignore(message)
publisher.close()
listener.close()
})
}| MoonBit type | PostgreSQL types |
|---|---|
| Bool | bool |
| Int | int2, int4 |
| Int64 | int8 |
| UInt | oid |
| Float | float4 |
| Double | float8 |
| String | text, varchar, name, and other text-like types |
| Bytes | bytea, uuid, and raw byte-oriented formats |
| Json | json, jsonb |
| T? | NULL on encode, optional decode on read |
| Array[T] / Array[T?] | one-dimensional arrays of supported built-in element types |
///|
async fn _parameter_example(client : @client.Client) -> Unit {
let name = "moonbit"
let params : Array[&@client.ToSql] = [name as &@client.ToSql]
let row = client.query_one("select $1::text as value", params~)
let value : String = row.get_name("value")
ignore(value)
}///|
async fn _streaming_query_example(client : @client.Client) -> Unit {
let stream = client.query("select generate_series(1, 3)::int4 as value")
guard stream.next() is Some(row) else { return }
let first_value : Int = row.get_name("value")
let summary = stream.finish()
ignore(first_value)
ignore(summary.row_count)
ignore(summary.command_tag)
}///|
struct EmailText {
value : String
} derive(Debug, Eq)
///|
impl @client.ToSql for EmailText with fn format(_, _) {
Text
}
///|
impl @client.ToSql for EmailText with fn accepts(_, type_) {
type_.oid == @client.Type::text().oid ||
type_.oid == @client.Type::varchar().oid
}
///|
impl @client.ToSql for EmailText with fn moonbit_type_name(_) {
"EmailText"
}
///|
impl @client.ToSql for EmailText with fn to_sql(self, _, buf) {
buf.write_bytes(@proto.utf8_encode(self.value))
No
}
///|
impl @client.FromSql for EmailText with fn accepts(type_) {
type_.oid == @client.Type::text().oid ||
type_.oid == @client.Type::varchar().oid
}
///|
impl @client.FromSql for EmailText with fn moonbit_type_name() {
"EmailText"
}
///|
impl @client.FromSql for EmailText with fn from_sql(_, _, raw) {
{ value: @utf8.decode(raw), }
}
///|
async fn _custom_codec_example(client : @client.Client) -> Unit {
let email : EmailText = { value: "moonbit@example.com", }
let params : Array[&@client.ToSql] = [email as &@client.ToSql]
let row = client.query_one("select $1::text as email", params~)
let decoded : EmailText = row.get_name("email")
ignore(decoded)
}///|
async fn _query_one_example(client : @client.Client) -> Unit {
let row = client.query_one("select count(*)::int8 as count from pg_class")
let count : Int64 = row.get_name("count")
ignore(count)
}///|
async fn _execute_example(client : @client.Client) -> Unit {
let id = 42
let params : Array[&@client.ToSql] = [id as &@client.ToSql]
let affected = client.execute(
"delete from items where id = $1::int4",
params~,
)
ignore(affected)
}///|
async fn _transaction_example(client : @client.Client) -> Unit {
let options = @client.TransactionOptions::new(
isolation_level="SERIALIZABLE",
read_only=true,
deferrable=true,
)
let tx = client.transaction(options~)
let nested = tx.transaction()
nested.batch_execute("select 1")
nested.rollback()
tx.commit()
}///|
async fn _prepare_example(client : @client.Client) -> Unit {
let statement = client.prepare("select $1::int4 as value")
let value = 42
let params : Array[&@client.ToSql] = [value as &@client.ToSql]
let row = client.query_statement(statement, params~).collect()[0]
let decoded : Int = row.get_name("value")
ignore(decoded)
statement.close()
}///|
async fn _prepare_typed_example(client : @client.Client) -> Unit {
let statement = client.prepare_typed("select $1 as value", [
@client.Type::text(),
])
let value = "moonbit"
let params : Array[&@client.ToSql] = [value as &@client.ToSql]
let row = client.query_statement(statement, params~).collect()[0]
let decoded : String = row.get_name("value")
ignore(decoded)
statement.close()
}///|
async fn _portal_example(client : @client.Client) -> Unit {
let statement = client.prepare("select generate_series(1, 5)::int4 as value")
let portal = client.bind(statement)
let stream = client.query_portal(portal, 2)
let first_row = stream.next()
let summary = stream.finish()
ignore(first_row)
ignore(summary.suspended)
portal.close()
statement.close()
}///|
async fn _copy_in_example(client : @client.Client) -> Unit {
let sink = client.copy_in(
"COPY items(id, name) FROM STDIN WITH (FORMAT text)",
)
sink.send(b"1\tmoonbit\n"[:])
let inserted = sink.finish()
ignore(inserted)
}///|
async fn _copy_out_example(client : @client.Client) -> Unit {
let stream = client.copy_out(
"COPY (select 'moonbit'::text as name) TO STDOUT",
)
let chunk = stream.next()
ignore(chunk)
stream.finish()
}///|
async fn _simple_query_example(client : @client.Client) -> Unit {
let stream = client.simple_query(
"select 'hello'::text as greeting; select 1::text as number",
)
let first = stream.next()
let rest = stream.collect()
ignore(first)
ignore(rest)
}pub(open) trait FromSql {
fn from_sql(Type, WireFormat, BytesView) -> Self raise
fn accepts(Type) -> Bool
fn moonbit_type_name() -> String
fn from_sql_null(Type, WireFormat) -> Self raise = _
}pub suberror ClientError {
Database(DatabaseError)
Authentication(String)
Closed(String)
Protocol(String)
Ssl(String)
Encode(String)
Decode(String)
WrongType(WrongTypeError)
ColumnNotFound(String)
RowCount(String)
UnexpectedMessage(String)
} derive(Eq, Debug)pub enum AsyncMessage {
Notice(DatabaseError)
Notification(Notification)
ParameterStatus(String, String)
} derive(Eq, Debug)type CancelTokentype Clientasync fn[T] Client::with_transaction(self : Client, f : async (Transaction) -> T, options? : TransactionOptions) -> Tpub struct Column {
name : String
table_oid : UInt
column_id : Int
type_ : Type
type_size : Int
type_modifier : Int
format : WireFormat
} derive(Eq, Debug)pub struct Config {
host : String
hostaddr : String?
port : Int
user : String
database : String
password : String?
ssl_mode : SslMode
ssl_root_cert : String?
channel_binding : ChannelBinding
application_name : String
options : String?
connect_timeout_ms : Int?
keepalives : Bool?
keepalives_idle_s : Int?
} derive(Eq, Debug)fn Config::from_parts(host : String, hostaddr : String?, port : Int, user : String, database : String, password : String?, ssl_mode : SslMode, ssl_root_cert : String?, channel_binding : ChannelBinding, application_name : String, options : String?, connect_timeout_ms : Int?, keepalives : Bool?, keepalives_idle_s : Int?) -> Configfn Config::new(host : String, hostaddr? : String, port? : Int, user~ : String, database? : String, password? : String, ssl_mode? : SslMode, ssl_root_cert? : String, channel_binding? : ChannelBinding, application_name~ : String, options? : String, connect_timeout_ms? : Int, keepalives? : Bool, keepalives_idle_s? : Int) -> Configtype Connectiontype CopyInSinkpub enum SimpleQueryMessage {
RowDescription(Array[String])
Row(SimpleQueryRow)
CommandComplete(String)
} derive(Eq, Debug)type SimpleQueryStreamtype Transactionasync fn Transaction::bind(self : Transaction, statement : Statement, params? : Array[&ToSql]) -> Portalasync fn Transaction::execute_raw(self : Transaction, statement : Statement, params? : Array[&ToSql]) -> Intasync fn Transaction::prepare_typed(self : Transaction, sql : String, types : Array[Type]) -> Statementfn Transaction::query_portal(self : Transaction, portal : Portal, max_rows : Int) -> RowStream raisefn Transaction::query_statement(self : Transaction, statement : Statement, params? : Array[&ToSql]) -> RowStream raisefn TransactionOptions::new(isolation_level? : String, read_only? : Bool, deferrable? : Bool) -> TransactionOptionsInstall
Download zipA secure, easy-to-use PostgreSQL client library for MoonBit with an included connection pool.
Dependencies