A am please to finally announce the first release of Caqti to our main opam repository. Caqti is an RDBMS client library for OCaml, currently shipped with drivers for MariaDB, PostgreSQL, and Sqlite3. In additional to serving as a compatibility layer, it provides monadic concurrency with support for Lwt and Async.
TL;DR (yes it is; hopefully some of you will read anyway): There is an example linked from the GitHub page.
Design
Caqti accepts a URI which provides the location, authentication, and configuration for a particular database, and returns a pool of first-class modules implementing a common signature used to execute queries. The pooled modules includes the connection object, so they are meant to be used only in local contexts. Automatic loading of drivers is provided by linking against caqti-dynload
, otherwise link against the needed drivers.
The client can define fixed requests (queries) at the module level. When executed, Caqti prepares and caches the prepared statements within the module corresponding to the connection. Caching of prepared statements can be disabled in case the query is generated on the fly.
Caqti handles encoding and decoding of results based on types specified along with the queries. Most common datatypes are supported except for the time-of-day (time
) type. The elementary types are represented as an open variant, which can be extended by providing conversion functions between the types implemented by drivers. On top of the elementary types, on can form tuples and custom types converted to and from tuples, though these custom types should be considered experimental for now.
On the other hand, Caqti does not provide a DSL for defining queries. Queries a basically strings, though in the form of templates in order to ease generation of queries strings and to support both ?
-style and $n
-style parameters independent of which database driver is used. The primitive request constructor actually takes a function which receives information about the driver and returns a template, in order to allow the client code to dispatch between different RDBMS where needed.
In other words, the focus of Caqti is to provide the most basic compatibility layer to allow writing code which works across RDBMS. It is okay to use it directly, but there is also room for higher level interfaces like code generators or DSLs implemented in plain OCaml or with the help of PPXes. I have seen several good approaches, but I’ll leave that for the discussion and future work.
Status
The API underwent some redesign this fall, but is largely based on code which I have used myself for a few years. Lwt and PostgreSQL is the best tested components, since I’ve used them in all my production code, though with help from @andrenth, I believe the MariaDB component is fairly solid too now. Both PostgreSQL and MariaDB drivers are based on asynchronous calls provided by the respective C bindings. The Sqlite3 driver is a fairly straight-forward preemptive wrapper around the already stable C bindings.
There may still be issues with conversion of certain values depending on databases, especially when the type cannot be inferred. Handling conversion as best as possible is one of the goals of Caqti, so please report it in the issue tracker or submit a PR with a test case.
This being the first properly announced release, I think it is also a good time for you to report back if we need adjustments to the API. Consider that also a warning, though I hope we can avoid substatial redesign.
The API documentation is not on-line at the moment, but can be generated with odig odoc
.
Please ignore the Caqti1_*
modules and *.v1
findlib libraries, they will be deprecated (as soon as I rewrite the epiSQL code generator, which is not released in opam-repository yet).
Thanks
Thanks to Markus Mottl, Alain Frisch, Christian Szegedy, and Andre Nathan for a great job on providing bindings to the C libraries, which requires special expertise in memory management, debugging, and packaging.