Request specification.
A Caqti request is a function to generate a query string from information about the driver, along with type descriptors to encode parameters and decode rows returned from the same query. Requests are passed to Caqti_connection_sig.S.call
or one of its shortcut methods provided by a database connection handle.
The request often represent a prepared query, in which case it is static and can be defined directly in a module scope. However, an optional oneshot
parameter may be passed to indicate a dynamically generated query.
Primitives
type ('a, 'b, +'m) t constraint 'm = [< `Zero | `One | `Many ]
A request specification embedding a query generator, parameter encoder, and row decoder.
'a
is the type of the expected parameter bundle.'b
is the type of a returned row.'m
is the possible multiplicities of returned rows.
create arg_type row_type row_mult f
is a request which takes parameters of type arg_type
, returns rows of type row_type
with multiplicity row_mult
, and which sends query strings generated from the query f di
, where di
is the Caqti_driver_info.t
of the target driver. The driver is responsible for turning parameter references into a form accepted by the database, while other differences must be handled by f
.
param_type req
is the type of parameter bundles expected by req
.
row_type req
is the type of rows returned by req
.
row_mult req
indicates how many rows req
may return. This is asserted when constructing the query.
val query_id : ('a, 'b, 'm) t -> int option
If req
is a prepared query, then query_id req
is Some id
for some id
which uniquely identifies req
, otherwise it is None
.
query req
is the function which generates the query of this request possibly tailored for the given driver.
Convenience
In the following functions, queries are written out as plain strings with the following syntax, which is parsed by Caqti into a Caqti_query.t
object before being passed to drivers.
Parameters are specified as either
"?"
for linear substitutions (like Sqlite and MariaDB), or"$1"
, "$2"
, ... for non-linear substitutions (like PostgreSQL).
Mixing the two styles in the same query string is not permitted. Note that numbering of non-linear parameters is offset by one compared to the P
parameters of the query objects defined in the previous section, in order to be consistent with PostgreSQL conventions.
Static references are references to the ?env
argument of the functions below, and thus fixed once the query has been constructed. The query parser accepts two forms:
"$(<var>)"
is substituted by env driver_info "<var>"
."$(<var>.)"
, if not found by the first rule, is substituted by env driver_info "<var>"
followed by a dot iff that result is nonempty."$<var>."
is a shortcut for "$(<var>.)"
.
These aid in substituting configurable fragments, like database schemas or table names. The latter form is suggested for qualifying tables, sequences, etc. with the main database schema. It should expand to a schema name followed by a dot, so that the empty string can be returned if the database does not support schemas or no schema is requested by the user.
Finally,
- Dollar signs in single-quoted strings are left unchanged.
"$<var>$"
is left unchanged."$$"
will be left unchanged in future versions, see the notice below.
Apart from the more generic create_p
, these function match up with retrieval functions of Caqti_connection_sig.S
and Caqti_response_sig.S
according to the multiplicity parameter of their types.
Deprecation of undocumented feature. It has been possible to quote the dollar sign by doubling it. This was undocumented and is hereby deprecated. If you need a plain dollar sign outside a quoted strings, you can add a variable to your environment, which expands to the dollar sign.
create_p arg_type row_type row_mult f
is a request which takes parameters of type arg_type
, returns rows of type row_type
with multiplicity row_mult
, and which sends a query string based on a preliminary form given by f di
, where di
is the Caqti_driver_info.t
of the target driver. The preliminary query string may contain parameter and static references as described in the introduction of this section.
exec_p arg_type s
is a shortcut for create_p arg_type Caqti_type.unit Caqti_mult.zero (fun _ -> s)
.
find_p arg_type row_type s
is a shortcut for create_p arg_type row_type Caqti_mult.one (fun _ -> s)
.
find_opt_p arg_type row_type s
is a shortcut for create_p arg_type row_type Caqti_mult.zero_or_one (fun _ -> s)
.
collect_p arg_type row_type s
is a shortcut for create_p arg_type row_type Caqti_mult.many (fun _ -> s)
.
val pp : Stdlib.Format.formatter -> ('a, 'b, 'm) t -> unit
pp ppf req
prints req
on ppf
in a form suitable for human inspection.
val pp_with_param :
?driver_info:Caqti_driver_info.t ->
Stdlib.Format.formatter ->
(('a, 'b, 'm) t * 'a) ->
unit
pp_with_param ppf (req, param)
prints req
and the associated param
to ppf
. This functions is meant for debugging; the output is neither guaranteed to be consistent across releases nor to contain a complete record of the data.
Due to concerns about exposure of sensitive data in debug logs, this function reverts to pp
unless the environment varibale CAQTI_DEBUG_PARAM
is set to true
. If you enable it for applications which do not consistenly annotate sensitive parameters with Caqti_type.redact
, make sure your debug logs are well secured.
How to Dynamically Assemble Queries and Parameters
In some cases, queries are constructed dynamically, e.g. when translating an expression for searching a database into SQL. In such cases the number of parameters and their types will typically vary, as well. A helper like the following can be used to existentially pack the parameter types along with the corresponding parameter values to allow collecing them incrementally:
module Dynparam = struct
type t = Pack : 'a Caqti_type.t * 'a -> t
let empty = Pack (Caqti_type.unit, ())
let add t x (Pack (t', x')) = Pack (Caqti_type.tup2 t' t, (x', x))
end
Now, given a param : Dynparam.t
and a corresponding query string qs
, one can construct a request and execute it:
let Dynparam.Pack (pt, pv) = param in
let req = Caqti_request.exec ~oneshot:true pt qs in
C.exec req pv
Note that dynamically constructed requests should have ~oneshot:true
unless they are memoized. Also note that it is natural to use create
for dynamically constructed queries, since it accepts the easily composible Caqti_query.t
type instead of plain strings.
This scheme can be specialized for particular use cases, including generation of fragments of the query
, which reduces the risk of wrongly matching up parameters with their uses in the query string.