pg: avoid leaking a PGconn on connection errors (#6543)

pull/6474/head
Leonardo Cecchi 2020-10-03 07:03:11 +02:00 committed by GitHub
parent c8a884f57f
commit 8ac0bd44bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 52 additions and 31 deletions

View File

@ -6,7 +6,6 @@ module pg
#flag windows -I @VROOT/thirdparty/pg/include #flag windows -I @VROOT/thirdparty/pg/include
#flag windows -L @VROOT/thirdparty/pg/win64 #flag windows -L @VROOT/thirdparty/pg/win64
#include <libpq-fe.h> #include <libpq-fe.h>
pub struct DB { pub struct DB {
mut: mut:
conn &C.PGconn conn &C.PGconn
@ -17,49 +16,71 @@ pub mut:
vals []string vals []string
} }
struct C.PGResult { } struct C.PGResult {
}
pub struct Config { pub struct Config {
pub: pub:
host string host string
port int = 5432 port int = 5432
user string user string
password string password string
dbname string dbname string
} }
fn C.PQconnectdb(a byteptr) &C.PGconn 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)
fn C.PQerrorMessage(arg_1 voidptr) byteptr
fn C.PQgetvalue(arg_1 voidptr, arg_2, arg_3 int) byteptr
fn C.PQstatus(arg_1 voidptr) int
fn C.PQntuples(arg_1 voidptr) int
fn C.PQnfields(arg_1 voidptr) int
fn C.PQexec(arg_1 voidptr) voidptr
fn C.PQexecParams(arg_1 voidptr) voidptr
fn C.PQclear(arg_1 voidptr) voidptr
fn C.PQfinish(arg_1 voidptr)
// Makes a new connection to the database server using
// the parameters from the `Config` structure, returning
// a connection error when something goes wrong
pub fn connect(config Config) ?DB { pub fn connect(config Config) ?DB {
conninfo := 'host=$config.host port=$config.port user=$config.user dbname=$config.dbname password=$config.password' conninfo := 'host=$config.host port=$config.port user=$config.user dbname=$config.dbname password=$config.password'
conn := C.PQconnectdb(conninfo.str) conn := C.PQconnectdb(conninfo.str)
status := C.PQstatus(conn) if conn == 0 {
println("status=$status") return error('libpq memory allocation error')
if status != C.CONNECTION_OK { }
error_msg := C.PQerrorMessage(conn) status := C.PQstatus(conn)
return error ('Connection to a PG database failed: ' + unsafe { error_msg.vstring() } ) if status != C.CONNECTION_OK {
// We force the construction of a new string as the
// error message will be freed by the next `PQfinish`
// call
c_error_msg := unsafe {C.PQerrorMessage(conn).vstring()}
error_msg := '$c_error_msg'
C.PQfinish(conn)
return error('Connection to a PG database failed: $error_msg')
}
return DB{
conn: conn
} }
return DB {conn: conn}
} }
fn res_to_rows(res voidptr) []Row { fn res_to_rows(res voidptr) []Row {
nr_rows := C.PQntuples(res) nr_rows := C.PQntuples(res)
nr_cols := C.PQnfields(res) nr_cols := C.PQnfields(res)
mut rows := []Row{} mut rows := []Row{}
for i in 0..nr_rows { for i in 0 .. nr_rows {
mut row := Row{} mut row := Row{}
for j in 0..nr_cols { for j in 0 .. nr_cols {
val := C.PQgetvalue(res, i, j) val := C.PQgetvalue(res, i, j)
sval := unsafe { val.vstring() } sval := unsafe {val.vstring()}
row.vals << sval row.vals << sval
} }
rows << row rows << row
@ -107,7 +128,7 @@ pub fn (db DB) q_strings(query string) []Row {
pub fn (db DB) exec(query string) []Row { pub fn (db DB) exec(query string) []Row {
res := C.PQexec(db.conn, query.str) res := C.PQexec(db.conn, query.str)
e := unsafe { C.PQerrorMessage(db.conn).vstring() } e := unsafe {C.PQerrorMessage(db.conn).vstring()}
if e != '' { if e != '' {
println('pg exec error:') println('pg exec error:')
println(e) println(e)
@ -125,11 +146,11 @@ fn rows_first_or_empty(rows []Row) ?Row {
pub fn (db DB) exec_one(query string) ?Row { pub fn (db DB) exec_one(query string) ?Row {
res := C.PQexec(db.conn, query.str) res := C.PQexec(db.conn, query.str)
e := unsafe { C.PQerrorMessage(db.conn).vstring() } e := unsafe {C.PQerrorMessage(db.conn).vstring()}
if e != '' { if e != '' {
return error('pg exec error: "$e"') return error('pg exec error: "$e"')
} }
row := rows_first_or_empty( res_to_rows(res) )? row := rows_first_or_empty(res_to_rows(res)) ?
return row return row
} }
@ -138,7 +159,7 @@ pub fn (db DB) exec_one(query string) ?Row {
pub fn (db DB) exec_param_many(query string, params []string) []Row { pub fn (db DB) exec_param_many(query string, params []string) []Row {
unsafe { unsafe {
mut param_vals := &byteptr(malloc(params.len * 8)) mut param_vals := &byteptr(malloc(params.len * 8))
for i in 0..params.len { for i in 0 .. params.len {
param_vals[i] = params[i].str param_vals[i] = params[i].str
} }
res := C.PQexecParams(db.conn, query.str, params.len, 0, param_vals, 0, 0, 0) res := C.PQexecParams(db.conn, query.str, params.len, 0, param_vals, 0, 0, 0)
@ -147,7 +168,7 @@ pub fn (db DB) exec_param_many(query string, params []string) []Row {
} }
} }
pub fn (db DB) exec_param2(query string, param, param2 string) []Row { pub fn (db DB) exec_param2(query, param, param2 string) []Row {
mut param_vals := [2]byteptr{} mut param_vals := [2]byteptr{}
param_vals[0] = param.str param_vals[0] = param.str
param_vals[1] = param2.str param_vals[1] = param2.str
@ -155,7 +176,7 @@ pub fn (db DB) exec_param2(query string, param, param2 string) []Row {
return db.handle_error_or_result(res, 'exec_param2') return db.handle_error_or_result(res, 'exec_param2')
} }
pub fn (db DB) exec_param(query string, param string) []Row { pub fn (db DB) exec_param(query, param string) []Row {
mut param_vals := [1]byteptr{} mut param_vals := [1]byteptr{}
param_vals[0] = param.str param_vals[0] = param.str
res := C.PQexecParams(db.conn, query.str, 1, 0, param_vals, 0, 0, 0) res := C.PQexecParams(db.conn, query.str, 1, 0, param_vals, 0, 0, 0)
@ -163,7 +184,7 @@ pub fn (db DB) exec_param(query string, param string) []Row {
} }
fn (db DB) handle_error_or_result(res voidptr, elabel string) []Row { fn (db DB) handle_error_or_result(res voidptr, elabel string) []Row {
e := unsafe { C.PQerrorMessage(db.conn).vstring() } e := unsafe {C.PQerrorMessage(db.conn).vstring()}
if e != '' { if e != '' {
println('pg $elabel error:') println('pg $elabel error:')
println(e) println(e)