Skip to content

Commit d2b30df

Browse files
authored
db.pg: add support for prepared statement, with db.prepare/3 and db.exec_prepared/2 (#23442)
1 parent 1832bc8 commit d2b30df

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

‎vlib/db/pg/pg.c.v‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ fn C.PQputCopyEnd(conn &C.PGconn, const_errmsg &char) int
154154

155155
fn C.PQgetCopyData(conn &C.PGconn, buffer &&char, async int) int
156156

157+
fn C.PQprepare(conn &C.PGconn, const_stmtName &char, const_query &char, nParams int, const_param_types &&char) &C.PGresult
158+
159+
fn C.PQexecPrepared(conn &C.PGconn, const_stmtName &char, nParams int, const_paramValues &char,
160+
const_paramLengths &int, const_paramFormats &int, resultFormat int) &C.PGresult
161+
157162
// cleanup
158163

159164
fn C.PQclear(res &C.PGresult)
@@ -309,6 +314,27 @@ pub fn (db DB) exec_param2(query string, param string, param2 string) ![]Row {
309314
return db.exec_param_many(query, [param, param2])
310315
}
311316

317+
// prepare submits a request to create a prepared statement with the given parameters, and waits for completion. You must provide the number of parameters (`$1, $2, $3 ...`) used in the statement
318+
pub fn (db DB) prepare(name string, query string, num_params int) ! {
319+
res := C.PQprepare(db.conn, &char(name.str), &char(query.str), num_params, 0) // defining param types is optional
320+
321+
return db.handle_error(res, 'prepare')
322+
}
323+
324+
// exec_prepared sends a request to execute a prepared statement with given parameters, and waits for the result. The number of parameters must match with the parameters declared in the prepared statement.
325+
pub fn (db DB) exec_prepared(name string, params []string) ![]Row {
326+
unsafe {
327+
mut param_vals := []&char{len: params.len}
328+
for i in 0 .. params.len {
329+
param_vals[i] = &char(params[i].str)
330+
}
331+
332+
res := C.PQexecPrepared(db.conn, &char(name.str), params.len, param_vals.data,
333+
0, 0, 0)
334+
return db.handle_error_or_result(res, 'exec_prepared')
335+
}
336+
}
337+
312338
fn (db DB) handle_error_or_result(res voidptr, elabel string) ![]Row {
313339
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
314340
if e != '' {
@@ -321,6 +347,17 @@ fn (db DB) handle_error_or_result(res voidptr, elabel string) ![]Row {
321347
return res_to_rows(res)
322348
}
323349

350+
fn (db DB) handle_error(res voidptr, elabel string) ! {
351+
e := unsafe { C.PQerrorMessage(db.conn).vstring() }
352+
if e != '' {
353+
C.PQclear(res)
354+
$if trace_pg_error ? {
355+
eprintln('pg error: ${e}')
356+
}
357+
return error('pg ${elabel} error:\n${e}')
358+
}
359+
}
360+
324361
// copy_expert executes COPY command
325362
// https://www.postgresql.org/docs/9.5/libpq-copy.html
326363
pub fn (db DB) copy_expert(query string, mut file io.ReaderWriter) !int {

‎vlib/db/pg/pg_test.v‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,16 @@ WHERE
2828
row.str()
2929
}
3030
}
31+
32+
fn test_prepared() {
33+
db := pg.connect(pg.Config{ user: 'postgres', password: 'secret', dbname: 'postgres' })!
34+
defer {
35+
db.close()
36+
}
37+
38+
db.prepare('test_prepared', 'SELECT NOW(), $1 AS NAME', 1) or { panic(err) }
39+
40+
result := db.exec_prepared('test_prepared', ['hello world']) or { panic(err) }
41+
42+
assert result.len == 1
43+
}

0 commit comments

Comments
 (0)