8 type t
= | Int
| Text
| Blob
| Float
| Bool
| Datetime
| Any
9 [@@deriving show
{with_path
=false}]
15 | Any
, _
| _
, Any
-> true
23 | Any
, t
| t
, Any
-> `Order
(t
, Any
)
24 | Int
, Float
| Float
, Int
-> `Order
(Int
,Float
)
25 | Text
, Blob
| Blob
, Text
-> `Order
(Text
,Blob
)
28 let common_type f x y
=
31 | `Order p
-> Some
(f p
)
34 let common_supertype = common_type snd
35 let common_subtype = common_type fst
37 type tyvar
= Typ
of t
| Var
of int
38 let string_of_tyvar = function Typ t
-> to_string t
| Var i
-> sprintf
"'%c" (Char.chr
@@ Char.code 'a'
+ i
)
41 | Group
of t
* bool (* 'a -> t ; bool = multi-column *)
43 | Poly
of t
(* 'a -> 'a -> t *) (* = F (Typ t, [Var 0; Var 0]) *)
44 | Ret
of t
(* _ -> t *)
45 | F
of tyvar
* tyvar list
47 let fixed ret args
= F
(Typ ret
, List.map
(fun t
-> Typ t
) args
)
52 | Agg
-> fprintf pp
"|'a| -> 'a"
53 | Group
(ret
,multi
) -> fprintf pp
"|%s'a| -> %s" (if multi
then "{...} as " else "") (to_string ret
)
54 | Poly ret
-> fprintf pp
"'a -> 'a -> %s" (to_string ret
)
55 | Ret ret
-> fprintf pp
"_ -> %s" (to_string ret
)
56 | F
(ret
, args
) -> fprintf pp
"%s -> %s" (String.concat
" -> " @@ List.map
string_of_tyvar args
) (string_of_tyvar ret
)
58 let string_of_func = Format.asprintf
"%a" pp_func
60 let is_grouping = function
61 | Group _
| Agg
-> true
62 | Ret _
| Poly _
| F _
-> false
67 type conflict_algo
= | Ignore
| Replace
| Abort
| Fail
| Rollback
68 [@@deriving show
{with_path
=false}]
70 type t
= | PrimaryKey
| NotNull
| Unique
| Autoincrement
| OnConflict
of conflict_algo
71 [@@deriving show
{with_path
=false}]
74 type attr
= {name
: string; domain
: Type.t
;}
75 [@@deriving show
{with_path
=false}]
77 let attr n d
= {name
=n
;domain
=d
}
84 exception Error
of t
* string
86 (** FIXME attribute case sensitivity? *)
87 let by_name name
= function attr -> attr.name
= name
88 let find_by_name t name
= List.find_all
(by_name name
) t
91 match find_by_name t name
with
93 | [] -> raise
(Error
(t
,"missing attribute : " ^ name
))
94 | _
-> raise
(Error
(t
,"duplicate attribute : " ^ name
))
96 let make_unique = List.unique ~cmp
:(fun a1 a2
-> a1
.name
= a2
.name
&& a1
.name
<> "")
97 let is_unique t
= List.length
(make_unique t
) = List.length t
98 let check_unique t
= is_unique t
|| raise
(Error
(t
,"duplicate attributes"))
100 let project names t
= List.map
(find t
) names
102 let change_inplace t before after
=
103 List.map
(fun attr ->
104 match by_name before
attr with
108 let cross t1 t2
= t1
@ t2
110 (** [contains t attr] tests whether schema [t] contains attribute [attr] *)
111 let contains t
attr = find t
attr.name
= attr
113 let check_contains t
attr =
114 if not
(contains t
attr) then
115 raise
(Error
(t
,"type mismatch for attribute " ^
attr.name
))
117 let sub l a
= List.filter
(fun x
-> not
(List.mem x a
)) l
119 let to_string v
= v
|> List.map
(fun attr -> sprintf
"%s %s" (Type.to_string attr.domain
) attr.name
) |>
120 String.concat
", " |> sprintf
"[%s]"
121 let names t
= t
|> List.map
(fun attr -> attr.name
) |> String.concat
"," |> sprintf
"[%s]"
124 let (common
,t1only
) = List.partition
(fun x
-> List.mem x t2
) t1
in
125 if 0 = List.length common
then failwith
"natural'";
126 let t2only = sub t2 common
in
127 common
@ t1only
@ t2only
130 try natural_ t1 t2
with
131 | _
-> raise
(Error
(t1
,"no common attributes for natural join of " ^
132 (names t1
) ^
" and " ^
(names t2
)))
134 let join_using l t1 t2
=
135 let common = List.map
(find t1
) l
in
136 List.iter
(check_contains t2
) common;
137 common @ sub t1
common @ sub t2
common
139 let check_types t1 t2
=
140 List.iter2
(fun a1 a2
->
141 match a1
.domain
, a2
.domain
with
144 | x
, y
when x
= y
-> ()
145 | _
-> raise
(Error
(t1
, sprintf
"Atributes do not match : %s of type %s and %s of type %s"
146 a1
.name
(Type.to_string a1
.domain
)
147 a2
.name
(Type.to_string a2
.domain
)))) t1 t2
149 let check_types t1 t2
=
150 try check_types t1 t2
with
151 | List.Different_list_size _
-> raise
(Error
(t1
, (to_string t1
) ^
" differs in size to " ^
(to_string t2
)))
153 let compound t1 t2
= check_types t1 t2
; t1
156 match find_by_name t col
.name
with
161 | `Default
-> t
@ [col
]
164 let (i
,_
) = List.findi
(fun _
attr -> by_name name
attr) t
in
165 let (l1
,l2
) = List.split_nth
(i
+1) t
in
168 Not_found
-> raise
(Error
(t
,"Can't insert column " ^ col
.name ^
" after non-existing column " ^ name
))
170 | _
-> raise
(Error
(t
,"Already has column " ^ col
.name
))
174 List.remove_if
(by_name col
) t
176 let change t oldcol col pos
=
178 | `Default
-> change_inplace t oldcol col
179 | `First
| `After _
-> add (drop t oldcol
) col pos
182 let print x
= prerr_endline
(to_string x
)
186 type table
= string * Schema.t
[@@deriving show
]
187 type schema
= Schema.t
189 let print_table out
(name
,schema
) =
190 IO.write_line out name
;
191 schema
|> List.iter
(fun {name
=name
;domain
=domain
} ->
192 IO.printf out
"%10s %s\n" (Type.to_string domain
) name
);
195 (** optional name and start/end position in string *)
196 type param_id
= string option * (int * int) [@@deriving show
]
197 type param
= param_id
* Type.t
[@@deriving show
]
198 type params
= param list
[@@deriving show
]
200 let params_to_string = show_params
202 type alter_pos
= [ `After
of string | `Default
| `First
]
203 type alter_action
= [ `Add
of attr * alter_pos
| `Drop
of string | `Change
of string * attr * alter_pos
| `None
]
205 type select_result
= (schema
* param list
)
207 type int_or_param
= [`Const
of int | `Limit
of param
]
208 type limit_t
= [ `Limit
| `Offset
]
210 cname
: string; (** column name *)
211 tname
: string option; (** table name *)
213 and limit
= (param_id
* Type.t
) list
* bool
214 and source1
= [ `Select
of select
| `Table
of string ]
215 and source
= source1
* string option
216 and join_cond
= [ `Cross
| `Search
of expr
| `Default
| `Natural
| `Using
of string list
]
218 columns
: column list
;
219 from
: (source
* (source
* join_cond
) list
) option;
222 having
: expr
option;
225 select
: select
* select list
;
227 limit
: limit
option;
230 | Value
of Type.t
(** literal value *)
232 | Fun
of Type.func
* expr list
(** parameters *)
233 | Select
of select_full
* bool (* single *)
235 | Inserted
of string (** inserted value *)
239 | Expr
of expr
* string option (** name *)
240 [@@deriving show
{with_path
=false}]
242 type columns
= column list
[@@deriving show
]
244 type expr_q
= [ `Value
of Type.t
(** literal value *)
246 | `Func
of Type.func
* expr_q list
(** return type, grouping, parameters *)
250 let expr_to_string = show_expr
252 type assignments
= (col_name
* expr
) list
257 action
: [ `Set
of assignments
option
258 | `Values
of (string list
option * expr list list
option) (* column names * list of value tuples *)
259 | `Select
of (string list
option * select_full
) ];
260 on_duplicate
: assignments
option;
264 | Create
of string * [ `Schema
of schema
| `Select
of select_full
]
266 | Alter
of string * alter_action list
267 | CreateIndex
of string * string * string list
(* index name, table name, columns *)
268 | Insert
of insert_action
269 | Delete
of string * expr
option
270 | Set
of string * expr
271 | Update
of string * assignments
* expr
option * expr list
* param list
(* where, order, limit *)
272 | UpdateMulti
of source list
* assignments
* expr
option
273 | Select
of select_full
274 | CreateRoutine
of string * Type.t
option * (string * Type.t
* expr
option) list
279 let test = [{name="a";domain=Type.Int}; {name="b";domain=Type.Int}; {name="c";domain=Type.Text};];;
282 let () = print (project ["b";"c";"b"] test)
283 let () = print (project ["b";"d"] test)
284 let () = print (rename test "a" "new_a")
287 module SMap
= Map.Make
(String
)
289 let functions = ref SMap.empty
291 let add_function k ret
=
293 let k = String.lowercase
k in
294 if SMap.mem
k map
then
295 failwith
(sprintf
"Function %S already defined" k)
299 functions := add !functions k ret
301 let get_function name
=
303 SMap.find (String.lowercase name
) !functions
305 Not_found
-> failwith
(sprintf
"Unknown function %S" name
)
308 let module T
= Type
in
309 let func ret l
= List.iter
(fun x
-> add_function x ret
) l
in
310 func T.Agg
["max";"min";"sum"]; (* TODO in sqlite3 min(a,b) acts as least(a,b), while min(a) is grouping *)
311 func T.(Group
(Int
,true)) ["count"];
312 func T.(Group
(Float
,false)) ["avg"];
313 func T.(fixed Text
[Text
;Text
]) ["strftime"];
314 func T.(fixed Text
[Text
]) ["lower";"upper"];
315 func T.(Ret Text
) ["concat"];
316 func T.(Ret Any
) ["coalesce"];
317 func T.(Ret Int
) ["length"; "random";"unix_timestamp";"least";"greatest"];