Skip to content

Commit 81a4ae5

Browse files
committed
Add support for returning results using Postgres' binary format
1 parent 5764ff3 commit 81a4ae5

File tree

6 files changed

+129
-14
lines changed

6 files changed

+129
-14
lines changed

examples/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
TARGETS = $(addsuffix .bc, async cursor dump populate prompt test_lo)
1+
TARGETS = $(addsuffix .bc, async binary cursor dump populate prompt test_lo)
22

33
.PHONY: all clean
44

examples/binary.ml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
(* Create a table, insert a binary string various ways, and read it various ways *)
2+
3+
open! Postgresql
4+
5+
let _ =
6+
if Array.length Sys.argv <> 3 then (
7+
Printf.printf "\
8+
Usage: dump conninfo table\n\
9+
Connect to PostgreSQL with [conninfo] (e.g. \"host=localhost\"),\n\
10+
and create, write to, read from, and then DELETE [table]\n";
11+
exit 1)
12+
13+
let table = Sys.argv.(2)
14+
let c =
15+
try new connection ~conninfo:Sys.argv.(1) () with
16+
| Error e ->
17+
prerr_endline (string_of_error e);
18+
exit 34
19+
| e ->
20+
prerr_endline (Printexc.to_string e);
21+
exit 35
22+
23+
24+
let create_table () : unit =
25+
c#exec ~expect:[Command_ok]
26+
("create table " ^ table ^ " (data bytea)")
27+
|> ignore
28+
29+
let write_escaped data : unit =
30+
c#exec ~expect:[Command_ok]
31+
("insert into " ^ table
32+
^ " (data)"
33+
^ " VALUES ('"
34+
^ c#escape_bytea data
35+
^ "')")
36+
|> ignore
37+
38+
let write_binary data : unit =
39+
c#exec ~expect:[Command_ok]
40+
~params:[|data|] ~binary_params:[|true|]
41+
("insert into " ^ table
42+
^ " (data)"
43+
^ " VALUES ($1)")
44+
|> ignore
45+
46+
let read_escaped () : string list =
47+
let result =
48+
c#exec ~expect:[Tuples_ok]
49+
("select data from " ^ table)
50+
in
51+
result#get_all_lst
52+
|> List.map List.hd
53+
|> List.map unescape_bytea
54+
55+
let read_binary () : string list =
56+
let result =
57+
c#exec ~expect:[Tuples_ok] ~binary_result:true
58+
("select data from " ^ table)
59+
in
60+
result#get_all_lst
61+
|> List.map List.hd
62+
63+
let delete_table () : unit =
64+
c#exec ~expect:[Command_ok]
65+
("drop table if exists " ^ table ^ " cascade")
66+
|> ignore
67+
68+
let string_list_to_string l =
69+
"["
70+
^ (String.concat ", " l)
71+
^ "]"
72+
73+
let main () =
74+
delete_table ();
75+
create_table ();
76+
(* first 'line' of a compiled binary, pasted in *)
77+
let data = "ELF>0L@–„$@8 @%$@@@¯¯888†¿†¿ p√p√0p√0ÿ‚! (ƒ(ƒ0(ƒ000TTTDDPÂtd`‘`‘`‘Ã[Ã[QÂtdRÂtdp√p√0p√0ê ê ∑Åù[»·≥¢l∆'„¶•0ë5!@ÇÄ%$ √ bFÄ@ê Ú0êÄàê‡ÄÉê– " in
78+
write_escaped data;
79+
write_binary data;
80+
let read_esc = read_escaped () in
81+
let read_bin = read_binary () in
82+
let expected = [data; data] in
83+
(* print_endline ("expected: " ^ string_list_to_string expected); *)
84+
(* print_endline ("read_escaped: " ^ string_list_to_string read_esc); *)
85+
(* print_endline ("read_binary: " ^ string_list_to_string read_bin); *)
86+
if read_esc <> expected
87+
then prerr_endline "read_escaped <> expected";
88+
if read_bin <> expected
89+
then prerr_endline "read_binary <> expected";
90+
if read_esc <> read_bin
91+
then prerr_endline "read_escaped <> read_binary";
92+
delete_table ();
93+
()
94+
95+
96+
let _ =
97+
try main () with
98+
| Error e ->
99+
prerr_endline (string_of_error e);
100+
exit 23
101+
| e ->
102+
prerr_endline (Printexc.to_string e);
103+
exit 24

examples/jbuild

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(jbuild_version 1)
22

33
(executables (
4-
(names (async cursor dump populate prompt test_lo))
4+
(names (async binary cursor dump populate prompt test_lo))
55
(flags (
66
:standard
77
-w -9 -strict-sequence -principal -short-paths

src/postgresql.ml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ module Stub = struct
369369
external result_isnull : result -> bool = "PQres_isnull" [@@noalloc]
370370

371371
external exec_params :
372-
connection -> string -> string array -> bool array -> result
372+
connection -> string -> string array -> bool array -> bool -> result
373373
= "PQexecParams_stub"
374374

375375
external prepare : connection -> string -> string -> result = "PQprepare_stub"
@@ -896,10 +896,14 @@ object (self)
896896
method empty_result status =
897897
new result (wrap_conn (fun conn -> (Stub.make_empty_res conn status)))
898898

899-
method exec ?(expect = []) ?(params = [||]) ?(binary_params = [||]) query =
899+
method exec
900+
?(expect = []) ?(params = [||]) ?(binary_params = [||])
901+
?(binary_result = false) query =
900902
let r =
901903
wrap_conn (fun conn ->
902-
let r = Stub.exec_params conn query params binary_params in
904+
let r =
905+
Stub.exec_params conn query params binary_params binary_result
906+
in
903907
if Stub.result_isnull r then signal_error conn
904908
else r)
905909
in

src/postgresql.mli

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -581,18 +581,23 @@ object
581581

582582
method exec :
583583
?expect : result_status list -> ?params : string array ->
584-
?binary_params : bool array -> string -> result
585-
(** [exec ?expect ?params ?binary_params query] synchronous execution
586-
of query or command [query]. The result status will be checked
587-
against all elements in [expect]. If [expect] is not empty and if
588-
there is no match, the exception [Unexpected_status] will be raised.
584+
?binary_params : bool array -> ?binary_result : bool ->
585+
string -> result
586+
587+
(** [exec ?expect ?params ?binary_params ?binary_result query]
588+
synchronous execution of query or command [query]. The result
589+
status will be checked against all elements in [expect]. If
590+
[expect] is not empty and if there is no match, the exception
591+
[Unexpected_status] will be raised.
589592
590593
Additional query parameters can be passed in the [params] array.
591594
They must not be escaped and they can be referred to in [query]
592595
as $1, $2, ... The value [null] can be used in the [params]
593596
array to denote an SQL NULL. It is possible to specify that some
594597
of the query parameters are passed as binary strings using the
595-
[binary_params] array.
598+
[binary_params] array. By default, results are returned in text
599+
format, but will be returned in binary format if [binary_result]
600+
is true.
596601
597602
If no (or an empty) query parameter is passed, it is possible to
598603
emit several commands with a single call.
@@ -602,6 +607,7 @@ object
602607
@param expect default = []
603608
@param params default = [||]
604609
@param binary_params default = [||]
610+
@param binary_result default = false
605611
606612
@raise Error if there is a connection error.
607613
@raise Error if there is an unexpected result status.

src/postgresql_stubs.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,8 @@ static inline void free_params_shallow(
623623
}
624624

625625
CAMLprim value PQexecParams_stub(
626-
value v_conn, value v_query, value v_params, value v_binary_params)
626+
value v_conn, value v_query, value v_params, value v_binary_params,
627+
value v_binary_result)
627628
{
628629
CAMLparam1(v_conn);
629630
PGconn *conn = get_conn(v_conn);
@@ -636,11 +637,12 @@ CAMLprim value PQexecParams_stub(
636637
int *formats, *lengths;
637638
copy_binary_params(v_params, v_binary_params, nparams, &formats, &lengths);
638639
memcpy(query, String_val(v_query), len);
640+
bool binary_result = Bool_val(v_binary_result);
639641
caml_enter_blocking_section();
640642
res =
641-
(nparams == 0)
643+
(nparams == 0 && !binary_result)
642644
? PQexec(conn, query)
643-
: PQexecParams(conn, query, nparams, NULL, params, lengths, formats, 0);
645+
: PQexecParams(conn, query, nparams, NULL, params, lengths, formats, binary_result);
644646
free_binary_params(formats, lengths);
645647
free_params(params, nparams);
646648
caml_stat_free(query);

0 commit comments

Comments
 (0)