Examples of Caqti infix?

Heya!

I’m trying to wrap my head around how to use the Caqti infix module (it seems the regular one was deprecated.

So I created the following query


let get_user id =
  let query =
    let open Caqti_request.Infix in
    (T.string ->! T.tup4 T.string T.string T.string (T.tup2 T.string T.bool))
      "SELECT id, username, password, privkey, is_admin FROM users WHERE id = ?"
  in
  fun (module Db : DB) ->
    Db.find_opt query id

It works and returns a (LWT result option) type.

I have a few questions:

  1. What does each infix operator mean in caqti? We have ->., ->! and then there is -->! -->. etc.

I have gathered so far that ->. is for unit operations, ->! for when it must return one result and ->? for then the result is optional. However I do not understand the extended arrows.

  1. From what I gather, first parameter for the infix parameter is basically “the input” and then the second parameter is the output. I found that there is Tup4 which I could use. However in this can I have 5 parameter and 4 seems to be the maximum, is there a more ergonomic way (like an infix operator) to handle many columns?

Sorry for not answering the asked question, but have you tried ppx_rapper? It automates away all this boilerplate and expresses queries using something very much like string interpolation–of course, using prepared statement and binding values.

From the docs, the extra - infix operators take a function as a third argument for creating a query (as opposed to a string). I believe the intention is for building up database agnostic queries.

I think Caqti_type ships up to t8 for 8-tuples. But you can compose many together (see caqti 2.0.1 · OCaml Package).

1 Like

The pattern I’ve seen used is something like:

    Caqti_type.(
      let ( & ) = tup2 in
      int & string & string & string & string & string & int & int & string ...
   )

The thing to be aware of is that is a nested tuple not a 999-tuple or anything. That means you need to supply parameters like:

(x.id, (x.name1, (x.name2, (x.something, (...)))))

Or define another infix operator to assemble the parameters.

1 Like

I did try it, but I couldn’t figure out how to make it work with the Dream web server, it expected

( (module Rapper_helper.CONNECTION),
  ([> Caqti_error.call_or_retrieve ] as 'e) )
Caqti_lwt.Pool.t

for a query I wrote


let many_arg_get_opt pool ~username =
  let query = 
    [%rapper
      get_opt
        {sql|
        SELECT @string{username}
        FROM users
        WHERE username = %string{username} 
        |sql}]
  in 
  Caqti_lwt.Pool.use (query ~username) pool

and what you get from Dream is a Caqti_lwt.connection. I couldn’t quite work out the types in order to use query with a caqti connection, only with a pool.

So I can’t find the Caqti method that takes query (module Rapper_helper.CONNECTION) -> (string option, [> Caqti_error.call_or_retrieve ]) result Lwt.t

EDIT: Argh just had a second look at the types, and it seems that Rapper_helper.CONNECTION Is equivalent to the Caqti_lwt.connection. That solves the issue.

1 Like

I have something like this:

Dream.sql request (fun db ->
  let query = [%rapper get_opt {sql| MY SQL REQUEST |sql}] in
  match%lwt query db with
     OK (Some (...)) -> ...
   | OK (None) -> ...
   | Error err -> ... 
)
2 Likes

Ad 1, the API documentation was update in the latest release. I think it should be much more clear than in the release you’re using. I would very much like to know if something is still unclear in that version.

Ad 2, the latest release has tuples up to 8 components, and the next release will have up to 12 components. Since the introduction of product types in the API, it is also fairly simple for the end-user to add higher tuples if needed.

2 Likes