aboutsummaryrefslogtreecommitdiffstats
path: root/lib/management/storage/lmdb.ml
blob: 80153cddad24b5e82c7e8567b83c44a1d5f28aae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
let not_implemented = Obj.magic ()

module Backend : Physical.BACKEND = struct
  open External.Lmdb
  open Ctypes

  module MDB_Errors = struct
    let mdb_notfound = -30798
  end

  type configuration = { path : string; name : string }
  type connection = { env : mdb_env structure ptr; dbi : mdb_dbi }
  type error = int

  type _ Effect.t +=
     | Current: mdb_txn structure ptr Effect.t
     | Rollback: unit Effect.t

  let parse (expr : Sexplib.Sexp.t) =
    let open Utilities.Result in
    let* keys = Configuration.map_of_sexp expr in
    let* path = Utilities.StringMap.find_opt "path" keys
                |> Option.to_result ~none:"Missing key: path"
                |> fmap (Configuration.atom_of_sexp)
    in
    let* name = Utilities.StringMap.find_opt "name" keys
                |> Option.to_result ~none:"Missing key: name"
                |> fmap (Configuration.atom_of_sexp)
    in
    Ok { path; name }

  let current_tx () = Effect.perform Current
  let current_tx' () = try current_tx () with Effect.Unhandled Current -> (from_voidp mdb_txn null)

  let with_transaction' env body =
    let open Utilities.Result in
    let success = ref false in
    let* tx = mdb_txn_begin' env (current_tx' ()) (Unsigned.UInt.of_int 0) in
    Fun.protect
      (fun () ->
        match body () with
        | x ->
           let* () = mdb_txn_commit tx in
           success := true;
           Ok (Some x)
        | effect Current, k -> Effect.Deep.continue k tx
        (* Should a transaction abort be an error value? *)
        | effect Rollback, _ ->
           let* () = mdb_txn_abort tx in
           success := true;
           Ok None)
      ~finally:(fun () -> if not !success then mdb_txn_abort tx |> ignore)

  let with_transaction { env; _ } body = with_transaction' env body

  let connect { path; name } =
    let open Utilities.Result in
    let* env = mdb_env_create' () in
    match mdb_env_open env path (Unsigned.UInt.of_int 0) PosixTypes.Mode.zero with
    | Error x ->
       mdb_env_close env;
       Error x
    | Ok () ->
       Result.bind
         (with_transaction' env
            (fun () ->
              let* dbi = mdb_dbi_open' (current_tx ()) name (Unsigned.UInt.of_int 0) in
              Ok { env; dbi }))
         Option.get

  let disconnect { env; _ } =
    mdb_env_close env

  let begin_transaction _conn = not_implemented

  let commit _conn = not_implemented

  let rollback _conn = not_implemented

  let get { dbi; _ } hash =
    match mdb_get' (current_tx ()) dbi (Bytes.of_string hash) with
    | Ok x -> Ok (Some x)
    | Error e when e = MDB_Errors.mdb_notfound -> Ok (None)
    | Error e -> Error e

  let put _conn _hash _value = not_implemented

  let exists _conn _hash = not_implemented

  let get_many _conn _hashes = not_implemented

  let put_many _conn _values = not_implemented
end
[@warning "-38-32"]