17 | x, `Param (None,pos) -> Some ((Some (match x with `Limit -> "limit" | `Offset -> "offset"),pos),Int)
18 | _, `Param p -> Some (p,Int)
20 list_filter_map param l, List.mem (`Limit,`Const 1) l
25 %token <string> IDENT TEXT BLOB
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
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
41 %token NUM_DIV_OP NUM_BIT_OP NUM_EQ_OP NUM_CMP_OP PLUS MINUS
42 %token T_INTEGER T_BLOB T_TEXT T_FLOAT T_BOOLEAN T_DATETIME
48 (* FIXME precedence of COMMA and JOIN *)
52 %nonassoc EQUAL NUM_EQ_OP
56 %left ASTERISK NUM_DIV_OP
62 %start <Sql.stmt> input
66 input: statement EOF { $1 }
68 if_not_exists: IF NOT EXISTS { }
69 if_exists: IF EXISTS {}
70 temporary: either(GLOBAL,LOCAL)? TEMPORARY { }
72 statement: CREATE ioption(temporary) TABLE ioption(if_not_exists) name=IDENT schema=table_definition
74 Create (name,`Schema schema)
76 | CREATE either(TABLE,VIEW) name=IDENT AS select=maybe_parenth(select_stmt)
78 Create (name,`Select select)
80 | ALTER TABLE name=table_name actions=commas(alter_action)
84 | DROP TABLE if_exists? name=IDENT
88 | CREATE UNIQUE? INDEX if_not_exists? name=table_name ON table=table_name cols=sequence(index_column)
90 CreateIndex (name, table, cols)
92 | select_stmt { Select $1 }
93 | insert_cmd table=IDENT names=sequence(IDENT)? VALUES values=sequence(expr)?
95 Insert (table,`Values (names, values))
97 | insert_cmd table=IDENT names=sequence(IDENT)? select=maybe_parenth(select_stmt)
99 Insert (table,`Select (names, select))
101 | insert_cmd table=IDENT SET ss=commas(set_column)?
103 Insert (table, `Set ss)
105 | update_cmd table=IDENT SET ss=commas(set_column) w=where? o=loption(order) lim=loption(limit)
107 Update (table,ss,w,o,lim)
109 /* http://dev.mysql.com/doc/refman/5.1/en/update.html multi-table syntax */
110 | update_cmd tables=commas(source) SET ss=commas(set_column) w=where?
112 UpdateMulti (tables,ss,w)
114 | DELETE FROM table=IDENT w=where?
118 | SET name=IDENT EQUAL e=expr
123 table_name: name=IDENT | IDENT DOT name=IDENT { name } (* FIXME db name *)
124 index_column: name=IDENT collate? order_type? { name }
126 table_definition: t=sequence_(column_def1) table_def_done { list_filter_map (function `Attr a -> Some a | `Constraint _ -> None) t }
127 | LIKE name=maybe_parenth(IDENT) { Tables.get name |> snd } (* mysql *)
129 (* ugly, can you fixme? *)
130 (* ignoring everything after RPAREN (NB one look-ahead token) *)
131 table_def_done: table_def_done1 RPAREN IGNORED* { Parser_state.mode_normal () }
132 table_def_done1: { Parser_state.mode_ignore () }
134 select_stmt: select_core other=list(preceded(compound_op,select_core)) o=loption(order) lim=limit_t?
139 select_core: SELECT select_type? r=commas(column1) f=from? w=where? g=loption(group) h=having? select_row_locking?
141 { columns=r; from=f; where=w; group=g; having=h; }
144 table_list: src=source joins=join_source* { (src,joins) }
146 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
147 | CROSS JOIN src=source { src,`Cross }
148 | qualified_join src=source cond=join_cond { src,cond }
150 qualified_join: COMMA | maybe_join_type JOIN { }
152 join_cond: ON e=expr { `Search e }
153 | USING l=sequence(IDENT) { `Using l }
156 source1: IDENT { `Table $1 }
157 | LPAREN s=select_core RPAREN { `Select s }
159 source: src=source1 alias=maybe_as { src, alias }
161 insert_cmd: INSERT DELAYED? OR? conflict_algo INTO | INSERT INTO | REPLACE INTO { }
162 update_cmd: UPDATE | UPDATE OR conflict_algo { }
163 conflict_algo: CONFLICT_ALGO | REPLACE { }
165 select_type: DISTINCT | ALL { }
174 FOR either(UPDATE, SHARE) update_or_share_of? NOWAIT? with_lock? { }
176 update_or_share_of: OF commas(IDENT) { }
178 with_lock: WITH LOCK { }
180 int_or_param: i=INTEGER { `Const i }
181 | p=PARAM { `Param p }
183 limit_t: LIMIT lim=int_or_param { make_limit [`Limit,lim] }
184 | LIMIT ofs=int_or_param COMMA lim=int_or_param { make_limit [`Offset,ofs; `Limit,lim] }
185 | LIMIT lim=int_or_param OFFSET ofs=int_or_param { make_limit [`Limit,lim; `Offset,ofs] }
187 limit: limit_t { fst $1 }
189 order: ORDER BY l=commas(terminated(expr,order_type?)) { l }
190 order_type: DESC | ASC { }
192 from: FROM t=table_list { t }
193 where: WHERE e=expr { e }
194 group: GROUP BY l=expr_list { l }
195 having: HAVING e=expr { e }
198 | IDENT DOT ASTERISK { Sql.AllOf $1 }
199 | ASTERISK { Sql.All }
200 | e=expr m=maybe_as { Sql.Expr (e,m) }
202 maybe_as: AS? name=IDENT { Some name }
205 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
207 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
208 | ADD index_type IDENT? sequence(IDENT) { `None }
209 | DROP INDEX IDENT { `None }
210 | DROP PRIMARY KEY { `None }
211 | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
212 | CHANGE COLUMN? old_name=IDENT column=column_def pos=alter_pos { `Change (old_name,column,pos) }
213 | MODIFY COLUMN? column=column_def pos=alter_pos { `Change (column.name,column,pos) }
214 | SET IDENT IDENT { `None }
215 index_type: INDEX | FULLTEXT | PRIMARY KEY { }
216 alter_pos: AFTER col=IDENT { `After col }
219 drop_behavior: CASCADE | RESTRICT { }
221 column_def: name=IDENT t=sql_type? column_def_extra*
222 { attr name (match t with Some x -> x | None -> Int) }
224 column_def1: c=column_def { `Attr c }
225 | pair(CONSTRAINT,IDENT)? c=table_constraint_1 { `Constraint c }
227 on_conflict: ON CONFLICT algo=conflict_algo { algo }
228 column_def_extra: PRIMARY KEY { Some PrimaryKey }
229 | NOT NULL { Some NotNull }
231 | UNIQUE { Some Unique }
232 | AUTOINCREMENT { Some Autoincrement }
233 | on_conflict { None }
234 | CHECK LPAREN expr RPAREN { None }
235 | DEFAULT default_value { None } (* FIXME check type with column *)
236 | COLLATE IDENT { None }
238 default_value: single_literal_value | datetime_value { } (* sub expr ? *)
240 (* FIXME check columns *)
242 | some_key IDENT? key_arg { [] }
243 | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)?
244 reference_action_clause*
246 | CHECK LPAREN expr RPAREN { [] }
248 reference_action_clause:
249 ON either(DELETE, UPDATE) reference_action { }
252 RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT { }
254 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
255 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
257 set_column: name=attr_name EQUAL e=expr { name,e }
259 (* expr: expr1 { $1 |> Syntax.expr_to_string |> prerr_endline; $1 } *)
261 anyall: ANY | ALL | SOME { }
263 mnot(X): NOT x = X | x = X { x }
265 attr_name: name=IDENT { (name,None) }
266 | table=IDENT DOT name=IDENT
267 | IDENT DOT table=IDENT DOT name=IDENT { (name,Some table) } (* FIXME database identifier *)
270 expr numeric_bin_op expr %prec PLUS { Fun ((Any,false),[$1;$3],`None) } (* TODO default Int *)
271 | expr boolean_bin_op expr %prec AND { Fun ((Bool,false),[$1;$3],`None) }
272 | e1=expr comparison_op anyall? e2=expr %prec EQUAL { Fun ((Bool,false),[e1;e2],`None) }
273 | expr CONCAT_OP expr { Fun ((Text,false),[$1;$3],`None) }
274 | e1=expr mnot(like) e2=expr e3=escape?
275 { Fun ((Any,false),(list_filter_map identity [Some e1; Some e2; e3]),`None) }
276 | unary_op expr { $2 }
277 | MINUS expr %prec UNARY_MINUS { $2 }
278 | LPAREN expr RPAREN { $2 }
279 | attr_name { Column $1 }
280 | v=literal_value | v=datetime_value { v }
281 | e1=expr mnot(IN) l=sequence(expr) { Fun ((Any,false),e1::l,`None) }
282 | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
284 Fun ((Any,false),[e1],`Single select)
286 | e1=expr IN table=IDENT { Tables.check(table); e1 }
287 | LPAREN select=select_stmt RPAREN
289 Fun ((Any,false),[],`Single select)
291 | PARAM { Param ($1,Any) }
292 | f=FUNCTION LPAREN p=func_params RPAREN { Fun (f,p,`None) }
293 | expr IS NOT? NULL { $1 }
294 | expr mnot(BETWEEN) expr AND expr { Fun ((Any,false),[$1;$3;$5],`None) } (* TODO default Int *)
295 | mnot(EXISTS) LPAREN select=select_stmt RPAREN { Fun ((Bool,false),[],`Select select) }
296 | CASE e1=expr? branches=nonempty_list(case_branch) e2=preceded(ELSE,expr)? END
298 let l = function None -> [] | Some x -> [x] in
299 Fun ((Any,false),l e1 @ List.flatten branches @ l e2, `None)
302 case_branch: WHEN e1=expr THEN e2=expr { [e1;e2] }
303 like: LIKE | LIKE_OP { }
305 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { Value Datetime }
308 | TEXT { Value Text }
309 | BLOB { Value Blob }
310 | INTEGER { Value Int }
311 | FLOAT { Value Float }
314 | TIMESTAMP TEXT { Value Datetime }
315 | NULL { Value Any } (* he he *)
317 single_literal_value:
318 | literal_value { $1 }
319 | MINUS INTEGER { Value Int }
320 | MINUS FLOAT { Value Float }
322 expr_list: l=commas(expr) { l }
323 func_params: expr_list { $1 }
326 escape: ESCAPE expr { $2 }
327 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_DIV_OP | NUM_BIT_OP { }
328 comparison_op: EQUAL | NUM_CMP_OP | NUM_EQ_OP { }
329 boolean_bin_op: AND | OR { }
335 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
337 | NATIONAL? text VARYING? charset? collate? { Text }
338 | ENUM sequence(TEXT) charset? collate? { Text }
339 | T_FLOAT PRECISION? { Float }
341 | T_DATETIME | DATE | TIME | TIMESTAMP { Datetime }
343 binary: T_BLOB | BINARY | BINARY VARYING { }
344 text: T_TEXT | T_TEXT LPAREN INTEGER RPAREN | CHARACTER { }
346 %inline either(X,Y): X | Y { }
347 %inline commas(X): l=separated_nonempty_list(COMMA,X) { l }
349 %inline sequence_(X): LPAREN l=commas(X) { l }
350 %inline sequence(X): l=sequence_(X) RPAREN { l }
352 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
353 collate: COLLATE IDENT { }
355 sql_type: t=sql_type_flavor
356 | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
357 | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
360 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
362 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }