Skip to content

Commit 4aa8df5

Browse files
committed
Support parameter types arguments.
1 parent 8300b4b commit 4aa8df5

File tree

4 files changed

+132
-56
lines changed

4 files changed

+132
-56
lines changed

examples/async.ml

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,36 @@ let test (c : connection) =
5454
REFERENCES postgresql_ocaml_async ON DELETE CASCADE)";
5555
assert ((fetch_single_result c)#status = Command_ok);
5656

57+
begin
58+
c#send_query
59+
~param_types:Postgresql.[|oid_of_ftype INT8; oid_of_ftype INT8|]
60+
~params:[|"4100100100"; "5100100100"|]
61+
"SELECT $1 + $2";
62+
let r = fetch_single_result c in
63+
assert (r#status = Tuples_ok);
64+
assert (r#nfields = 1);
65+
assert (r#ntuples = 1);
66+
assert (r#getvalue 0 0 = "9200200200");
67+
end;
68+
5769
(* Populate using a prepared statement. *)
58-
c#send_prepare "test_ins"
59-
"INSERT INTO postgresql_ocaml_async (a, b) VALUES ($1, $2)";
60-
assert ((fetch_single_result c)#status = Command_ok);
61-
c#send_query_prepared ~params:[|"2"; "two"|] "test_ins";
62-
assert ((fetch_single_result c)#status = Command_ok);
63-
c#send_query_prepared ~params:[|"3"; "three"|] "test_ins";
64-
assert ((fetch_single_result c)#status = Command_ok);
70+
let shown_ntuples = 10 in
71+
let expected_ntuples = 3 * 100 in
72+
for i = 0 to 2 do
73+
let stmt = sprintf "test_ins_%d" i in
74+
let param_types =
75+
Array.sub Postgresql.[|oid_of_ftype INT4; oid_of_ftype TEXT|] 0 i
76+
in
77+
c#send_prepare stmt ~param_types
78+
"INSERT INTO postgresql_ocaml_async (a, b) VALUES ($1, $2)";
79+
assert ((fetch_single_result c)#status = Command_ok);
80+
for j = 1 to 100 do
81+
let c0 = string_of_int (i + 3 * j) in
82+
let c1 = sprintf "The number %d." (i + 3 * j) in
83+
c#send_query_prepared ~params:[|c0; c1|] stmt;
84+
assert ((fetch_single_result c)#status = Command_ok)
85+
done
86+
done;
6587

6688
(* Prepare a select statement. *)
6789
c#send_prepare "test_sel" "SELECT * FROM postgresql_ocaml_async";
@@ -80,26 +102,27 @@ let test (c : connection) =
80102
c#send_query_prepared "test_sel";
81103
let r = fetch_single_result c in
82104
assert (r#status = Tuples_ok);
83-
assert (r#ntuples = 2);
105+
assert (r#ntuples = expected_ntuples);
84106
assert (r#nfields = 3);
85-
for i = 0 to r#ntuples - 1 do
86-
Printf.printf "%s %s %s\n"
87-
(r#getvalue i 0) (r#getvalue i 1) (r#getvalue i 2)
107+
for i = 0 to min r#ntuples shown_ntuples - 1 do
108+
printf "%s, %s, %s\n" (r#getvalue i 0) (r#getvalue i 1) (r#getvalue i 2)
88109
done;
110+
printf "[...]\n";
89111

90112
(* Run it in single-row mode. *)
91113
c#send_query_prepared "test_sel";
92114
c#set_single_row_mode;
93-
for i = 0 to 2 do
115+
for i = 0 to expected_ntuples do
94116
match fetch_result c with
95117
| None -> assert false
96-
| Some r when i < 2 ->
118+
| Some r when i < expected_ntuples ->
97119
assert (r#status = Single_tuple);
98-
Printf.printf "%s %s %s\n"
99-
(r#getvalue 0 0) (r#getvalue 0 1) (r#getvalue 0 2)
120+
if i < shown_ntuples then
121+
printf "%s, %s, %s\n" (r#getvalue 0 0) (r#getvalue 0 1) (r#getvalue 0 2)
100122
| Some r ->
101123
assert (r#status = Tuples_ok)
102124
done;
125+
printf "[...]\n";
103126
assert (fetch_result c = None);
104127

105128
(* Drop the main table. *)

src/postgresql.ml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,13 @@ module Stub = struct
380380
external result_isnull : result -> bool = "PQres_isnull" [@@noalloc]
381381

382382
external exec_params :
383-
connection -> string -> string array -> bool array -> bool -> result
384-
= "PQexecParams_stub"
383+
connection -> string -> oid array -> string array -> bool array ->
384+
bool -> result
385+
= "PQexecParams_stub_bc" "PQexecParams_stub"
385386

386-
external prepare : connection -> string -> string -> result = "PQprepare_stub"
387+
external prepare :
388+
connection -> string -> string -> oid array -> result
389+
= "PQprepare_stub"
387390

388391
external exec_prepared :
389392
connection -> string -> string array -> bool array -> result
@@ -473,11 +476,12 @@ module Stub = struct
473476
connection -> bool = "PQisnonblocking_stub" [@@noalloc]
474477

475478
external send_query_params :
476-
connection -> string -> string array -> bool array -> (int [@untagged])
479+
connection -> string -> oid array -> string array -> bool array ->
480+
(int [@untagged])
477481
= "PQsendQueryParams_stub_bc" "PQsendQueryParams_stub"
478482

479483
external send_prepare :
480-
connection -> string -> string -> (int [@untagged])
484+
connection -> string -> string -> oid array -> (int [@untagged])
481485
= "PQsendPrepare_stub_bc" "PQsendPrepare_stub" [@@noalloc]
482486

483487
external send_query_prepared :
@@ -928,12 +932,13 @@ object (self)
928932
new result (wrap_conn (fun conn -> (Stub.make_empty_res conn status)))
929933

930934
method exec
931-
?(expect = []) ?(params = [||]) ?(binary_params = [||])
932-
?(binary_result = false) query =
935+
?(expect = []) ?(param_types = [||]) ?(params = [||])
936+
?(binary_params = [||]) ?(binary_result = false) query =
933937
let r =
934938
wrap_conn (fun conn ->
935939
let r =
936-
Stub.exec_params conn query params binary_params binary_result
940+
Stub.exec_params conn query param_types params
941+
binary_params binary_result
937942
in
938943
if Stub.result_isnull r then signal_error conn
939944
else r)
@@ -944,10 +949,10 @@ object (self)
944949
raise (Error (Unexpected_status (stat, res#error, expect)))
945950
else res
946951

947-
method prepare stm_name query =
952+
method prepare ?(param_types = [||]) stm_name query =
948953
new result (
949954
wrap_conn (fun conn ->
950-
let r = Stub.prepare conn stm_name query in
955+
let r = Stub.prepare conn stm_name query param_types in
951956
if Stub.result_isnull r then signal_error conn
952957
else r))
953958

@@ -972,14 +977,17 @@ object (self)
972977
if Stub.result_isnull r then signal_error conn
973978
else r))
974979

975-
method send_query ?(params = [||]) ?(binary_params = [||]) query =
980+
method send_query
981+
?(param_types = [||]) ?(params = [||]) ?(binary_params = [||]) query =
976982
wrap_conn (fun conn ->
977-
if Stub.send_query_params conn query params binary_params <> 1 then
983+
if Stub.send_query_params conn query param_types params binary_params
984+
<> 1 then
978985
signal_error conn)
979986

980-
method send_prepare stm_name query =
987+
method send_prepare ?(param_types = [||]) stm_name query =
981988
wrap_conn (fun conn ->
982-
if Stub.send_prepare conn stm_name query <> 1 then signal_error conn)
989+
if Stub.send_prepare conn stm_name query param_types <> 1 then
990+
signal_error conn)
983991

984992
method send_query_prepared ?(params = [||]) ?(binary_params = [||]) stm_name =
985993
wrap_conn (fun conn ->

src/postgresql.mli

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -704,10 +704,11 @@ object
704704
*)
705705

706706
method exec :
707-
?expect : result_status list -> ?params : string array ->
707+
?expect : result_status list ->
708+
?param_types : oid array -> ?params : string array ->
708709
?binary_params : bool array -> ?binary_result : bool ->
709710
string -> result
710-
(** [exec ?expect ?params ?binary_params ?binary_result query]
711+
(** [exec ?expect ?params ?param_types ?binary_params ?binary_result query]
711712
synchronous execution of query or command [query]. The result
712713
status will be checked against all elements in [expect]. If
713714
[expect] is not empty and if there is no match, the exception
@@ -728,6 +729,7 @@ object
728729
@return result of query.
729730
730731
@param expect default = []
732+
@param param_types default = [||]
731733
@param params default = [||]
732734
@param binary_params default = [||]
733735
@param binary_result default = false
@@ -736,9 +738,9 @@ object
736738
@raise Error if there is an unexpected result status.
737739
*)
738740

739-
method prepare : string -> string -> result
740-
(** [prepare stm_name query] creates a prepared query named [stm_name]
741-
which will execute the query or command [query] when passed to
741+
method prepare : ?param_types : oid array -> string -> string -> result
742+
(** [prepare ?param_types stm_name query] creates a prepared query named
743+
[stm_name] which will execute the query or command [query] when passed to
742744
[#exec_prepared]. *)
743745

744746
method exec_prepared :
@@ -772,10 +774,10 @@ object
772774
*)
773775

774776
method send_query :
775-
?params : string array -> ?binary_params : bool array
776-
-> string -> unit
777-
(** [send_query ?params ?binary_params query] asynchronous execution
778-
of query or command [query].
777+
?param_types : oid array -> ?params : string array ->
778+
?binary_params : bool array -> string -> unit
779+
(** [send_query ?param_types ?params ?binary_params query] asynchronous
780+
execution of query or command [query].
779781
780782
Additional query parameters can be passed in the [params] array.
781783
They must not be escaped and they can be referred to in [query]
@@ -787,16 +789,17 @@ object
787789
If no (or an empty) query parameter is passed, it is possible to
788790
emit several commands with a single call.
789791
792+
@param param_types default = [||]
790793
@param params default = [||]
791794
@param binary_params default = [||]
792795
793796
@raise Error if there is a connection error.
794797
*)
795798

796-
method send_prepare : string -> string -> unit
797-
(** [#send_prepare stm_name query] sends a query preparation without waiting
798-
for the result. This does the same as {!prepare} except that the status
799-
is reported by {!get_result} when available.
799+
method send_prepare : ?param_types : oid array -> string -> string -> unit
800+
(** [#send_prepare ?param_types stm_name query] sends a query preparation
801+
without waiting for the result. This does the same as {!prepare} except
802+
that the status is reported by {!get_result} when available.
800803
801804
@raise Error if there is a connection error. *)
802805

src/postgresql_stubs.c

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,23 @@ static inline void free_binary_params(int *formats, int *lengths)
583583
if (lengths != NULL) caml_stat_free(lengths);
584584
}
585585

586+
static inline Oid * copy_param_types(
587+
value v_param_types, size_t nparams, size_t nparam_types)
588+
{
589+
Oid *param_types;
590+
size_t i;
591+
if (nparam_types == 0) return NULL;
592+
if (nparam_types > nparams) nparam_types = nparams;
593+
param_types = caml_stat_alloc(nparams * sizeof(Oid));
594+
for (i = 0; i < nparam_types; i++) {
595+
value v_param_type = Field(v_param_types, i);
596+
param_types[i] = Int_val(v_param_type);
597+
}
598+
memset(param_types + nparam_types, 0,
599+
(nparams - nparam_types) * sizeof(Oid));
600+
return param_types;
601+
}
602+
586603
static inline const char * const * copy_params(value v_params, size_t nparams)
587604
{
588605
char **params;
@@ -631,8 +648,8 @@ static inline void free_params_shallow(
631648
}
632649

633650
CAMLprim value PQexecParams_stub(
634-
value v_conn, value v_query, value v_params, value v_binary_params,
635-
value v_binary_result)
651+
value v_conn, value v_query, value v_param_types, value v_params,
652+
value v_binary_params, value v_binary_result)
636653
{
637654
CAMLparam1(v_conn);
638655
PGconn *conn = get_conn(v_conn);
@@ -642,6 +659,8 @@ CAMLprim value PQexecParams_stub(
642659
char *query = caml_stat_alloc(len);
643660
size_t nparams = Wosize_val(v_params);
644661
const char * const *params = copy_params(v_params, nparams);
662+
size_t nparam_types = Wosize_val(v_param_types);
663+
Oid *param_types = copy_param_types(v_param_types, nparams, nparam_types);
645664
int *formats, *lengths;
646665
copy_binary_params(v_params, v_binary_params, nparams, &formats, &lengths);
647666
memcpy(query, String_val(v_query), len);
@@ -651,17 +670,26 @@ CAMLprim value PQexecParams_stub(
651670
(nparams == 0 && !binary_result)
652671
? PQexec(conn, query)
653672
: PQexecParams(
654-
conn, query, nparams, NULL,
673+
conn, query, nparams, param_types,
655674
params, lengths, formats, binary_result);
675+
if (param_types != NULL) caml_stat_free(param_types);
656676
free_binary_params(formats, lengths);
657677
free_params(params, nparams);
658678
caml_stat_free(query);
659679
caml_leave_blocking_section();
660680
CAMLreturn(alloc_result(res, np_cb));
661681
}
662682

683+
CAMLprim value PQexecParams_stub_bc(value *argv, int argn)
684+
{
685+
(void)argn; /* unused */
686+
return
687+
PQexecParams_stub(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
688+
}
689+
663690
#ifdef PG_OCAML_8_2
664-
CAMLprim value PQprepare_stub(value v_conn, value v_stm_name, value v_query)
691+
CAMLprim value PQprepare_stub(
692+
value v_conn, value v_stm_name, value v_query, value v_param_types)
665693
{
666694
CAMLparam1(v_conn);
667695
PGconn *conn = get_conn(v_conn);
@@ -671,10 +699,13 @@ CAMLprim value PQprepare_stub(value v_conn, value v_stm_name, value v_query)
671699
size_t query_len = caml_string_length(v_query) + 1;
672700
char *stm_name = caml_stat_alloc(stm_name_len);
673701
char *query = caml_stat_alloc(query_len);
702+
size_t nparams = Wosize_val(v_param_types);
703+
Oid *param_types = copy_param_types(v_param_types, nparams, nparams);
674704
memcpy(stm_name, String_val(v_stm_name), stm_name_len);
675705
memcpy(query, String_val(v_query), query_len);
676706
caml_enter_blocking_section();
677-
res = PQprepare(conn, stm_name, query, 0, NULL);
707+
res = PQprepare(conn, stm_name, query, nparams, param_types);
708+
if (param_types != NULL) caml_stat_free(param_types);
678709
caml_stat_free(stm_name);
679710
caml_stat_free(query);
680711
caml_leave_blocking_section();
@@ -963,45 +994,56 @@ CAMLprim value PQsetnonblocking_stub_bc(value v_conn, value v_arg)
963994
noalloc_conn_info(PQisnonblocking, Val_bool)
964995

965996
CAMLprim intnat PQsendQueryParams_stub(
966-
value v_conn, value v_query, value v_params, value v_binary_params)
997+
value v_conn, value v_query, value v_param_types, value v_params,
998+
value v_binary_params)
967999
{
9681000
PGconn *conn = get_conn(v_conn);
9691001
const char *query = String_val(v_query);
9701002
size_t nparams = Wosize_val(v_params);
9711003
const char * const *params = copy_params_shallow(v_params, nparams);
1004+
size_t nparam_types = Wosize_val(v_param_types);
1005+
Oid *param_types = copy_param_types(v_param_types, nparams, nparam_types);
9721006
int *lengths, *formats;
9731007
intnat res;
9741008
copy_binary_params(v_params, v_binary_params, nparams, &formats, &lengths);
9751009
res =
9761010
(nparams == 0)
9771011
? PQsendQuery(conn, query)
9781012
: PQsendQueryParams(
979-
conn, query, nparams, NULL, params, lengths, formats, 0);
1013+
conn, query, nparams, param_types, params, lengths, formats, 0);
1014+
if (param_types != NULL) caml_stat_free(param_types);
9801015
free_binary_params(formats, lengths);
9811016
free_params_shallow(params, nparams);
9821017
return res;
9831018
}
9841019

9851020
CAMLprim value PQsendQueryParams_stub_bc(
986-
value v_conn, value v_query, value v_params, value v_binary_params)
1021+
value v_conn, value v_query, value v_param_types, value v_params,
1022+
value v_binary_params)
9871023
{
988-
return
989-
Val_int(PQsendQueryParams_stub(v_conn, v_query, v_params, v_binary_params));
1024+
return Val_int(PQsendQueryParams_stub(
1025+
v_conn, v_query, v_param_types, v_params, v_binary_params));
9901026
}
9911027

9921028
CAMLprim intnat PQsendPrepare_stub(
993-
value v_conn, value v_stm_name, value v_query)
1029+
value v_conn, value v_stm_name, value v_query, value v_param_types)
9941030
{
9951031
PGconn *conn = get_conn(v_conn);
9961032
const char *stm_name = String_val(v_stm_name);
9971033
const char *query = String_val(v_query);
998-
return PQsendPrepare(conn, stm_name, query, 0, NULL);
1034+
size_t nparams = Wosize_val(v_param_types);
1035+
Oid *param_types = copy_param_types(v_param_types, nparams, nparams);
1036+
intnat res;
1037+
res = PQsendPrepare(conn, stm_name, query, nparams, param_types);
1038+
if (param_types != NULL) caml_stat_free(param_types);
1039+
return res;
9991040
}
10001041

10011042
CAMLprim value PQsendPrepare_stub_bc(
1002-
value v_conn, value v_stm_name, value v_query)
1043+
value v_conn, value v_stm_name, value v_query, value v_param_types)
10031044
{
1004-
return Val_int(PQsendPrepare_stub(v_conn, v_stm_name, v_query));
1045+
return
1046+
Val_int(PQsendPrepare_stub(v_conn, v_stm_name, v_query, v_param_types));
10051047
}
10061048

10071049
CAMLprim intnat PQsendQueryPrepared_stub(

0 commit comments

Comments
 (0)