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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
(** Letsqlite main interface. *)
(** {1 Type definitions}
Types used for monad composition.
*)
type stmt_wrap = bool * Sqlite3.stmt
type 'a stmt_m =
| Failed of Sqlite3.Rc.t
| Norm of ('a * stmt_wrap)
(** The ['a] value in the [stmt_m] is referred to as the bundled value.
A function may "fail with [e]", in which it returns [Failed e],
or it may "return a value", in which it returns [Norm v s].
They are collectively refered to as the monad's values.
The bundled {!Sqlite.stmt} value is refered to as [S], and the
value bundled is refered to as [V].
The boolean value is the {i preserve on failure} flag. When true,
monadic functions will not call {!Sqlite3.finalize} on the contained
statement. {b This will cause a memory leak if not handled correctly.}
{i Never directly use [Failed].} You run the likelihood of leaking
memory or causing a crash.
*)
type ('a,'b) monad_fun = ('a * stmt_wrap) -> 'b stmt_m
type rowdata = (string, Sqlite3.Data.t) Hashtbl.t
(** {1 Primary Functions} *)
val (>>$) : 'a stmt_m -> ('a,'b) monad_fun -> 'b stmt_m
(** [>>$] is the monadic composition function.
[s >>$ f] return [s] if [s] is [Failed] and [f V S] otherwise.
*)
val (let$) : 'a stmt_m -> ('a,'b) monad_fun -> 'b stmt_m
(** [let$ (v,s) = f (V,S) in ...] is equivalent to
[f (V,S) >>$ (fun (v,s) -> ...)]. *)
val (@>$) : ('a,'b) monad_fun -> ('b,'c) monad_fun -> ('a,'c) monad_fun
(** [f @>$ g] composes to monadic functions so they become another
monadic function. *)
val (>-$) : 'a stmt_m -> ('a,'b) monad_fun -> 'a stmt_m
(** [>-$] is the passthrough function.
[s >-$ f] is equivalent to [s >>$ f] except that the bundled
value from [f (V,S)] is discarded and replaced with [V]. This function
passes the original [V] through.
*)
val gs : stmt_wrap -> Sqlite3.stmt
(** [gs s] returns the statment in an instance of [stmt_wrap]. *)
(** {1 Non-Execution Functions}
These functions do not execute any SQL queries. They exist to make dealing
with the monad easier.
*)
val transform : ('a -> 'b) -> ('a,'b) monad_fun
(** [transform f] maps [V] to [f V]. *)
val inject : 'a -> ('b,'a) monad_fun
(** [inject x] returns [Norm x S], discarding [V]. *)
val fail : Sqlite3.Rc.t -> ('a,'b) monad_fun
(** [fail e] finalizes [S] and returns [Failure], where the error
code is [e] if [e] is not a success error code, and the return code
of {!Sqlite3.finalize} otherwise.
*)
val stmtfail : Sqlite3.Rc.t -> stmt_wrap -> unit stmt_m
(** [stmtfail e s] is equivalent to [fail] except it assembles a statement
with unit type. *)
val lift_err : Sqlite3.Rc.t -> ('a,'a) monad_fun
(** [lift_err e] is equivalent to [fail e] when [e] is not a success
value, and otherwise returns the monad's values unchanged.
{b NOTE}: You may be tempted to define a function like
[lift : (Sqlite3.stmt -> Sqlite3.Rc.t) -> ('a,'a) monad_fun]
which would take a function and apply [lift_err (f S) V S]
to the function. However, the partial application [lift f]
would leave you with a {i weak type}, which will force your
type to have a fixed type. *)
(** {1 Execution functions}
All of these functions will (effectively) exceute {!fail} and return
[Failed] on failure. All functions will omit error behavior unless
there is something different across any of them. *)
val prepare : Sqlite3.db -> string -> 'a -> 'a stmt_m
(** [prepare db stmt v] returns a monad with bundled value [v] and
the statement in [s].
*)
val prepare0 : Sqlite3.db -> string -> unit stmt_m
(** [prepare0 db stmt] is equivalent to [prepare db stmt ()]. *)
val prepare_keep : bool -> Sqlite3.db -> string -> 'a -> 'a stmt_m
(** [prepare_keep b] is like [prepare], but [b] is the preserve-on-failure
file.
*)
val reprepare : Sqlite3.db -> string -> ('a,'a) monad_fun
(** [reprepare db stmt] finalizes [S] and prepares [stmt] in
its place. *)
val reset : ('a,'a) monad_fun
(** [reset] resets [S] so that it may be executed again. *)
val step : ('a,'a) monad_fun
(** [step] is equivalent to {!Sqlite3.step}.
{b NOTE}: [step] will fail when the statement returns a row.
*)
val rowfold : ('a -> rowdata -> ('a,Sqlite3.Rc.t) Result.t) -> ('a,'a) monad_fun
(** [rowfold f] iterates over each row returns by [S] and applies
[f] to it, returning some value. *)
val iter : (rowdata -> Sqlite3.Rc.t) -> ('a,unit) monad_fun
(** [rowfold_unit f] is equivalent to [rowfold_init f ()] but [f]
is not passed a unit argument. *)
val map : ('a -> ('b,'c) monad_fun) -> 'a list -> ('b,'c list) monad_fun
(** [map f l] applies the monadic function [f] to each element of [l],
returning all the list of all their values. *)
val fold : ('a -> ('b,'b) monad_fun) -> 'a list -> ('b,'b) monad_fun
(** [fold f l] applies each element of [l] to make a monadic function [f]. *)
val get_exactly_one_row : ('a,(unit * rowdata)) monad_fun
(** [get_exactly_one_row] steps [S] and stores the returned row. The
value in [S] is discarded. The function fails if there is not exactly
one row.
The function returns a tuple with the unit type to interact with
{!extract}.
*)
val exec_extract : ('a -> 'b option) -> ('b -> 'c option)
-> (('d * 'a),('c * 'a)) monad_fun
(** [exec_extract extr conv] extracts a value from the second element in
[V], and then applies [conv] to it. If both were successful, the
function replaces the first element of [V] with the converted
value. *)
val extract : string -> (Sqlite3.Data.t -> 'b option)
-> (('a * rowdata),('b * rowdata)) monad_fun
(** [extract name f] finds the value in the row with header name [name]
and applies [f] to it. If [f] succeeds then its return value is
stored in [V]. The rowdata is preserved.
*)
val finalize : 'a stmt_m -> 'a
(** [finalize] raises an exception if the execution failed, and [V]
otherwise. *)
val check : 'a stmt_m -> ('a * 'a stmt_m)
(** [check] checks if the statement is an error value and throws an
exception if it is. If it is not, then it returns [(V,S)]. *)
val bind_values : Sqlite3.Data.t list -> ('a,'a) monad_fun
(** [bind_values l] binds each index of [l] to a positional parameter
in [S]. *)
val clear_bindings : ('a,'a) monad_fun
(** [clear_bindings] clears any values bound to [S]. *)
val exec : Sqlite3.db -> string -> unit stmt_m
(** [exec db s] is equivalent to a call to {!prepare} followed by {!step}. *)
val reexec : Sqlite3.db -> string -> ('a,'a) monad_fun
(** [reexec db s] is equivalent to [reprepare >>$ step]. *)
val bsrc : Sqlite3.Data.t list -> ('a,'a) monad_fun
(** [bsrc l] is equivalent to [bind_values >>$ step >>$ reset >>$ clear_bindings]. *)
|