1 (** SQL syntax and RA *)
10 tables
: Tables.table list
;
11 joined_schema
: Schema.t
;
12 insert_schema
: Schema.t
;
15 let empty_env = { tables
= []; joined_schema
= []; insert_schema
= []; }
17 let collect f l
= List.flatten
(List.map f l
)
20 let schema_as_params = List.map
(fun attr
-> (Some attr
.name
,(0,0)), Some attr
.domain
)
22 let schema_of tables name
= snd
@@ Tables.get_from tables name
24 let values_or_all table names
=
25 let schema = Tables.get_schema table
in
27 | Some names
-> Schema.project names
schema
30 let list_filter_map = ExtList.List.filter_map
36 | `Func
(_
,l
) -> List.fold_left
loop acc l
41 let rec is_singular = function
46 (* grouping function of zero or single parameter or function of all singular values *)
47 (Type.is_grouping func
&& List.length args
<= 1) || List.for_all
is_singular args
48 | Select _
-> false (* ? *)
49 | Inserted _
-> false (* ? *)
51 let test_all_grouping columns
=
52 List.for_all
(function Expr
(e
,_
) -> is_singular e
| All
| AllOf _
-> false) columns
54 let cross = List.fold_left
Schema.cross []
56 (* all columns from tables, without duplicates *)
57 (* FIXME check type of duplicates *)
58 let all_columns = Schema.make_unique $
cross
59 let all_tbl_columns = all_columns $
List.map snd
61 let resolve_column tables joined_schema
{cname
;tname
} =
62 Schema.find
(Option.map_default
(schema_of tables
) joined_schema tname
) cname
64 let resolve_column_assignments tables l
=
65 let all = all_tbl_columns tables
in
66 l
|> List.map
begin fun (col
,expr
) ->
67 (* hint expression to unify with the column type *)
68 let typ = (resolve_column tables
all col
).domain
in
69 Fun
(Type.(Ret Any
), [Value
typ;expr
])
72 let get_columns_schema tables l
=
73 let all = all_tbl_columns tables
in
75 l
|> List.map
(fun col
-> { name
= col
.cname
; domain
= (resolve_column tables
all col
).domain
; })
77 (** replace each name reference (Column, Inserted, etc) with Value of corresponding type *)
78 let rec resolve_columns env expr
=
81 eprintf
"\nRESOLVE COLUMNS %s\n%!" (expr_to_string expr
);
82 eprintf
"schema: "; Sql.Schema.print env
.joined_schema
;
83 Tables.print stderr env
.tables
;
88 | Column col
-> `Value
(resolve_column env
.tables env
.joined_schema col
).domain
90 let attr = try Schema.find env
.insert_schema name
with Schema.Error
(_
,s
) -> fail
"for inserted values : %s" s
in
94 `Func
(r
,List.map
each l
)
95 | Select
(select
,single
) ->
96 let as_params = List.map
(fun x
-> `Param x
) in
97 let (schema,p
,_
) = eval_select_full env select
in
98 match schema,single
with
99 | [ {domain
;_
} ], true -> `Func
(Type.Ret domain
, as_params p
)
100 | s
, true -> raise
(Schema.Error
(s
, "only one column allowed for SELECT operator in this expression"))
101 | _
-> fail
"not implemented: multi-column select in expression"
105 (** assign types to parameters where possible *)
106 and assign_types expr
=
107 let rec typeof (e
:expr_q
) = (* FIXME simplify *)
110 | `Param
(_
,t
) -> e
, t
111 | `Func
(func
,params
) ->
113 let (params
,types
) = params
|> List.map
typeof |> List.split
in
115 sprintf
"%s applied to (%s)"
116 (string_of_func func
)
117 (String.concat
", " @@ List.map to_string types
)
119 let (ret
,inferred_params
) = match func
, types
with
120 | Agg
, [typ] -> typ, types
121 | Group
(ret
,false), [_
]
122 | Group
(ret
,true), _
-> ret
, types
123 | (Agg
| Group _
), _
-> fail
"cannot use this grouping function with %d parameters" (List.length types
)
124 | F
(_
, args
), _
when List.length args
<> List.length types
-> fail
"types do not match : %s" (show ())
125 | F
(ret
, args
), _
->
126 let typevar = Hashtbl.create
10 in
127 let l = List.map2
begin fun arg
typ ->
129 | Typ arg
-> matches arg
typ
132 match Hashtbl.find
typevar i
with
133 | exception Not_found
-> Hashtbl.replace
typevar i
typ; typ
136 (* prefer more precise type *)
137 if arg = Type.Any
then Hashtbl.replace
typevar i
typ;
141 let convert = function Typ t
-> t
| Var i
-> Hashtbl.find
typevar i
in
142 if List.fold_left
(&&) true l then
143 convert ret
, List.map
convert args
145 fail
"types do not match : %s" (show ())
146 | Ret Any
, _
-> (* lame - make a best guess, return type same as for parameters *)
147 begin match List.filter
((<>) Any
) types
with
149 | h
::tl
when List.for_all
(matches h
) tl
-> h
, List.map
(fun _
-> h
) types
152 | Ret ret
, _
-> ret
, types
(* ignoring arguments FIXME *)
154 match List.filter
((<>) Any
) types
with
156 | h
::tl
when List.for_all
(matches h
) tl
-> ret
, List.map
(fun _
-> h
) types
157 | _
-> fail
"all parameters should have same type : %s" (show ())
159 let assign inferred x
=
161 | `Param
(n
,Any
) -> `Param
(n
, inferred
)
164 `Func
(func
,(List.map2
assign inferred_params params
)), ret
168 and resolve_types env expr
=
169 let expr = resolve_columns env
expr in
171 let (expr'
,t
as r
) = assign_types
expr in
172 if debug then eprintf
"resolved types %s : %s\n%!" (show_expr_q
expr'
) (Type.to_string t
);
176 eprintfn
"resolve_types failed with %s at:" (Printexc.to_string exn
);
177 eprintfn
"%s" (show_expr_q
expr);
180 and infer_schema env columns
=
181 (* let all = tables |> List.map snd |> List.flatten in *)
182 let resolve1 = function
183 | All
-> env
.joined_schema
184 | AllOf t
-> schema_of env
.tables t
188 | Column
col -> resolve_column env
.tables env
.joined_schema
col
189 | _
-> attr "" (resolve_types env e
|> snd
)
191 let col = Option.map_default
(fun n
-> {col with name
= n
}) col name
in
194 collect resolve1 columns
196 and test_all_const columns
=
197 let rec is_const = function
198 | Fun
(_
,args
) -> List.for_all
is_const args
199 | Select _
-> false (* FIXME ? *)
204 | Expr
(e
,_
) -> is_const e
207 List.for_all
test columns
209 and get_params env e
= e
|> resolve_types env
|> fst
|> get_params_q
213 let e = Sub [Value Type.Text; Param (Next,None); Sub []; Param (Named "ds", Some Type.Int);] in
214 e |> get_params |> to_string |> print_endline
217 and params_of_columns env
=
219 | All
| AllOf
_ -> []
220 | Expr
(e,_) -> get_params env
e
224 and get_params_opt env
= function
225 | Some x
-> get_params env x
228 and get_params_l env
l = collect (get_params env
) l
230 and do_join
(env
,params
) (((_, schema1
),params1
),kind
) =
231 let joined_schema = match kind
with
234 | `Default
-> Schema.cross env
.joined_schema schema1
235 | `Natural
-> Schema.natural env
.joined_schema schema1
236 | `Using
l -> Schema.join_using
l env
.joined_schema schema1
238 let env = { env with joined_schema } in
239 let p = match kind
with
240 | `Cross
| `Default
| `Natural
| `Using
_ -> []
241 | `Search
e -> get_params
env e (* TODO should use final schema (same as tables)? *)
243 env, params
@ params1
@ p
245 and join
env ((t0
,p0
),joins
) =
246 assert (env.joined_schema = []);
247 let all_tables = List.fold_left
(fun acc
((table
,_),_) -> table
::acc
) [t0
] joins
in
248 let env = { env with tables
= env.tables
@ all_tables; joined_schema = snd t0
} in
249 List.fold_left do_join
(env, p0
) joins
251 and params_of_assigns
env ss
=
252 let exprs = resolve_column_assignments env.tables ss
in
253 get_params_l
env exprs
255 and params_of_order o final_schema tables
=
256 get_params_l
{ tables
; joined_schema=(final_schema
:: (List.map snd tables
) |> all_columns); insert_schema
= []; } o
258 and ensure_simple_expr
= function
259 | Value x
-> `Value x
260 | Param x
-> `Param x
261 | Column
_ | Inserted
_ -> failwith
"Not a simple expression"
262 | Fun
(func
,_) when Type.is_grouping func
-> failwith
"Grouping function not allowed in simple expression"
263 | Fun
(x
,l) -> `Func
(x
,List.map ensure_simple_expr
l) (* FIXME *)
264 | Select
_ -> failwith
"not implemented : ensure_simple_expr for SELECT"
266 and eval_select
env { columns
; from
; where
; group
; having
; } =
267 (* nested selects generate new fresh schema in scope, cannot refer to outer schema,
268 but can refer to attributes of tables through `tables` *)
269 let env = { env with joined_schema = [] } in
272 | Some
(t
,l) -> join
env (resolve_source
env t
, List.map
(fun (x
,k
) -> resolve_source
env x
, k
) l)
275 let singlerow = group
= [] && test_all_grouping columns
in
276 let singlerow2 = where
= None
&& group
= [] && test_all_const columns
in
277 let p1 = params_of_columns
env columns
in
278 let p3 = get_params_opt
env where
in
279 let p4 = get_params_l
env group
in
280 let p5 = get_params_opt
env having
in
281 let cardinality = if singlerow then `One
else
282 if singlerow2 then `Zero_one
else `Nat
in
283 (infer_schema
env columns
, p1 @ p2
@ p3 @ p4 @ p5, env.tables
, cardinality)
285 and resolve_source
env (x
,alias
) =
286 let src = match x
with
287 | `Select select
-> let (s
,p,_,_) = eval_select
env select
in ("",s
), p
288 | `Table s
-> Tables.get s
, []
291 | Some name
-> let ((_,s
),p) = src in ((name
,s
),p)
294 and eval_select_full
env { select
=(select
,other
); order
; limit
; } =
295 let (s1
,p1,tbls
,cardinality) = eval_select
env select
in
296 let (s2l
,p2l
) = List.split
(List.map
(fun (s
,p,_,_) -> s
,p) @@ List.map
(eval_select
env) other
) in
298 eprintf
"cardinality=%s other=%u\n%!"
299 (Stmt.cardinality_to_string
cardinality)
301 let cardinality = if other
= [] then cardinality else `Nat
in
302 (* ignoring tables in compound statements - they cannot be used in ORDER BY *)
303 let final_schema = List.fold_left
Schema.compound s1 s2l
in
304 let p3 = params_of_order order
final_schema tbls
in
305 let (p4,limit1
) = match limit
with | Some x
-> x
| None
-> [],false in
306 (* Schema.check_unique schema; *)
308 if limit1
&& cardinality = `Nat
then `Zero_one
310 final_schema,(p1@(List.flatten p2l
)@p3@p4), Stmt.Select
cardinality
313 let update_tables tables ss w
=
314 let (tables
,params
) = List.split tables
in
315 let env = { tables
; joined_schema=cross @@ List.map snd tables
; insert_schema
=get_columns_schema tables
(List.map fst ss
); } in
316 let p1 = params_of_assigns
env ss
in
317 let p2 = get_params_opt
env w
in
318 (List.flatten params
) @ p1 @ p2
320 let annotate_select select types
=
321 let (select1
,compound
) = select
.select
in
322 let rec loop acc cols types
=
323 match cols
, types
with
324 | [], [] -> List.rev acc
325 | (All
| AllOf
_) :: _, _ -> failwith
"Asterisk not supported"
326 | Expr
(e,name
) :: cols
, t
:: types
-> loop (Expr
(Fun
(F
(Typ t
, [Typ t
]), [e]), name
) :: acc
) cols types
327 | _, [] | [], _ -> failwith
"Select cardinality doesn't match Insert"
329 { select
with select
= { select1
with columns
= loop [] select1
.columns types
}, compound
}
331 let eval (stmt
:Sql.stmt
) =
334 | Create
(name
,`Schema
schema) ->
335 Tables.add
(name
,schema);
337 | Create
(name
,`Select select
) ->
338 let (schema,params
,_) = eval_select_full
empty_env select
in
339 Tables.add
(name
,schema);
340 ([],params
,Create name
)
341 | Alter
(name
,actions
) ->
343 | `Add
(col,pos
) -> Tables.alter_add name
col pos
344 | `Drop
col -> Tables.alter_drop name
col
345 | `Change
(oldcol
,col,pos
) -> Tables.alter_change name oldcol
col pos
346 | `None
-> ()) actions
;
351 | CreateIndex
(name
,table
,cols
) ->
352 Sql.Schema.project cols
(Tables.get_schema table
) |> ignore
; (* just check *)
353 [],[],CreateIndex name
354 | Insert
{ target
=table
; action
=`Values
(names
, values
); on_duplicate
; } ->
355 let expect = values_or_all table names
in
356 let env = { tables
= [Tables.get table
]; joined_schema = expect; insert_schema
= expect; } in
357 let params, inferred
= match values
with
358 | None
-> [], Some
(Values
, expect)
360 let vl = List.map
List.length values
in
361 let cl = List.length
expect in
362 if List.exists
(fun n
-> n
<> cl) vl then
363 fail
"Expecting %u expressions in every VALUES tuple" cl;
364 let assigns = List.map
(fun tuple
-> List.combine
(List.map
(fun a
-> {cname
=a
.name
; tname
=None
}) expect) tuple
) values
in
365 params_of_assigns
env (List.concat
assigns), None
367 let params2 = params_of_assigns
env (Option.default
[] on_duplicate
) in
368 [], params @ params2, Insert
(inferred
,table
)
369 | Insert
{ target
=table
; action
=`Select
(names
, select
); on_duplicate
; } ->
370 let expect = values_or_all table names
in
371 let env = { tables
= [Tables.get table
]; joined_schema = expect; insert_schema
= expect; } in
372 let select = annotate_select select (List.map
(fun a
-> a
.domain
) expect) in
373 let (schema,params,_) = eval_select_full
env select in
374 ignore
(Schema.compound
expect schema); (* test equal types once more (not really needed) *)
375 let params2 = params_of_assigns
env (Option.default
[] on_duplicate
) in
376 [], params @ params2, Insert
(None
,table
)
377 | Insert
{ target
=table
; action
=`Set ss
; on_duplicate
; } ->
378 let expect = values_or_all table
(Option.map
(List.map
(function ({cname
; tname
=None
},_) -> cname
| _ -> assert false)) ss
) in
379 let env = { tables
= [Tables.get table
]; joined_schema = expect; insert_schema
= expect; } in
380 let (params,inferred
) = match ss
with
381 | None
-> [], Some
(Assign
, Tables.get_schema table
)
382 | Some ss
-> params_of_assigns
env ss
, None
384 let params2 = params_of_assigns
env (Option.default
[] on_duplicate
) in
385 [], params @ params2, Insert
(inferred
,table
)
386 | Delete
(table
, where
) ->
387 let t = Tables.get table
in
388 let p = get_params_opt
{ tables
=[t]; joined_schema=snd
t; insert_schema
=[]; } where
in
392 | Column
_ -> [] (* this is not column but some db-specific identifier *)
393 | _ -> get_params_q (ensure_simple_expr
e)
396 | Update
(table
,ss
,w
,o
,lim
) ->
397 let params = update_tables [Tables.get table
,[]] ss w
in
398 let p3 = params_of_order o
[] [Tables.get table
] in
399 [], params @ p3 @ lim
, Update
(Some table
)
400 | UpdateMulti
(tables
,ss
,w
) ->
401 let tables = List.map
(resolve_source
empty_env) tables in
402 let params = update_tables tables ss w
in
403 [], params, Update None
404 | Select
select -> eval_select_full
empty_env select