sql: parse TRUE FALSE (mysql)
[sqlgg.git] / lib / sql_parser.mly
blobe8b930d784798f555bebcc21a20ee6c685e3be8c
1 /*
2   Simple SQL parser
3 */
6 %{
7   open Sql
8   open Sql.Type
9   open Sql.Constraint
10   open Syntax
11   open Prelude
13   (* preserve order *)
14   let make_limit l =
15     let param = function
16       | _, `Const _ -> None
17       | x, `Param (None,pos) -> Some ((Some (match x with `Limit -> "limit" | `Offset -> "offset"),pos),Int)
18       | _, `Param p -> Some (p,Int)
19     in
20     list_filter_map param l, List.mem (`Limit,`Const 1) l
24 %token <int> INTEGER
25 %token <string> IDENT TEXT BLOB
26 %token <float> FLOAT
27 %token <Sql.param_id> PARAM
28 %token <Sql.Type.t * bool> FUNCTION /* return type * is grouping function? */
29 %token LPAREN RPAREN COMMA EOF DOT NULL
30 %token CONFLICT_ALGO
31 %token SELECT INSERT OR INTO CREATE UPDATE VIEW TABLE VALUES WHERE ASTERISK DISTINCT ALL ANY SOME
32        LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP LIKE
33        EXCL TILDE NOT BETWEEN AND ESCAPE USING UNION EXCEPT INTERSECT AS
34        CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL CROSS REPLACE IN GROUP HAVING
35        UNIQUE PRIMARY KEY FOREIGN AUTOINCREMENT ON CONFLICT TEMPORARY IF EXISTS
36        PRECISION UNSIGNED ZEROFILL VARYING CHARSET NATIONAL ASCII UNICODE COLLATE BINARY CHARACTER
37        DATETIME_FUNC DATE TIME TIMESTAMP ALTER ADD COLUMN CASCADE RESTRICT DROP
38        GLOBAL LOCAL VALUE REFERENCES CHECK CONSTRAINT IGNORED AFTER INDEX FULLTEXT FIRST
39        CASE WHEN THEN ELSE END CHANGE MODIFY DELAYED ENUM FOR SHARE MODE LOCK
40        OF WITH NOWAIT ACTION NO IS INTERVAL
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
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
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 table=IDENT names=sequence(IDENT)? VALUES values=sequence(expr)?
99               {
100                 Insert (table,`Values (names, values))
101               }
102          | insert_cmd table=IDENT names=sequence(IDENT)? select=maybe_parenth(select_stmt)
103               {
104                 Insert (table,`Select (names, select))
105               }
106          | insert_cmd table=IDENT SET ss=commas(set_column)?
107               {
108                 Insert (table, `Set 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               }
128 table_name: name=IDENT | IDENT DOT name=IDENT { name } (* FIXME db name *)
129 index_prefix: LPAREN n=INTEGER RPAREN { n }
130 index_column: name=IDENT index_prefix? collate? order_type? { name }
132 table_definition: t=sequence_(column_def1) table_def_done { list_filter_map (function `Attr a -> Some a | `Constraint _ -> None) t }
133                 | LIKE name=maybe_parenth(IDENT) { Tables.get name |> snd } (* mysql *)
135 (* ugly, can you fixme? *)
136 (* ignoring everything after RPAREN (NB one look-ahead token) *)
137 table_def_done: table_def_done1 RPAREN IGNORED* { Parser_state.mode_normal () }
138 table_def_done1: { Parser_state.mode_ignore () }
140 select_stmt: select_core other=list(preceded(compound_op,select_core)) o=loption(order) lim=limit_t? select_row_locking?
141               {
142                 ($1, other, o, lim)
143               }
145 select_core: SELECT select_type? r=commas(column1) f=from?  w=where?  g=loption(group) h=having?
146               {
147                 { columns=r; from=f; where=w; group=g; having=h; }
148               }
150 table_list: src=source joins=join_source* { (src,joins) }
152 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
153            | CROSS JOIN src=source { src,`Cross }
154            | qualified_join src=source cond=join_cond { src,cond }
156 qualified_join: COMMA | maybe_join_type JOIN { }
158 join_cond: ON e=expr { `Search e }
159          | USING l=sequence(IDENT) { `Using l }
160          | (* *) { `Default }
162 source1: IDENT { `Table $1 }
163        | LPAREN s=select_core RPAREN { `Select s }
165 source: src=source1 alias=maybe_as { src, alias }
167 insert_cmd: INSERT DELAYED? OR? conflict_algo INTO | INSERT INTO | REPLACE INTO { }
168 update_cmd: UPDATE | UPDATE OR conflict_algo { }
169 conflict_algo: CONFLICT_ALGO | REPLACE { }
171 select_type: DISTINCT | ALL { }
173 select_row_locking: 
174     for_update_or_share+
175       { }
176   | LOCK IN SHARE MODE
177       { }
179 for_update_or_share:
180   FOR either(UPDATE, SHARE) update_or_share_of? NOWAIT? with_lock? { }
182 update_or_share_of: OF commas(IDENT) { }
184 with_lock: WITH LOCK { }
186 int_or_param: i=INTEGER { `Const i }
187             | p=PARAM { `Param p }
189 limit_t: LIMIT lim=int_or_param { make_limit [`Limit,lim] }
190        | LIMIT ofs=int_or_param COMMA lim=int_or_param { make_limit [`Offset,ofs; `Limit,lim] }
191        | LIMIT lim=int_or_param OFFSET ofs=int_or_param { make_limit [`Limit,lim; `Offset,ofs] }
193 limit: limit_t { fst $1 }
195 order: ORDER BY l=commas(terminated(expr,order_type?)) { l }
196 order_type: DESC | ASC { }
198 from: FROM t=table_list { t }
199 where: WHERE e=expr { e }
200 group: GROUP BY l=expr_list { l }
201 having: HAVING e=expr { e }
203 column1:
204        | IDENT DOT ASTERISK { Sql.AllOf $1 }
205        | ASTERISK { Sql.All }
206        | e=expr m=maybe_as { Sql.Expr (e,m) }
208 maybe_as: AS? name=IDENT { Some name }
209         | { None }
211 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
213 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
214             | ADD index_type IDENT? sequence(IDENT) { `None }
215             | DROP INDEX IDENT { `None }
216             | DROP PRIMARY KEY { `None }
217             | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
218             | CHANGE COLUMN? old_name=IDENT column=column_def pos=alter_pos { `Change (old_name,column,pos) }
219             | MODIFY COLUMN? column=column_def pos=alter_pos { `Change (column.name,column,pos) }
220             | SET IDENT IDENT { `None }
221 index_type: INDEX | FULLTEXT | PRIMARY KEY { }
222 alter_pos: AFTER col=IDENT { `After col }
223          | FIRST { `First }
224          | { `Default }
225 drop_behavior: CASCADE | RESTRICT { }
227 column_def: name=IDENT t=sql_type? column_def_extra*
228     { attr name (match t with Some x -> x | None -> Int) }
230 column_def1: c=column_def { `Attr c }
231            | pair(CONSTRAINT,IDENT)? c=table_constraint_1 { `Constraint c }
233 on_conflict: ON CONFLICT algo=conflict_algo { algo }
234 column_def_extra: PRIMARY KEY { Some PrimaryKey }
235                 | NOT NULL { Some NotNull }
236                 | NULL { None }
237                 | UNIQUE { Some Unique }
238                 | AUTOINCREMENT { Some Autoincrement }
239                 | on_conflict { None }
240                 | CHECK LPAREN expr RPAREN { None }
241                 | DEFAULT default_value { None } (* FIXME check type with column *)
242                 | COLLATE IDENT { None }
244 default_value: single_literal_value | datetime_value { } (* sub expr ? *)
246 (* FIXME check columns *)
247 table_constraint_1:
248       | some_key IDENT? key_arg { [] }
249       | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)?
250         reference_action_clause*
251           { [] }
252       | CHECK LPAREN expr RPAREN { [] }
254 reference_action_clause:
255   ON either(DELETE, UPDATE) reference_action { }
257 reference_action:
258   RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT { }
260 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
261 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
263 set_column: name=attr_name EQUAL e=expr { name,e }
265 (* expr: expr1 { $1 |> Syntax.expr_to_string |> prerr_endline; $1 } *)
267 anyall: ANY | ALL | SOME { }
269 mnot(X): NOT x = X | x = X { x }
271 attr_name: name=IDENT { (name,None) }
272          | table=IDENT DOT name=IDENT
273          | IDENT DOT table=IDENT DOT name=IDENT { (name,Some table) } (* FIXME database identifier *)
275 expr:
276       expr numeric_bin_op expr %prec PLUS { Fun ((Any,false),[$1;$3],`None) } (* TODO default Int *)
277     | expr boolean_bin_op expr %prec AND { Fun ((Bool,false),[$1;$3],`None) }
278     | e1=expr comparison_op anyall? e2=expr %prec EQUAL { Fun ((Bool,false),[e1;e2],`None) }
279     | expr CONCAT_OP expr { Fun ((Text,false),[$1;$3],`None) }
280     | e1=expr mnot(like) e2=expr e3=escape?
281       { Fun ((Any,false),(list_filter_map identity [Some e1; Some e2; e3]),`None) }
282     | unary_op expr { $2 }
283     | MINUS expr %prec UNARY_MINUS { $2 }
284     | INTERVAL expr interval_unit { $2 }
285     | LPAREN expr RPAREN { $2 }
286     | attr_name { Column $1 }
287     | v=literal_value | v=datetime_value { v }
288     | e1=expr mnot(IN) l=sequence(expr) { Fun ((Any,false),e1::l,`None) }
289     | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
290       {
291         Fun ((Any,false),[e1],`Single select)
292       }
293     | e1=expr IN table=IDENT { Tables.check(table); e1 }
294     | LPAREN select=select_stmt RPAREN
295       {
296         Fun ((Any,false),[],`Single select)
297       }
298     | PARAM { Param ($1,Any) }
299     | f=FUNCTION LPAREN p=func_params RPAREN { Fun (f,p,`None) }
300     | expr IS NOT? NULL { $1 }
301     | expr mnot(BETWEEN) expr AND expr { Fun ((Any,false),[$1;$3;$5],`None) } (* TODO default Int *)
302     | mnot(EXISTS) LPAREN select=select_stmt RPAREN { Fun ((Bool,false),[],`Select select) }
303     | CASE e1=expr? branches=nonempty_list(case_branch) e2=preceded(ELSE,expr)? END
304       {
305         let l = function None -> [] | Some x -> [x] in
306         Fun ((Any,false),l e1 @ List.flatten branches @ l e2, `None)
307       }
309 case_branch: WHEN e1=expr THEN e2=expr { [e1;e2] }
310 like: LIKE | LIKE_OP { }
312 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { Value Datetime }
314 literal_value:
315     | TEXT { Value Text }
316     | BLOB { Value Blob }
317     | INTEGER { Value Int }
318     | FLOAT { Value Float }
319     | TRUE
320     | FALSE { Value Bool }
321     | DATE TEXT
322     | TIME TEXT
323     | TIMESTAMP TEXT { Value Datetime }
324     | NULL { Value Any } (* he he *)
326 single_literal_value:
327     | literal_value { $1 }
328     | MINUS INTEGER { Value Int }
329     | MINUS FLOAT { Value Float }
331 expr_list: l=commas(expr) { l }
332 func_params: expr_list { $1 }
333            | ASTERISK { [] }
334            | (* *) { [] }
335 escape: ESCAPE expr { $2 }
336 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_DIV_OP | NUM_BIT_OP { }
337 comparison_op: EQUAL | NUM_CMP_OP | NUM_EQ_OP { }
338 boolean_bin_op: AND | OR { }
340 unary_op: EXCL { }
341         | TILDE { }
342         | NOT { }
344 interval_unit: MICROSECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR
345              | SECOND_MICROSECOND | MINUTE_MICROSECOND | MINUTE_SECOND
346              | HOUR_MICROSECOND | HOUR_SECOND | HOUR_MINUTE
347              | DAY_MICROSECOND | DAY_SECOND | DAY_MINUTE | DAY_HOUR
348              | YEAR_MONTH { }
350 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
351                | binary { Blob }
352                | NATIONAL? text VARYING? charset? collate? { Text }
353                | ENUM sequence(TEXT) charset? collate? { Text }
354                | T_FLOAT PRECISION? { Float }
355                | T_BOOLEAN { Bool }
356                | T_DATETIME | YEAR | DATE | TIME | TIMESTAMP { Datetime }
358 binary: T_BLOB | BINARY | BINARY VARYING { }
359 text: T_TEXT | T_TEXT LPAREN INTEGER RPAREN | CHARACTER { }
361 %inline either(X,Y): X | Y { }
362 %inline commas(X): l=separated_nonempty_list(COMMA,X) { l }
363 (* (x1,x2,...,xn) *)
364 %inline sequence_(X): LPAREN l=commas(X) { l }
365 %inline sequence(X): l=sequence_(X) RPAREN { l }
367 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
368 collate: COLLATE IDENT { }
370 sql_type: t=sql_type_flavor
371         | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
372         | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
373         { t }
375 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
377 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }