15 let params_of select = List.map (fun x -> `Param x) (snd select)
17 let select_value select =
19 if (List.length s <> 1) then
20 raise (RA.Schema.Error (s,"only one column allowed for SELECT operator in this expression"));
23 let values_or_all table names =
24 let schema = Tables.get_schema table in
26 | Some names -> RA.Schema.project names schema
29 let update_tables tables ss w =
30 let (tables,params) = List.split tables in
31 let p1 = Syntax.params_of_assigns tables ss in
32 let p2 = get_params_opt tables (Syntax.all_tbl_columns tables) w in
33 (List.flatten params) @ p1 @ p2
39 | x, `Param (None,pos) -> Some ((Some (match x with `Limit -> "limit" | `Offset -> "offset"),pos),Int)
40 | _, `Param p -> Some (p,Int)
42 List.filter_map param l, List.mem (`Limit,`Const 1) l
47 %token <string> IDENT TEXT BLOB
49 %token <Stmt.param_id> PARAM
50 %token <Sql.Type.t * bool> FUNCTION /* return type * is grouping function? */
51 %token LPAREN RPAREN COMMA EOF DOT NULL
53 %token SELECT INSERT OR INTO CREATE UPDATE VIEW TABLE VALUES WHERE ASTERISK DISTINCT ALL ANY SOME
54 LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP LIKE
55 EXCL TILDE NOT TEST_NULL BETWEEN AND ESCAPE USING UNION EXCEPT INTERSECT AS
56 CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL CROSS REPLACE IN GROUP HAVING
57 UNIQUE PRIMARY KEY FOREIGN AUTOINCREMENT ON CONFLICT TEMPORARY IF EXISTS
58 PRECISION UNSIGNED ZEROFILL VARYING CHARSET NATIONAL ASCII UNICODE COLLATE BINARY CHARACTER
59 DATETIME_FUNC DATE TIME TIMESTAMP ALTER ADD COLUMN CASCADE RESTRICT DROP
60 GLOBAL LOCAL VALUE REFERENCES CHECK CONSTRAINT IGNORED AFTER INDEX FULLTEXT FIRST
61 CASE WHEN THEN ELSE END CHANGE MODIFY DELAYED ENUM
62 %token NUM_DIV_OP NUM_BIT_OP NUM_EQ_OP NUM_CMP_OP PLUS MINUS
63 %token T_INTEGER T_BLOB T_TEXT T_FLOAT T_BOOLEAN T_DATETIME
69 (* FIXME precedence of COMMA and JOIN *)
74 %nonassoc EQUAL NUM_EQ_OP
78 %left ASTERISK NUM_DIV_OP
82 %type <Syntax.expr> expr
84 %start <RA.Schema.t * Stmt.params * Stmt.kind> input
88 input: statement EOF { $1 }
90 if_not_exists: IF NOT EXISTS { }
91 if_exists: IF EXISTS {}
92 temporary: either(GLOBAL,LOCAL)? TEMPORARY { }
94 statement: CREATE ioption(temporary) TABLE ioption(if_not_exists) name=IDENT schema=table_definition
96 Tables.add (name,schema);
99 | ALTER TABLE name=table_name actions=commas(alter_action)
102 | `Add (col,pos) -> Tables.alter_add name col pos
103 | `Drop col -> Tables.alter_drop name col
104 | `Change (oldcol,col,pos) -> Tables.alter_change name oldcol col pos
105 | `None -> ()) actions;
108 | DROP TABLE if_exists? name=IDENT
113 | CREATE either(TABLE,VIEW) name=IDENT AS select=maybe_parenth(select_stmt)
115 let (s,p) = select in
119 | CREATE UNIQUE? INDEX if_not_exists? name=table_name
120 ON table=table_name cols=sequence(index_column)
122 RA.Schema.project cols (Tables.get_schema table) >> ignore; (* just check *)
123 [],[],CreateIndex name
125 | select_stmt_t { $1 }
126 | insert_cmd table=IDENT names=sequence(IDENT)? VALUES values=sequence(expr)?
128 let expect = values_or_all table names in
129 let params, inferred = match values with
130 | None -> [], Some (Values, expect)
132 let vl = List.length values in
133 let cl = List.length expect in
135 failwith (sprintf "Expected %u expressions in VALUES list, %u provided" cl vl);
136 let assigns = List.combine (List.map (fun a -> a.RA.name, None) expect) values in
137 Syntax.params_of_assigns [Tables.get table] assigns, None
139 [], params, Insert (inferred,table)
141 | insert_cmd table=IDENT names=sequence(IDENT)? select=maybe_parenth(select_stmt)
143 let (schema,params) = select in
144 let expect = values_or_all table names in
145 ignore (RA.Schema.compound expect schema); (* test equal types *)
146 [], params, Insert (None,table)
148 | insert_cmd table=IDENT SET ss=commas(set_column)?
150 let (params,inferred) = match ss with
151 | None -> [], Some (Assign, Tables.get_schema table)
152 | Some ss -> Syntax.params_of_assigns [Tables.get table] ss, None
154 [], params, Insert (inferred,table)
156 | update_cmd table=IDENT SET ss=commas(set_column) w=where? o=loption(order) lim=loption(limit)
158 let params = update_tables [Tables.get table,[]] ss w in
159 let p3 = Syntax.params_of_order o [] [Tables.get table] in
160 [], params @ p3 @ lim, Update (Some table)
162 /* http://dev.mysql.com/doc/refman/5.1/en/update.html multi-table syntax */
163 | update_cmd tables=commas(source) SET ss=commas(set_column) w=where?
165 let params = update_tables tables ss w in
166 [], params, Update None
168 | DELETE FROM table=IDENT w=where?
170 let t = Tables.get table in
171 let p = get_params_opt [t] (snd t) w in
174 | SET IDENT EQUAL e=expr
177 | `Column _ -> [] (* this is not column but some db-specific identifier *)
178 | _ -> get_params_q (ensure_simple_expr e)
183 table_name: name=IDENT | IDENT DOT name=IDENT { name } (* FIXME db name *)
184 index_column: name=IDENT collate? order_type? { name }
186 table_definition: t=sequence_(column_def1) table_def_done { List.filter_map (function `Attr a -> Some a | `Constraint _ -> None) t }
187 | LIKE name=maybe_parenth(IDENT) { Tables.get name >> snd } (* mysql *)
189 (* ugly, can you fixme? *)
190 (* ignoring everything after RPAREN (NB one look-ahead token) *)
191 table_def_done: table_def_done1 RPAREN IGNORED* { Parser_state.mode_normal () }
192 table_def_done1: { Parser_state.mode_ignore () }
194 select_stmt_t: select_core other=list(preceded(compound_op,select_core))
195 o=loption(order) lim=limit_t?
197 let (s1,p1,tbls,singlerow1) = $1 in
198 let (s2l,p2l) = List.split (List.map (fun (s,p,_,_) -> s,p) other) in
199 if Sqlgg_config.debug1 () then
200 eprintf "singlerow=%b other=%u\n%!" singlerow1 (List.length other);
201 let singlerow1 = singlerow1 && other = [] in
202 (* ignoring tables in compound statements - they cannot be used in ORDER BY *)
203 let final_schema = List.fold_left RA.Schema.compound s1 s2l in
204 let p3 = Syntax.params_of_order o final_schema tbls in
205 let (p4,singlerow) = match lim with | Some x -> x | None -> [],false in
206 (* RA.Schema.check_unique schema; *)
207 final_schema,(p1@(List.flatten p2l)@p3@p4), Select (singlerow || singlerow1)
210 select_stmt: select_stmt_t { let (s,p,_) = $1 in s,p }
212 select_core: SELECT select_type? r=commas(column1)
218 let (tbls,p2,joined_schema) = Syntax.join t in
219 let singlerow = g = [] && Syntax.test_all_grouping r in
220 let singlerow2 = w = None && g = [] && Syntax.test_all_const r in
221 let p1 = Syntax.params_of_columns tbls joined_schema r in
222 let p3 = Syntax.get_params_opt tbls joined_schema w in
223 let p4 = Syntax.get_params_l tbls joined_schema g in
224 let p5 = Syntax.get_params_opt tbls joined_schema h in
225 (Syntax.infer_schema r tbls joined_schema, p1 @ p2 @ p3 @ p4 @ p5, tbls, singlerow || singlerow2)
228 table_list: src=source joins=join_source* { (src,joins) }
230 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
231 | CROSS JOIN src=source { src,`Cross }
232 | qualified_join src=source cond=join_cond { src,cond }
234 qualified_join: COMMA | maybe_join_type JOIN { }
236 join_cond: ON e=expr { `Search e }
237 | USING l=sequence(IDENT) { `Using l }
240 source1: IDENT { Tables.get $1,[] }
241 | LPAREN s=select_core RPAREN { let (s,p,_,_) = s in ("",s),p }
243 source: src=source1 alias=maybe_as
246 | Some name -> let ((_,s),p) = src in ((name,s),p)
250 insert_cmd: INSERT DELAYED? OR? conflict_algo INTO | INSERT INTO | REPLACE INTO { }
251 update_cmd: UPDATE | UPDATE OR conflict_algo { }
252 conflict_algo: CONFLICT_ALGO | REPLACE { }
254 select_type: DISTINCT | ALL { }
256 int_or_param: i=INTEGER { `Const i }
257 | p=PARAM { `Param p }
259 limit_t: LIMIT lim=int_or_param { limit [`Limit,lim] }
260 | LIMIT ofs=int_or_param COMMA lim=int_or_param { limit [`Offset,ofs; `Limit,lim] }
261 | LIMIT lim=int_or_param OFFSET ofs=int_or_param { limit [`Limit,lim; `Offset,ofs] }
263 limit: limit_t { fst $1 }
265 order: ORDER BY l=commas(terminated(expr,order_type?)) { l }
266 order_type: DESC | ASC { }
268 where: WHERE e=expr { e }
269 group: GROUP BY l=expr_list { l }
270 having: HAVING e=expr { e }
273 | IDENT DOT ASTERISK { Syntax.AllOf $1 }
274 | ASTERISK { Syntax.All }
275 | e=expr m=maybe_as { Syntax.Expr (e,m) }
277 maybe_as: AS? name=IDENT { Some name }
280 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
282 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
283 | ADD index_type IDENT? sequence(IDENT) { `None }
284 | DROP INDEX IDENT { `None }
285 | DROP PRIMARY KEY { `None }
286 | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
287 | CHANGE COLUMN? old_name=IDENT column=column_def pos=alter_pos { `Change (old_name,column,pos) }
288 | MODIFY COLUMN? column=column_def pos=alter_pos { `Change (column.RA.name,column,pos) }
289 | SET IDENT IDENT { `None }
290 index_type: INDEX | FULLTEXT | PRIMARY KEY { }
291 alter_pos: AFTER col=IDENT { `After col }
294 drop_behavior: CASCADE | RESTRICT { }
296 column_def: name=IDENT t=sql_type? column_def_extra*
297 { RA.attr name (match t with Some x -> x | None -> Int) }
299 column_def1: c=column_def { `Attr c }
300 | pair(CONSTRAINT,IDENT)? c=table_constraint_1 { `Constraint c }
302 on_conflict: ON CONFLICT algo=conflict_algo { algo }
303 column_def_extra: PRIMARY KEY { Some PrimaryKey }
304 | NOT NULL { Some NotNull }
306 | UNIQUE { Some Unique }
307 | AUTOINCREMENT { Some Autoincrement }
308 | on_conflict { None }
309 | CHECK LPAREN expr RPAREN { None }
310 | DEFAULT default_value { None } (* FIXME check type with column *)
311 | COLLATE IDENT { None }
313 default_value: single_literal_value | datetime_value { } (* sub expr ? *)
315 (* FIXME check columns *)
317 | some_key IDENT? key_arg { [] }
318 | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)? { [] }
319 | CHECK LPAREN expr RPAREN { [] }
321 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
322 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
324 set_column: name=attr_name EQUAL e=expr { name,e }
326 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
328 anyall: ANY | ALL | SOME { }
330 mnot(X): NOT x = X | x = X { x }
332 attr_name: name=IDENT { (name,None) }
333 | table=IDENT DOT name=IDENT
334 | IDENT DOT table=IDENT DOT name=IDENT { (name,Some table) } (* FIXME database identifier *)
337 expr numeric_bin_op expr %prec PLUS { `Func ((Int,false),[$1;$3]) }
338 | expr boolean_bin_op expr %prec AND { `Func ((Bool,false),[$1;$3]) }
339 | e1=expr comparison_op anyall? e2=expr %prec EQUAL { `Func ((Bool,false),[e1;e2]) }
340 | expr CONCAT_OP expr { `Func ((Text,false),[$1;$3]) }
341 | e1=expr mnot(like) e2=expr e3=escape?
342 { `Func ((Any,false),(List.filter_valid [Some e1; Some e2; e3])) }
343 | unary_op expr { $2 }
344 | MINUS expr %prec UNARY_MINUS { $2 }
345 | LPAREN expr RPAREN { $2 }
346 | attr_name { `Column $1 }
347 | v=literal_value | v=datetime_value { v }
348 | e1=expr mnot(IN) l=sequence(expr) { `Func ((Any,false),e1::l) }
349 | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
351 `Func ((Any,false),e1::select_value select)
353 | e1=expr IN table=IDENT { Tables.check(table); e1 }
354 | LPAREN select=select_stmt RPAREN
356 `Func ((Any,false),select_value select)
358 | PARAM { `Param ($1,Any) }
359 | f=FUNCTION LPAREN p=func_params RPAREN { `Func (f,p) }
360 | expr TEST_NULL { $1 }
361 | expr mnot(BETWEEN) expr AND expr { `Func ((Int,false),[$1;$3;$5]) }
362 | mnot(EXISTS) LPAREN select=select_stmt RPAREN { `Func ((Bool,false),params_of select) }
363 | CASE e1=expr? branches=nonempty_list(case_branch) e2=preceded(ELSE,expr)? END
365 let l = function None -> [] | Some x -> [x] in
366 `Func ((Any,false),l e1 @ List.flatten branches @ l e2)
369 case_branch: WHEN e1=expr THEN e2=expr { [e1;e2] }
370 like: LIKE | LIKE_OP { }
372 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { `Value Datetime }
375 | TEXT { `Value Text }
376 | BLOB { `Value Blob }
377 | INTEGER { `Value Int }
378 | FLOAT { `Value Float }
381 | TIMESTAMP TEXT { `Value Datetime }
382 | NULL { `Value Any } (* he he *)
384 single_literal_value:
385 | literal_value { $1 }
386 | MINUS INTEGER { `Value Int }
387 | MINUS FLOAT { `Value Float }
389 expr_list: l=commas(expr) { l }
390 func_params: expr_list { $1 }
393 escape: ESCAPE expr { $2 }
394 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_DIV_OP | NUM_BIT_OP { }
395 comparison_op: EQUAL | NUM_CMP_OP | NUM_EQ_OP { }
396 boolean_bin_op: AND | OR { }
402 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
404 | NATIONAL? text VARYING? charset? collate? { Text }
405 | ENUM sequence(TEXT) charset? collate? { Text }
406 | T_FLOAT PRECISION? { Float }
408 | T_DATETIME | DATE | TIME | TIMESTAMP { Datetime }
410 binary: T_BLOB | BINARY | BINARY VARYING { }
411 text: T_TEXT | T_TEXT LPAREN INTEGER RPAREN | CHARACTER { }
413 %inline either(X,Y): X | Y { }
414 %inline commas(X): l=separated_nonempty_list(COMMA,X) { l }
416 %inline sequence_(X): LPAREN l=commas(X) { l }
417 %inline sequence(X): l=sequence_(X) RPAREN { l }
419 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
420 collate: COLLATE IDENT { }
422 sql_type: t=sql_type_flavor
423 | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
424 | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
427 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
429 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }