///|
#external
type Connection
///|
#external
type QueryResult
///|
#external
type PreparedStatement
///|
///|
pub enum Value {
Null
Bool(Bool)
Int(Int)
Int64(Int64)
Float(Float)
String(String)
Bytes(Bytes)
}
///|
pub enum PgError {
ConnectionError(String)
AuthError(String)
QueryError(String)
ProtocolError(String)
}
///|
pub fn PgError::to_string(self : PgError) -> String {
match self {
ConnectionError(s) => "ConnectionError(" + s + ")"
AuthError(s) => "AuthError(" + s + ")"
QueryError(s) => "QueryError(" + s + ")"
ProtocolError(s) => "ProtocolError(" + s + ")"
}
}
///|
pub trait ToValue {
to_value(Self) -> Value
}
///|
pub impl ToValue for Int with to_value(n) {
Value::Int(n)
}
///|
pub impl ToValue for String with to_value(s) {
Value::String(s)
}
///|
pub impl ToValue for Bool with to_value(b) {
Value::Bool(b)
}
///|
pub impl ToValue for Int64 with to_value(n) {
Value::Int64(n)
}
///|
pub impl ToValue for Float with to_value(f) {
Value::Float(f)
}
///|
pub impl ToValue for Bytes with to_value(b) {
Value::Bytes(b)
}
///|
pub impl ToValue for Value with to_value(v) {
v
}
///|
pub fn connect(conninfo : String) -> Result[Connection, PgError] {
let conn = pg_connect_internal(string_to_c_bytes(conninfo))
let err = pg_error_message(conn)
if err.length() > 0 {
pg_close_internal(conn)
Err(PgError::ConnectionError(err))
} else {
Ok(conn)
}
}
///|
fn string_to_c_bytes(s : String) -> Bytes {
let arr = Array::make(0, (0 : Byte))
for i = 0; i < s.length(); i = i + 1 {
let c = s[i]
let hi = (c >> 8).to_byte()
let lo = c.to_byte()
if hi != (0 : Byte) {
arr.push(hi)
}
arr.push(lo)
}
arr.push((0 : Byte))
Bytes::from_array(arr)
}
///|
pub fn Connection::query(
self : Connection,
sql : String,
) -> Result[QueryResult, PgError] {
let result = pg_query_internal(self, string_to_c_bytes(sql))
if pg_result_is_null(result) > 0 {
let err = pg_error_message(self)
Err(PgError::QueryError(err))
} else {
Ok(result)
}
}
///|
pub fn[T : ToValue] Connection::execute(
self : Connection,
sql : String,
params : Array[T],
) -> Result[QueryResult, PgError] {
let values = params.map(fn(p) { p.to_value() })
let param_count = values.length()
let result = pg_execute_internal(
self,
string_to_c_bytes(sql),
values,
param_count,
)
if pg_result_is_null(result) > 0 {
let err = pg_error_message(self)
Err(PgError::QueryError(err))
} else {
Ok(result)
}
}
///|
pub fn QueryResult::rows(self : QueryResult) -> Array[Array[String]] {
let n_rows = pg_ntuples(self)
let n_cols = pg_nfields(self)
let row_arrays = Array::make(n_rows, Array::make(n_cols, ""))
for i = 0; i < n_rows; i = i + 1 {
let row_arr = Array::make(n_cols, "")
for j = 0; j < n_cols; j = j + 1 {
let val = pg_getvalue(self, i, j)
ignore(row_arr.set(j, val))
}
ignore(row_arrays.set(i, row_arr))
}
row_arrays
}
///|
pub fn QueryResult::columns(self : QueryResult) -> Array[String] {
let n_cols = pg_nfields(self)
let cols = Array::make(n_cols, "")
for i = 0; i < n_cols; i = i + 1 {
let name = pg_fname(self, i)
ignore(cols.set(i, name))
}
cols
}
///|
pub fn QueryResult::affected_rows(self : QueryResult) -> Int {
pg_cmdtuples(self).to_int()
}
///|
pub fn QueryResult::free(self : QueryResult) -> Unit {
pg_free_result(self)
}
///|
pub fn Connection::prepare(
self : Connection,
name : String,
sql : String,
) -> Result[PreparedStatement, PgError] {
let stmt = pg_prepare_internal(
self,
string_to_c_bytes(name),
string_to_c_bytes(sql),
)
if pg_statement_is_null(stmt) > 0 {
let err = pg_error_message(self)
Err(PgError::QueryError(err))
} else {
Ok(stmt)
}
}
///|
pub fn[T : ToValue] PreparedStatement::execute(
self : PreparedStatement,
params : Array[T],
) -> Result[QueryResult, PgError] {
let values = params.map(fn(p) { p.to_value() })
let result = pg_execute_prepared_internal(self, values)
if pg_result_is_null(result) > 0 {
let err = pg_statement_error(self)
Err(PgError::QueryError(err))
} else {
Ok(result)
}
}
///|
pub fn PreparedStatement::close(self : PreparedStatement) -> Unit {
pg_close_statement_internal(self)
}
///|
pub fn Connection::close(self : Connection) -> Unit {
pg_close_internal(self)
}
///|
extern "C" fn pg_connect_internal(conninfo : Bytes) -> Connection = "pg_connect_internal"
///|
extern "C" fn pg_query_internal(conn : Connection, sql : Bytes) -> QueryResult = "pg_query_internal"
///|
extern "C" fn pg_execute_internal(
conn : Connection,
sql : Bytes,
params : Array[Value],
param_count : Int,
) -> QueryResult = "pg_execute_internal"
///|
extern "C" fn pg_prepare_internal(
conn : Connection,
name : Bytes,
sql : Bytes,
) -> PreparedStatement = "pg_prepare_internal"
///|
extern "C" fn pg_execute_prepared_internal(
stmt : PreparedStatement,
params : Array[Value],
) -> QueryResult = "pg_execute_prepared_internal"
///|
extern "C" fn pg_close_statement_internal(stmt : PreparedStatement) -> Unit = "pg_close_statement_internal"
///|
extern "C" fn pg_close_internal(conn : Connection) -> Unit = "pg_close_internal"
///|
extern "C" fn pg_error_message(conn : Connection) -> String = "pg_error_message"
///|
extern "C" fn pg_statement_error(stmt : PreparedStatement) -> String = "pg_statement_error"
///|
extern "C" fn pg_ntuples(result : QueryResult) -> Int = "pg_ntuples"
///|
extern "C" fn pg_nfields(result : QueryResult) -> Int = "pg_nfields"
///|
extern "C" fn pg_getvalue(result : QueryResult, row : Int, col : Int) -> String = "pg_getvalue"
///|
extern "C" fn pg_fname(result : QueryResult, col : Int) -> String = "pg_fname"
///|
extern "C" fn pg_cmdtuples(result : QueryResult) -> Int64 = "pg_cmdtuples"
///|
extern "C" fn pg_free_result(result : QueryResult) -> Unit = "pg_free_result"
///|
extern "C" fn pg_connection_is_null(conn : Connection) -> Int = "pg_connection_is_null"
///|
extern "C" fn pg_result_is_null(result : QueryResult) -> Int = "pg_result_is_null"
///|
extern "C" fn pg_statement_is_null(stmt : PreparedStatement) -> Int = "pg_statement_is_null"
///|
pub fn pg_get_env(name : String) -> String {
let name_bytes = string_to_c_bytes(name)
pg_get_env_internal(name_bytes, name_bytes.length())
}
///|
extern "C" fn pg_get_env_internal(name : Bytes, len : Int) -> String = "pg_get_env"