prepare release 0.4.1
[sqlgg.git] / lib / sql_parser.mly
blobf987a7b306107da6e21fbefc2b4e09899cb4522f
1 /*
2   Simple SQL parser
3 */
6 %{
7   open Sql
8   open Sql.Type
9   open Sql.Constraint
10   open Syntax
12   (* preserve order *)
13   let make_limit l =
14     let param = function
15       | _, `Const _ -> None
16       | x, `Param (None,pos) -> Some ((Some (match x with `Limit -> "limit" | `Offset -> "offset"),pos),Int)
17       | _, `Param p -> Some (p,Int)
18     in
19     list_filter_map param l, List.mem (`Limit,`Const 1) l
21   let poly ret args = Fun (F (Typ ret, List.map (fun _ -> Var 0) args), args)
24 %token <int> INTEGER
25 %token <string> IDENT TEXT BLOB
26 %token <float> FLOAT
27 %token <Sql.param_id> PARAM
28 %token LPAREN RPAREN COMMA EOF DOT NULL
29 %token CONFLICT_ALGO
30 %token SELECT INSERT OR INTO CREATE UPDATE VIEW TABLE VALUES WHERE ASTERISK DISTINCT ALL ANY SOME
31        LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP LIKE
32        EXCL TILDE NOT BETWEEN AND ESCAPE USING UNION EXCEPT INTERSECT AS
33        CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL CROSS REPLACE IN GROUP HAVING
34        UNIQUE PRIMARY KEY FOREIGN AUTOINCREMENT ON CONFLICT TEMPORARY IF EXISTS
35        PRECISION UNSIGNED ZEROFILL VARYING CHARSET NATIONAL ASCII UNICODE COLLATE BINARY CHARACTER
36        DATETIME_FUNC DATE TIME TIMESTAMP ALTER ADD COLUMN CASCADE RESTRICT DROP
37        GLOBAL LOCAL VALUE REFERENCES CHECK CONSTRAINT IGNORED AFTER INDEX FULLTEXT FIRST
38        CASE WHEN THEN ELSE END CHANGE MODIFY DELAYED ENUM FOR SHARE MODE LOCK
39        OF WITH NOWAIT ACTION NO IS INTERVAL
40 %token FUNCTION PROCEDURE LANGUAGE RETURNS OUT INOUT BEGIN COMMENT
41 %token MICROSECOND SECOND MINUTE HOUR DAY WEEK MONTH QUARTER YEAR
42        SECOND_MICROSECOND MINUTE_MICROSECOND MINUTE_SECOND
43        HOUR_MICROSECOND HOUR_SECOND HOUR_MINUTE
44        DAY_MICROSECOND DAY_SECOND DAY_MINUTE DAY_HOUR
45        YEAR_MONTH FALSE TRUE DUPLICATE
46 %token NUM_DIV_OP NUM_BIT_OP NUM_EQ_OP NUM_CMP_OP PLUS MINUS
47 %token T_INTEGER T_BLOB T_TEXT T_FLOAT T_BOOLEAN T_DATETIME T_UUID
50 %left COMMA_JOIN
51 %left JOIN_JOIN
53 (* FIXME precedence of COMMA and JOIN *)
55 %left OR
56 %left AND
57 %nonassoc EQUAL NUM_EQ_OP
58 %nonassoc NUM_CMP_OP
59 %nonassoc NUM_BIT_OP
60 %left PLUS MINUS
61 %left ASTERISK NUM_DIV_OP
62 %left CONCAT_OP
63 %nonassoc UNARY_MINUS
65 %type <Sql.expr> expr
67 %start <Sql.stmt> input
71 input: statement EOF { $1 }
73 if_not_exists: IF NOT EXISTS { }
74 if_exists: IF EXISTS {}
75 temporary: either(GLOBAL,LOCAL)? TEMPORARY { }
77 statement: CREATE ioption(temporary) TABLE ioption(if_not_exists) name=IDENT schema=table_definition
78               {
79                 Create (name,`Schema schema)
80               }
81          | CREATE either(TABLE,VIEW) name=IDENT AS select=maybe_parenth(select_stmt)
82               {
83                 Create (name,`Select select)
84               }
85          | ALTER TABLE name=table_name actions=commas(alter_action)
86               {
87                 Alter (name,actions)
88               }
89          | DROP TABLE if_exists? name=IDENT
90               {
91                 Drop name
92               }
93          | CREATE UNIQUE? INDEX if_not_exists? name=table_name ON table=table_name cols=sequence(index_column)
94               {
95                 CreateIndex (name, table, cols)
96               }
97          | select_stmt { Select $1 }
98          | insert_cmd target=IDENT names=sequence(IDENT)? VALUES values=sequence(expr)? ss=on_duplicate?
99               {
100                 Insert { target; action=`Values (names, values); on_duplicate=ss; }
101               }
102          | insert_cmd target=IDENT names=sequence(IDENT)? select=maybe_parenth(select_stmt) ss=on_duplicate?
103               {
104                 Insert { target; action=`Select (names, select); on_duplicate=ss; }
105               }
106          | insert_cmd target=IDENT SET set=commas(set_column)? ss=on_duplicate?
107               {
108                 Insert { target; action=`Set set; on_duplicate=ss; }
109               }
110          | update_cmd table=IDENT SET ss=commas(set_column) w=where? o=loption(order) lim=loption(limit)
111               {
112                 Update (table,ss,w,o,lim)
113               }
114          /* http://dev.mysql.com/doc/refman/5.1/en/update.html multi-table syntax */
115          | update_cmd tables=commas(source) SET ss=commas(set_column) w=where?
116               {
117                 UpdateMulti (tables,ss,w)
118               }
119          | DELETE FROM table=IDENT w=where?
120               {
121                 Delete (table,w)
122               }
123          | SET name=IDENT EQUAL e=expr
124               {
125                 Set (name, e)
126               }
127          | CREATE or_replace? FUNCTION name=IDENT params=sequence(func_parameter)
128            RETURNS ret=sql_type
129            routine_extra?
130            AS? routine_body
131            routine_extra?
132               {
133                 add_function name (Ret ret);
134                 CreateRoutine (name, Some ret, params)
135               }
136          | CREATE or_replace? PROCEDURE name=IDENT params=sequence(proc_parameter)
137            routine_extra?
138            AS? routine_body
139            routine_extra?
140               {
141                 add_function name (Ret Any); (* FIXME void *)
142                 CreateRoutine (name, None, params)
143               }
145 parameter_default_: DEFAULT | EQUAL { }
146 parameter_default: parameter_default_ e=expr { e }
147 func_parameter: n=IDENT AS? t=sql_type e=parameter_default? { (n,t,e) }
148 parameter_mode: IN | OUT | INOUT { }
149 proc_parameter: parameter_mode? p=func_parameter { p }
151 or_replace: OR REPLACE { }
153 routine_body: TEXT | compound_stmt { }
154 compound_stmt: BEGIN statement+ END { } (* mysql *)
156 routine_extra: LANGUAGE IDENT { }
157              | COMMENT TEXT { }
159 table_name: name=IDENT | IDENT DOT name=IDENT { name } (* FIXME db name *)
160 index_prefix: LPAREN n=INTEGER RPAREN { n }
161 index_column: name=IDENT index_prefix? collate? order_type? { name }
163 table_definition: t=sequence_(column_def1) table_def_done { list_filter_map (function `Attr a -> Some a | `Constraint _ | `Index _ -> None) t }
164                 | LIKE name=maybe_parenth(IDENT) { Tables.get name |> snd } (* mysql *)
166 (* ugly, can you fixme? *)
167 (* ignoring everything after RPAREN (NB one look-ahead token) *)
168 table_def_done: table_def_done1 RPAREN IGNORED* { Parser_state.mode_normal () }
169 table_def_done1: { Parser_state.mode_ignore () }
171 select_stmt: select_core other=list(preceded(compound_op,select_core)) o=loption(order) lim=limit_t? select_row_locking?
172               {
173                 { select = ($1, other); order=o; limit=lim; }
174               }
176 select_core: SELECT select_type? r=commas(column1) f=from?  w=where?  g=loption(group) h=having?
177               {
178                 { columns=r; from=f; where=w; group=g; having=h; }
179               }
181 table_list: src=source joins=join_source* { (src,joins) }
183 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
184            | CROSS JOIN src=source { src,`Cross }
185            | qualified_join src=source cond=join_cond { src,cond }
187 qualified_join: COMMA | maybe_join_type JOIN { }
189 join_cond: ON e=expr { `Search e }
190          | USING l=sequence(IDENT) { `Using l }
191          | (* *) { `Default }
193 source1: IDENT { `Table $1 }
194        | LPAREN s=select_core RPAREN { `Select s }
196 source: src=source1 alias=maybe_as { src, alias }
198 insert_cmd: INSERT DELAYED? OR? conflict_algo INTO | INSERT INTO | REPLACE INTO { }
199 update_cmd: UPDATE | UPDATE OR conflict_algo { }
200 conflict_algo: CONFLICT_ALGO | REPLACE { }
201 on_duplicate: ON DUPLICATE KEY UPDATE ss=commas(set_column) { ss }
203 select_type: DISTINCT | ALL { }
205 select_row_locking:
206     for_update_or_share+
207       { }
208   | LOCK IN SHARE MODE
209       { }
211 for_update_or_share:
212   FOR either(UPDATE, SHARE) update_or_share_of? NOWAIT? with_lock? { }
214 update_or_share_of: OF commas(IDENT) { }
216 with_lock: WITH LOCK { }
218 int_or_param: i=INTEGER { `Const i }
219             | p=PARAM { `Param p }
221 limit_t: LIMIT lim=int_or_param { make_limit [`Limit,lim] }
222        | LIMIT ofs=int_or_param COMMA lim=int_or_param { make_limit [`Offset,ofs; `Limit,lim] }
223        | LIMIT lim=int_or_param OFFSET ofs=int_or_param { make_limit [`Limit,lim; `Offset,ofs] }
225 limit: limit_t { fst $1 }
227 order: ORDER BY l=commas(terminated(expr,order_type?)) { l }
228 order_type: DESC | ASC { }
230 from: FROM t=table_list { t }
231 where: WHERE e=expr { e }
232 group: GROUP BY l=expr_list { l }
233 having: HAVING e=expr { e }
235 column1:
236        | IDENT DOT ASTERISK { Sql.AllOf $1 }
237        | ASTERISK { Sql.All }
238        | e=expr m=maybe_as { Sql.Expr (e,m) }
240 maybe_as: AS? name=IDENT { Some name }
241         | { None }
243 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
245 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
246             | ADD index_type IDENT? sequence(IDENT) { `None }
247             | DROP INDEX IDENT { `None }
248             | DROP PRIMARY KEY { `None }
249             | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
250             | CHANGE COLUMN? old_name=IDENT column=column_def pos=alter_pos { `Change (old_name,column,pos) }
251             | MODIFY COLUMN? column=column_def pos=alter_pos { `Change (column.name,column,pos) }
252             | SET IDENT IDENT { `None }
253 index_type: INDEX | FULLTEXT | PRIMARY KEY { }
254 alter_pos: AFTER col=IDENT { `After col }
255          | FIRST { `First }
256          | { `Default }
257 drop_behavior: CASCADE | RESTRICT { }
259 column_def: name=IDENT t=sql_type? column_def_extra*
260     { attr name (match t with Some x -> x | None -> Int) }
262 column_def1: c=column_def { `Attr c }
263            | pair(CONSTRAINT,IDENT)? c=table_constraint_1 { `Constraint c }
264            | INDEX cols=sequence(index_column) { `Index cols }
266 on_conflict: ON CONFLICT algo=conflict_algo { algo }
267 column_def_extra: PRIMARY KEY { Some PrimaryKey }
268                 | NOT NULL { Some NotNull }
269                 | NULL { None }
270                 | UNIQUE { Some Unique }
271                 | AUTOINCREMENT { Some Autoincrement }
272                 | on_conflict { None }
273                 | CHECK LPAREN expr RPAREN { None }
274                 | DEFAULT default_value { None } (* FIXME check type with column *)
275                 | COLLATE IDENT { None }
277 default_value: single_literal_value | datetime_value { } (* sub expr ? *)
279 (* FIXME check columns *)
280 table_constraint_1:
281       | some_key IDENT? key_arg { [] }
282       | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)?
283         reference_action_clause*
284           { [] }
285       | CHECK LPAREN expr RPAREN { [] }
287 reference_action_clause:
288   ON either(DELETE, UPDATE) reference_action { }
290 reference_action:
291   RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT { }
293 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
294 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
296 set_column: name=attr_name EQUAL e=expr { name,e }
298 (* expr: expr1 { $1 |> Syntax.expr_to_string |> prerr_endline; $1 } *)
300 anyall: ANY | ALL | SOME { }
302 mnot(X): NOT x = X | x = X { x }
304 attr_name: cname=IDENT { { cname; tname=None} }
305          | table=IDENT DOT cname=IDENT
306          | IDENT DOT table=IDENT DOT cname=IDENT { {cname; tname=Some table} } (* FIXME database identifier *)
308 expr:
309       expr numeric_bin_op expr %prec PLUS { Fun ((Ret Any),[$1;$3]) } (* TODO default Int *)
310     | expr boolean_bin_op expr %prec AND { Fun ((fixed Bool [Bool;Bool]),[$1;$3]) }
311     | e1=expr comparison_op anyall? e2=expr %prec EQUAL { poly Bool [e1;e2] }
312     | expr CONCAT_OP expr { Fun ((fixed Text [Text;Text]),[$1;$3]) }
313     | e1=expr mnot(like) e2=expr e3=escape?
314       {
315         match e3 with
316         | None -> Fun ((fixed Bool [Text; Text]), [e1;e2])
317         | Some e3 -> Fun ((fixed Bool [Text; Text; Text]), [e1;e2;e3])
318       }
319     | unary_op expr { $2 }
320     | MINUS expr %prec UNARY_MINUS { $2 }
321     | INTERVAL expr interval_unit { Fun (fixed Datetime [Int], [$2]) }
322     | LPAREN expr RPAREN { $2 }
323     | attr_name { Column $1 }
324     | VALUES LPAREN n=IDENT RPAREN { Inserted n }
325     | v=literal_value | v=datetime_value { v }
326     | e1=expr mnot(IN) l=sequence(expr) { poly Bool (e1::l) }
327     | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
328       {
329         Fun ((Poly Bool),[e1; Select (select, true)])
330       }
331     | e1=expr IN table=IDENT { Tables.check table; e1 }
332     | LPAREN select=select_stmt RPAREN { Select (select, true) }
333     | PARAM { Param ($1,Any) }
334     | f=IDENT LPAREN p=func_params RPAREN { Fun (get_function f, p) }
335     | expr IS NOT? NULL { Fun (Ret Bool, [$1]) }
336     | expr mnot(BETWEEN) expr AND expr { poly Bool [$1;$3;$5] }
337     | mnot(EXISTS) LPAREN select=select_stmt RPAREN { Fun ((Ret Bool),[Select (select,false)]) } (* FIXME Poly Bool *)
338     | CASE e1=expr? branches=nonempty_list(case_branch) e2=preceded(ELSE,expr)? END (* FIXME typing *)
339       {
340         let maybe f = function None -> [] | Some x -> [f x] in
341         let t_args =
342           match e1 with
343           | None -> (List.flatten @@ List.map (fun _ -> [Typ Bool; Var 1]) branches)
344           | Some _ -> [Var 0] @ (List.flatten @@ List.map (fun _ -> [Var 0; Var 1]) branches)
345         in
346         let t_args = t_args @ maybe (fun _ -> Var 1) e2 in
347         let v_args = maybe Prelude.identity e1 @ List.flatten branches @ maybe Prelude.identity e2 in
348         Fun (F (Var 1, t_args), v_args)
349       }
350     | IF LPAREN e1=expr COMMA e2=expr COMMA e3=expr RPAREN { Fun (F (Var 0, [Typ Bool;Var 0;Var 0]), [e1;e2;e3]) }
352 case_branch: WHEN e1=expr THEN e2=expr { [e1;e2] }
353 like: LIKE | LIKE_OP { }
355 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { Value Datetime }
357 literal_value:
358     | TEXT { Value Text }
359     | BLOB { Value Blob }
360     | INTEGER { Value Int }
361     | FLOAT { Value Float }
362     | TRUE
363     | FALSE { Value Bool }
364     | DATE TEXT
365     | TIME TEXT
366     | TIMESTAMP TEXT { Value Datetime }
367     | NULL { Value Any } (* he he *)
369 single_literal_value:
370     | literal_value { $1 }
371     | MINUS INTEGER { Value Int }
372     | MINUS FLOAT { Value Float }
374 expr_list: l=commas(expr) { l }
375 func_params: expr_list { $1 }
376            | ASTERISK { [] }
377            | (* *) { [] }
378 escape: ESCAPE expr { $2 }
379 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_DIV_OP | NUM_BIT_OP { }
380 comparison_op: EQUAL | NUM_CMP_OP | NUM_EQ_OP { }
381 boolean_bin_op: AND | OR { }
383 unary_op: EXCL { }
384         | TILDE { }
385         | NOT { }
387 interval_unit: MICROSECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR
388              | SECOND_MICROSECOND | MINUTE_MICROSECOND | MINUTE_SECOND
389              | HOUR_MICROSECOND | HOUR_SECOND | HOUR_MINUTE
390              | DAY_MICROSECOND | DAY_SECOND | DAY_MINUTE | DAY_HOUR
391              | YEAR_MONTH { }
393 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
394                | binary { Blob }
395                | NATIONAL? text VARYING? charset? collate? { Text }
396                | ENUM sequence(TEXT) charset? collate? { Text }
397                | T_FLOAT PRECISION? { Float }
398                | T_BOOLEAN { Bool }
399                | T_DATETIME | YEAR | DATE | TIME | TIMESTAMP { Datetime }
400                | T_UUID { Blob }
402 binary: T_BLOB | BINARY | BINARY VARYING { }
403 text: T_TEXT | T_TEXT LPAREN INTEGER RPAREN | CHARACTER { }
405 %inline either(X,Y): X | Y { }
406 %inline commas(X): l=separated_nonempty_list(COMMA,X) { l }
407 (* (x1,x2,...,xn) *)
408 %inline sequence_(X): LPAREN l=commas(X) { l }
409 %inline sequence(X): l=sequence_(X) RPAREN { l }
411 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
412 collate: COLLATE IDENT { }
414 sql_type: t=sql_type_flavor
415         | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
416         | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
417         { t }
419 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
421 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }