kura_driver behaviour (kura v2.20.7)

View Source

Pluggable database driver behaviour.

A kura_driver implementation is the seam between kura's portable query layer and a specific database client library. Each impl wraps one client (minato, pgo, esqlite, mysql_otp, ...) behind a uniform query/transaction surface.

The pool layer (kura_pool) hands out a connection. The driver layer runs SQL on it. Splitting them lets a single pool impl (e.g. an in-memory test pool) be paired with multiple drivers, and lets a driver be swapped without touching pool semantics.

Three call shapes

query/5 runs SQL on a pool. The driver picks (or already has) a connection. Used by the common case where the caller does not own a checkout.

query_on/4 runs SQL on a specific connection the caller already holds. Used by the sandbox path: the test process owns a checked-out conn and wants every query routed to it.

transaction/3 wraps a fun in a transaction. The driver chooses how to thread connection context into queries inside the fun (process dict, explicit arg, etc.); kura code inside the fun calls kura_db:query/3 as usual and the driver's query/5 impl observes the transaction context.

Today

The canonical impl is kura_driver_minato, and kura_driver_pgo is still shipped. Both keep the in-flight transaction in the process dictionary so that queries written inside a transaction function code that does kura_db:transaction(Repo, fun() -> kura_repo:insert(...) end) continues to work transparently.

Summary

Types

Driver-specific options. The PostgreSQL drivers honor decode_opts, and the minato driver also passes timeout, which is a deadline after which the statement is cancelled on the server. Other drivers may ignore unknown keys or define their own.

Parameter list in the order placeholders appear in the SQL.

Driver-specific result shape. Today this is whatever the underlying client returns (#{rows := [...], num_rows := N, ...} for both PostgreSQL drivers). A future kura layer may normalize this; the driver behaviour does not prescribe a shape.

Query SQL string. Driver impls accept iodata.

Callbacks

Optional. Create the database if missing. Called by kura_migrator.

Optional. Round-trip a trivial query against Pool (e.g. SELECT 1).

Run SQL on a pool. The driver leases a connection (via PoolMod) or threads through an active transaction context, runs the query, and returns the result.

Run SQL on a specific connection the caller already owns. Used by the sandbox path where a test process holds a checked-out conn for its lifetime.

Wrap Fun in a database transaction over a pool. Queries inside Fun route to the transaction's connection by whatever convention the driver uses (both PostgreSQL drivers: the process dictionary).

Types

opts()

-type opts() :: map().

Driver-specific options. The PostgreSQL drivers honor decode_opts, and the minato driver also passes timeout, which is a deadline after which the statement is cancelled on the server. Other drivers may ignore unknown keys or define their own.

params()

-type params() :: [term()].

Parameter list in the order placeholders appear in the SQL.

query_result()

-type query_result() :: dynamic().

Driver-specific result shape. Today this is whatever the underlying client returns (#{rows := [...], num_rows := N, ...} for both PostgreSQL drivers). A future kura layer may normalize this; the driver behaviour does not prescribe a shape.

sql()

-type sql() :: iodata().

Query SQL string. Driver impls accept iodata.

Callbacks

ensure_database(Config)

(optional)
-callback ensure_database(Config :: map()) -> ok | {error, term()}.

Optional. Create the database if missing. Called by kura_migrator.

probe_pool(Pool)

(optional)
-callback probe_pool(Pool :: kura_pool:name()) -> ok | {error, term()}.

Optional. Round-trip a trivial query against Pool (e.g. SELECT 1).

query(PoolMod, Pool, SQL, Params, Opts)

-callback query(PoolMod, Pool, SQL, Params, Opts) -> query_result()
                   when
                       PoolMod :: module(),
                       Pool :: kura_pool:name(),
                       SQL :: sql(),
                       Params :: params(),
                       Opts :: opts().

Run SQL on a pool. The driver leases a connection (via PoolMod) or threads through an active transaction context, runs the query, and returns the result.

query_on(Conn, SQL, Params, Opts)

-callback query_on(Conn, SQL, Params, Opts) -> query_result()
                      when Conn :: kura_pool:conn(), SQL :: sql(), Params :: params(), Opts :: opts().

Run SQL on a specific connection the caller already owns. Used by the sandbox path where a test process holds a checked-out conn for its lifetime.

transaction(PoolMod, Pool, Fun, Opts)

-callback transaction(PoolMod, Pool, Fun, Opts) -> term()
                         when
                             PoolMod :: module(),
                             Pool :: kura_pool:name(),
                             Fun :: fun(() -> term()),
                             Opts :: opts().

Wrap Fun in a database transaction over a pool. Queries inside Fun route to the transaction's connection by whatever convention the driver uses (both PostgreSQL drivers: the process dictionary).

Opts carries driver-specific options. The pgo driver honors pool_options => [{timeout, infinity | non_neg_integer()}] for long-running streams; the minato driver takes no options here, because a statement's deadline is set per query rather than per transaction. Other drivers may ignore unknown keys.