I am trying to write something like:
Caqti_type.Std.(string ->* t2 int string)
"SELECT id, content from post WHERE content LIKE %?%"
and I got this exception “‘%’ is not allowed after parameter reference ‘?’”, which is the expected result as mentioned here.
Is there a way to make it work besides using sprintf or ^ to inject the search term myself?
(Edit: I wrote a self contained program for the case)
module type CONN = Caqti_blocking.CONNECTION
let sql_uri = "sqlite3::memory:"
module Q = struct
open Caqti_request.Infix
open Caqti_type.Std
let create_table =
(unit ->. unit)
@@ {sql|
CREATE TABLE post (
id INTEGER PRIMARY KEY,
content TEXT
)
|sql}
let add = (string ->. unit) @@ "INSERT INTO post (content) VALUES (?)"
let all = (unit ->* t2 int string) @@ "SELECT id, content from post"
let search =
(string ->* t2 int string)
@@ "SELECT id, content from post WHERE content LIKE %$1%"
end
let connect () = Caqti_blocking.connect (Uri.of_string sql_uri)
let create_table (module Conn : CONN) = Conn.exec Q.create_table ()
let populate (module Conn : CONN) =
let ( >>= ) = Result.bind in
let add p () = Conn.exec Q.add p in
add "Post 01" () >>= add "Post 02" >>= add "Post 03"
let list_all (module Conn : CONN) = Conn.collect_list Q.all ()
let search text (module Conn : CONN) = Conn.collect_list Q.search text
let print_posts = List.iter (fun (id, post) -> Printf.printf "%d\t%s\n" id post)
let run () =
let ( let* ) = Result.bind in
let* conn = connect () in
let* () = create_table conn in
let* () = populate conn in
Result.map print_posts @@ list_all conn
let run2 () =
let ( let* ) = Result.bind in
let* conn = connect () in
Result.map print_posts @@ search "Post" conn
let () =
match run () with
| Error e ->
print_endline "Failed. ";
print_endline (Caqti_error.show e)
| Ok () -> print_endline "done."
;
(* the following section will fail *)
match run2 () with
| Error e ->
print_endline "Failed. ";
print_endline (Caqti_error.show e)
| Ok () -> print_endline "done."
Output:
$ dune exec ./q.exe
1 Post 01
2 Post 02
3 Post 03
done.
Failed.
Request to <sqlite3::memory:> failed: Sqlite3.prepare: near "%": syntax error. Query: "SELECT id, content from post WHERE content LIKE %?1%".
(Edit: Solved version here, thanks to @paurkedal , I also added the missing part of populating the db in run2)
TLDR: use ‘%’ || ? || ‘%’
module type CONN = Caqti_blocking.CONNECTION
let sql_uri = "sqlite3::memory:"
module Q = struct
open Caqti_request.Infix
open Caqti_type.Std
let create_table =
(unit ->. unit)
@@ {sql|
CREATE TABLE post (
id INTEGER PRIMARY KEY,
content TEXT
)
|sql}
let add = (string ->. unit) @@ "INSERT INTO post (content) VALUES (?)"
let all = (unit ->* t2 int string) @@ "SELECT id, content from post"
let search =
(string ->* t2 int string)
@@ "SELECT id, content from post WHERE content LIKE '%' || ? || '%'"
end
let connect () = Caqti_blocking.connect (Uri.of_string sql_uri)
let create_table (module Conn : CONN) = Conn.exec Q.create_table ()
let populate (module Conn : CONN) =
let ( >>= ) = Result.bind in
let add p () = Conn.exec Q.add p in
add "Post 01" () >>= add "Post 02" >>= add "Post 03"
let list_all (module Conn : CONN) = Conn.collect_list Q.all ()
let search text (module Conn : CONN) = Conn.collect_list Q.search text
let print_posts = List.iter (fun (id, post) -> Printf.printf "%d\t%s\n" id post)
let run () =
let ( let* ) = Result.bind in
let* conn = connect () in
let* () = create_table conn in
let* () = populate conn in
Result.map print_posts @@ list_all conn
let run2 () =
let ( let* ) = Result.bind in
let* conn = connect () in
let* () = create_table conn in
let* () = populate conn in
Result.map print_posts @@ search "Post" conn
let () =
match run () with
| Error e ->
print_endline "Failed. ";
print_endline (Caqti_error.show e)
| Ok () -> print_endline "done."
;
match run2 () with
| Error e ->
print_endline "Failed. ";
print_endline (Caqti_error.show e)
| Ok () -> print_endline "done."