Repodb.Multi

repodb · API reference

Transactional composition of named database operations.

A multi is an ordered set of named operations executed inside one transaction. Each operation can read results produced by earlier operations, which makes it useful for workflows such as "insert a user, then insert an audit event with that user id".

let create_user_and_log email =
  Multi.empty
  |> Multi.insert_returning "user" ~table:User.table ~columns:[ "email" ]
       ~values:[ Driver.Value.text email ]
  |> Multi.insert_fn "audit" (fun results ->
      let row = Multi.get_row_raw_exn results "user" in
      let user_id = Driver.row_int64 row "id" in
      ( Audit.table,
        [ "user_id"; "event" ],
        [ Driver.Value.int64 user_id; Driver.Value.text "created" ] ))

Make(D).execute conn multi returns either all named results or a multi_error containing the failing operation name, normalized database error, and results completed before the failure. The surrounding transaction is rolled back on failure, so completed is diagnostic information, not a list of committed writes.

Use validate_multi in tests or CLIs to catch duplicate operation names before execution. Prefer descriptive names because they are the keys used to retrieve results and the names reported in errors.

module StringMap : sig ... end
type stored_result = 
  | Unit
  | Row of Driver.row
  | Rows of Driver.row list
type results = stored_result StringMap.t
type multi_error = {
  failed_operation : string;
  error : Error.db_error;
  completed : results;
}
val empty_results : 'a StringMap.t
val get_unit : stored_result StringMap.t -> StringMap.key -> unit option
val get_unit_exn : stored_result StringMap.t -> StringMap.key -> unit
val get_row : 
  stored_result StringMap.t ->
  StringMap.key ->
  decode:(Driver.row -> 'a) ->
  'a option
val get_row_exn : 
  stored_result StringMap.t ->
  StringMap.key ->
  decode:(Driver.row -> 'a) ->
  'a
val get_row_raw : 
  stored_result StringMap.t ->
  StringMap.key ->
  Driver.row option
val get_row_raw_exn : stored_result StringMap.t -> StringMap.key -> Driver.row
val get_rows : 
  stored_result StringMap.t ->
  StringMap.key ->
  decode:(Driver.row -> 'a) ->
  'a list option
val get_rows_exn : 
  stored_result StringMap.t ->
  StringMap.key ->
  decode:(Driver.row -> 'a) ->
  'a list
val get_rows_raw : 
  stored_result StringMap.t ->
  StringMap.key ->
  Driver.row list option
val get_rows_raw_exn : 
  stored_result StringMap.t ->
  StringMap.key ->
  Driver.row list
val put_unit : StringMap.key -> stored_result StringMap.t -> results
val put_row : 
  StringMap.key ->
  Driver.row ->
  stored_result StringMap.t ->
  results
val put_rows : 
  StringMap.key ->
  Driver.row list ->
  stored_result StringMap.t ->
  results
type 'conn operation = 
  | Insert of {
    table : Schema.table;
    columns : string list;
    values : Driver.Value.t list;
  }
  | InsertFn of {
    f : results -> Schema.table * string list * Driver.Value.t list;
  }
  | InsertReturning of {
    table : Schema.table;
    columns : string list;
    values : Driver.Value.t list;
  }
  | InsertReturningFn of {
    f : results -> Schema.table * string list * Driver.Value.t list;
  }
  | Update of {
    table : Schema.table;
    columns : string list;
    values : Driver.Value.t list;
    where_column : string;
    where_value : Driver.Value.t;
  }
  | UpdateFn of {
    f : results ->
    Schema.table * string list * Driver.Value.t list * string * Driver.Value.t;
  }
  | Delete of {
    table : Schema.table;
    where_column : string;
    where_value : Driver.Value.t;
  }
  | DeleteFn of {
    f : results -> Schema.table * string * Driver.Value.t;
  }
  | Run of {
    f : 'conn -> results -> (stored_result, Error.db_error) result;
  }
type 'conn entry = {
  name : string;
  op : 'conn operation;
}
type 'conn t = 'conn entry list
val new_multi : unit -> 'conn t
val empty : 'conn t
val insert : 
  string ->
  table:Schema.table ->
  columns:string list ->
  values:Driver.Value.t list ->
  'conn t ->
  'conn t
val insert_fn : 
  string ->
  f:(results -> Schema.table * string list * Driver.Value.t list) ->
  'conn t ->
  'conn t
val insert_returning : 
  string ->
  table:Schema.table ->
  columns:string list ->
  values:Driver.Value.t list ->
  'conn t ->
  'conn t
val insert_returning_fn : 
  string ->
  f:(results -> Schema.table * string list * Driver.Value.t list) ->
  'conn t ->
  'conn t
val update : 
  string ->
  table:Schema.table ->
  columns:string list ->
  values:Driver.Value.t list ->
  where_column:string ->
  where_value:Driver.Value.t ->
  'conn t ->
  'conn t
val update_fn : 
  string ->
  f:
    (results ->
      Schema.table
      * string list
      * Driver.Value.t list
      * string
      * Driver.Value.t) ->
  'conn t ->
  'conn t
val delete : 
  string ->
  table:Schema.table ->
  where_column:string ->
  where_value:Driver.Value.t ->
  'conn t ->
  'conn t
val delete_fn : 
  string ->
  f:(results -> Schema.table * string * Driver.Value.t) ->
  'conn t ->
  'conn t
val run : 
  string ->
  f:('conn -> results -> (stored_result, Error.db_error) result) ->
  'conn t ->
  'conn t
val run_no_result : 
  string ->
  f:('conn -> results -> (unit, Error.db_error) result) ->
  'conn t ->
  'conn t
val run_unit : 
  string ->
  f:('conn -> results -> (unit, Error.db_error) result) ->
  'conn t ->
  'conn t
val run_row : 
  string ->
  f:('conn -> results -> (Driver.row, Error.db_error) result) ->
  'conn t ->
  'conn t
val run_rows : 
  string ->
  f:('conn -> results -> (Driver.row list, Error.db_error) result) ->
  'conn t ->
  'conn t
val merge : 'conn t -> 'conn t -> 'conn t
val to_list : 'conn t -> 'conn t
val names : 'conn t -> string list
val has_name : string -> 'conn t -> bool
val validate_multi : 'conn t -> (unit, string) result
module Make (D : Driver.S) : sig ... end