16 | x, `Param (None,pos) -> Some ((Some (match x with `Limit -> "limit" | `Offset -> "offset"),pos),Int)
17 | _, `Param p -> Some (p,Int)
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)
25 %token <string> IDENT TEXT BLOB
27 %token <Sql.param_id> PARAM
28 %token <Sql.Type.func> FUNCTION
29 %token LPAREN RPAREN COMMA EOF DOT NULL
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 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
53 (* FIXME precedence of COMMA and JOIN *)
57 %nonassoc EQUAL NUM_EQ_OP
61 %left ASTERISK NUM_DIV_OP
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
79 Create (name,`Schema schema)
81 | CREATE either(TABLE,VIEW) name=IDENT AS select=maybe_parenth(select_stmt)
83 Create (name,`Select select)
85 | ALTER TABLE name=table_name actions=commas(alter_action)
89 | DROP TABLE if_exists? name=IDENT
93 | CREATE UNIQUE? INDEX if_not_exists? name=table_name ON table=table_name cols=sequence(index_column)
95 CreateIndex (name, table, cols)
97 | select_stmt { Select $1 }
98 | insert_cmd target=IDENT names=sequence(IDENT)? VALUES values=sequence(expr)? ss=on_duplicate?
100 Insert { target; action=`Values (names, values); on_duplicate=ss; }
102 | insert_cmd target=IDENT names=sequence(IDENT)? select=maybe_parenth(select_stmt) ss=on_duplicate?
104 Insert { target; action=`Select (names, select); on_duplicate=ss; }
106 | insert_cmd target=IDENT SET set=commas(set_column)? ss=on_duplicate?
108 Insert { target; action=`Set set; on_duplicate=ss; }
110 | update_cmd table=IDENT SET ss=commas(set_column) w=where? o=loption(order) lim=loption(limit)
112 Update (table,ss,w,o,lim)
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?
117 UpdateMulti (tables,ss,w)
119 | DELETE FROM table=IDENT w=where?
123 | SET name=IDENT EQUAL e=expr
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 _ | `Index _ -> 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?
142 { select = ($1, other); order=o; limit=lim; }
145 select_core: SELECT select_type? r=commas(column1) f=from? w=where? g=loption(group) h=having?
147 { columns=r; from=f; where=w; group=g; having=h; }
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 }
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 { }
170 on_duplicate: ON DUPLICATE KEY UPDATE ss=commas(set_column) { ss }
172 select_type: DISTINCT | ALL { }
181 FOR either(UPDATE, SHARE) update_or_share_of? NOWAIT? with_lock? { }
183 update_or_share_of: OF commas(IDENT) { }
185 with_lock: WITH LOCK { }
187 int_or_param: i=INTEGER { `Const i }
188 | p=PARAM { `Param p }
190 limit_t: LIMIT lim=int_or_param { make_limit [`Limit,lim] }
191 | LIMIT ofs=int_or_param COMMA lim=int_or_param { make_limit [`Offset,ofs; `Limit,lim] }
192 | LIMIT lim=int_or_param OFFSET ofs=int_or_param { make_limit [`Limit,lim; `Offset,ofs] }
194 limit: limit_t { fst $1 }
196 order: ORDER BY l=commas(terminated(expr,order_type?)) { l }
197 order_type: DESC | ASC { }
199 from: FROM t=table_list { t }
200 where: WHERE e=expr { e }
201 group: GROUP BY l=expr_list { l }
202 having: HAVING e=expr { e }
205 | IDENT DOT ASTERISK { Sql.AllOf $1 }
206 | ASTERISK { Sql.All }
207 | e=expr m=maybe_as { Sql.Expr (e,m) }
209 maybe_as: AS? name=IDENT { Some name }
212 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
214 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
215 | ADD index_type IDENT? sequence(IDENT) { `None }
216 | DROP INDEX IDENT { `None }
217 | DROP PRIMARY KEY { `None }
218 | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
219 | CHANGE COLUMN? old_name=IDENT column=column_def pos=alter_pos { `Change (old_name,column,pos) }
220 | MODIFY COLUMN? column=column_def pos=alter_pos { `Change (column.name,column,pos) }
221 | SET IDENT IDENT { `None }
222 index_type: INDEX | FULLTEXT | PRIMARY KEY { }
223 alter_pos: AFTER col=IDENT { `After col }
226 drop_behavior: CASCADE | RESTRICT { }
228 column_def: name=IDENT t=sql_type? column_def_extra*
229 { attr name (match t with Some x -> x | None -> Int) }
231 column_def1: c=column_def { `Attr c }
232 | pair(CONSTRAINT,IDENT)? c=table_constraint_1 { `Constraint c }
233 | INDEX cols=sequence(index_column) { `Index cols }
235 on_conflict: ON CONFLICT algo=conflict_algo { algo }
236 column_def_extra: PRIMARY KEY { Some PrimaryKey }
237 | NOT NULL { Some NotNull }
239 | UNIQUE { Some Unique }
240 | AUTOINCREMENT { Some Autoincrement }
241 | on_conflict { None }
242 | CHECK LPAREN expr RPAREN { None }
243 | DEFAULT default_value { None } (* FIXME check type with column *)
244 | COLLATE IDENT { None }
246 default_value: single_literal_value | datetime_value { } (* sub expr ? *)
248 (* FIXME check columns *)
250 | some_key IDENT? key_arg { [] }
251 | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)?
252 reference_action_clause*
254 | CHECK LPAREN expr RPAREN { [] }
256 reference_action_clause:
257 ON either(DELETE, UPDATE) reference_action { }
260 RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT { }
262 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
263 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
265 set_column: name=attr_name EQUAL e=expr { name,e }
267 (* expr: expr1 { $1 |> Syntax.expr_to_string |> prerr_endline; $1 } *)
269 anyall: ANY | ALL | SOME { }
271 mnot(X): NOT x = X | x = X { x }
273 attr_name: cname=IDENT { { cname; tname=None} }
274 | table=IDENT DOT cname=IDENT
275 | IDENT DOT table=IDENT DOT cname=IDENT { {cname; tname=Some table} } (* FIXME database identifier *)
278 expr numeric_bin_op expr %prec PLUS { Fun ((Ret Any),[$1;$3]) } (* TODO default Int *)
279 | expr boolean_bin_op expr %prec AND { Fun ((fixed Bool [Bool;Bool]),[$1;$3]) }
280 | e1=expr comparison_op anyall? e2=expr %prec EQUAL { poly Bool [e1;e2] }
281 | expr CONCAT_OP expr { Fun ((fixed Text [Text;Text]),[$1;$3]) }
282 | e1=expr mnot(like) e2=expr e3=escape?
285 | None -> Fun ((fixed Bool [Text; Text]), [e1;e2])
286 | Some e3 -> Fun ((fixed Bool [Text; Text; Text]), [e1;e2;e3])
288 | unary_op expr { $2 }
289 | MINUS expr %prec UNARY_MINUS { $2 }
290 | INTERVAL expr interval_unit { Fun (fixed Datetime [Int], [$2]) }
291 | LPAREN expr RPAREN { $2 }
292 | attr_name { Column $1 }
293 | v=literal_value | v=datetime_value { v }
294 | e1=expr mnot(IN) l=sequence(expr) { poly Bool (e1::l) }
295 | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
297 Fun ((Poly Bool),[e1; Select (select, true)])
299 | e1=expr IN table=IDENT { Tables.check table; e1 }
300 | LPAREN select=select_stmt RPAREN { Select (select, true) }
301 | PARAM { Param ($1,Any) }
302 | f=FUNCTION LPAREN p=func_params RPAREN { Fun (f,p) }
303 | expr IS NOT? NULL { Fun (Ret Bool, [$1]) }
304 | expr mnot(BETWEEN) expr AND expr { poly Bool [$1;$3;$5] }
305 | mnot(EXISTS) LPAREN select=select_stmt RPAREN { Fun ((Ret Bool),[Select (select,false)]) } (* FIXME Poly Bool *)
306 | CASE e1=expr? branches=nonempty_list(case_branch) e2=preceded(ELSE,expr)? END (* FIXME typing *)
308 let maybe f = function None -> [] | Some x -> [f x] in
311 | None -> (List.flatten @@ List.map (fun _ -> [Typ Bool; Var 1]) branches)
312 | Some _ -> [Var 0] @ (List.flatten @@ List.map (fun _ -> [Var 0; Var 1]) branches)
314 let t_args = t_args @ maybe (fun _ -> Var 1) e2 in
315 let v_args = maybe Prelude.identity e1 @ List.flatten branches @ maybe Prelude.identity e2 in
316 Fun (F (Var 1, t_args), v_args)
319 case_branch: WHEN e1=expr THEN e2=expr { [e1;e2] }
320 like: LIKE | LIKE_OP { }
322 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { Value Datetime }
325 | TEXT { Value Text }
326 | BLOB { Value Blob }
327 | INTEGER { Value Int }
328 | FLOAT { Value Float }
330 | FALSE { Value Bool }
333 | TIMESTAMP TEXT { Value Datetime }
334 | NULL { Value Any } (* he he *)
336 single_literal_value:
337 | literal_value { $1 }
338 | MINUS INTEGER { Value Int }
339 | MINUS FLOAT { Value Float }
341 expr_list: l=commas(expr) { l }
342 func_params: expr_list { $1 }
345 escape: ESCAPE expr { $2 }
346 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_DIV_OP | NUM_BIT_OP { }
347 comparison_op: EQUAL | NUM_CMP_OP | NUM_EQ_OP { }
348 boolean_bin_op: AND | OR { }
354 interval_unit: MICROSECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR
355 | SECOND_MICROSECOND | MINUTE_MICROSECOND | MINUTE_SECOND
356 | HOUR_MICROSECOND | HOUR_SECOND | HOUR_MINUTE
357 | DAY_MICROSECOND | DAY_SECOND | DAY_MINUTE | DAY_HOUR
360 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
362 | NATIONAL? text VARYING? charset? collate? { Text }
363 | ENUM sequence(TEXT) charset? collate? { Text }
364 | T_FLOAT PRECISION? { Float }
366 | T_DATETIME | YEAR | DATE | TIME | TIMESTAMP { Datetime }
368 binary: T_BLOB | BINARY | BINARY VARYING { }
369 text: T_TEXT | T_TEXT LPAREN INTEGER RPAREN | CHARACTER { }
371 %inline either(X,Y): X | Y { }
372 %inline commas(X): l=separated_nonempty_list(COMMA,X) { l }
374 %inline sequence_(X): LPAREN l=commas(X) { l }
375 %inline sequence(X): l=sequence_(X) RPAREN { l }
377 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
378 collate: COLLATE IDENT { }
380 sql_type: t=sql_type_flavor
381 | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
382 | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
385 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
387 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }