v/vlib/pg/pg.v

174 lines
4.0 KiB
V

module pg
#flag -lpq
#flag linux -I/usr/include/postgresql
#flag darwin -I/opt/local/include/postgresql11
#flag windows -I @VROOT/thirdparty/pg/include
#flag windows -L @VROOT/thirdparty/pg/win64
#include <libpq-fe.h>
pub struct DB {
mut:
conn &C.PGconn
}
pub struct Row {
pub mut:
vals []string
}
struct C.PGResult { }
pub struct Config {
pub:
host string
port int = 5432
user string
password string
dbname string
}
fn C.PQconnectdb(a byteptr) &C.PGconn
fn C.PQerrorMessage(voidptr) byteptr
fn C.PQgetvalue(voidptr, int, int) byteptr
fn C.PQstatus(voidptr) int
fn C.PQntuples(voidptr) int
fn C.PQnfields(voidptr) int
fn C.PQexec(voidptr) voidptr
fn C.PQexecParams(voidptr) voidptr
fn C.PQclear(voidptr) voidptr
fn C.PQfinish(voidptr)
pub fn connect(config Config) ?DB {
conninfo := 'host=$config.host port=$config.port user=$config.user dbname=$config.dbname password=$config.password'
conn := C.PQconnectdb(conninfo.str)
status := C.PQstatus(conn)
println("status=$status")
if status != C.CONNECTION_OK {
error_msg := C.PQerrorMessage(conn)
return error ('Connection to a PG database failed: ' + unsafe { error_msg.vstring() } )
}
return DB {conn: conn}
}
fn res_to_rows(res voidptr) []Row {
nr_rows := C.PQntuples(res)
nr_cols := C.PQnfields(res)
mut rows := []Row{}
for i in 0..nr_rows {
mut row := Row{}
for j in 0..nr_cols {
val := C.PQgetvalue(res, i, j)
sval := unsafe { val.vstring() }
row.vals << sval
}
rows << row
}
C.PQclear(res)
return rows
}
// close frees the underlaying resource allocated by the database connection
pub fn (db DB) close() {
C.PQfinish(db.conn)
}
pub fn (db DB) q_int(query string) int {
rows := db.exec(query)
if rows.len == 0 {
println('q_int "$query" not found')
return 0
}
row := rows[0]
if row.vals.len == 0 {
return 0
}
val := row.vals[0]
return val.int()
}
pub fn (db DB) q_string(query string) string {
rows := db.exec(query)
if rows.len == 0 {
println('q_string "$query" not found')
return ''
}
row := rows[0]
if row.vals.len == 0 {
return ''
}
val := row.vals[0]
return val
}
pub fn (db DB) q_strings(query string) []Row {
return db.exec(query)
}
pub fn (db DB) exec(query string) []Row {
res := C.PQexec(db.conn, query.str)
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
if e != '' {
println('pg exec error:')
println(e)
return res_to_rows(res)
}
return res_to_rows(res)
}
fn rows_first_or_empty(rows []Row) ?Row {
if rows.len == 0 {
return error('no row')
}
return rows[0]
}
pub fn (db DB) exec_one(query string) ?Row {
res := C.PQexec(db.conn, query.str)
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
if e != '' {
return error('pg exec error: "$e"')
}
row := rows_first_or_empty( res_to_rows(res) )?
return row
}
// The entire function can be considered unsafe because of the malloc and the
// free. This prevents warnings and doesn't seem to affect behavior.
pub fn (db DB) exec_param_many(query string, params []string) []Row {
unsafe {
mut param_vals := &byteptr(malloc(params.len * 8))
for i in 0..params.len {
param_vals[i] = params[i].str
}
res := C.PQexecParams(db.conn, query.str, params.len, 0, param_vals, 0, 0, 0)
free(param_vals)
return db.handle_error_or_result(res, 'exec_param_many')
}
}
pub fn (db DB) exec_param2(query string, param, param2 string) []Row {
mut param_vals := [2]byteptr{}
param_vals[0] = param.str
param_vals[1] = param2.str
res := C.PQexecParams(db.conn, query.str, 2, 0, param_vals, 0, 0, 0)
return db.handle_error_or_result(res, 'exec_param2')
}
pub fn (db DB) exec_param(query string, param string) []Row {
mut param_vals := [1]byteptr{}
param_vals[0] = param.str
res := C.PQexecParams(db.conn, query.str, 1, 0, param_vals, 0, 0, 0)
return db.handle_error_or_result(res, 'exec_param')
}
fn (db DB) handle_error_or_result(res voidptr, elabel string) []Row {
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
if e != '' {
println('pg $elabel error:')
println(e)
return res_to_rows(res)
}
return res_to_rows(res)
}