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"));
26 %token <string> IDENT TEXT BLOB
28 %token <Stmt.param_id> PARAM
29 %token <Sql.Type.t option> FUNCTION
30 %token LPAREN RPAREN COMMA EOF DOT NULL
32 %token SELECT INSERT OR INTO CREATE UPDATE VIEW TABLE VALUES WHERE ASTERISK DISTINCT ALL ANY SOME
33 LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP
34 EXCL TILDE NOT TEST_NULL BETWEEN AND ESCAPE USING UNION EXCEPT INTERSECT AS
35 CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL CROSS REPLACE IN GROUP HAVING
36 UNIQUE PRIMARY KEY FOREIGN AUTOINCREMENT ON CONFLICT TEMPORARY IF EXISTS
37 PRECISION UNSIGNED ZEROFILL VARYING CHARSET NATIONAL ASCII UNICODE COLLATE BINARY CHARACTER
38 DATETIME_FUNC DATE TIME TIMESTAMP ALTER ADD COLUMN CASCADE RESTRICT DROP
39 GLOBAL LOCAL VALUE REFERENCES CHECK CONSTRAINT IGNORED AFTER INDEX FULLTEXT FIRST
40 %token NUM_BINARY_OP PLUS MINUS COMPARISON_OP
41 %token T_INTEGER T_BLOB T_TEXT T_FLOAT T_BOOLEAN T_DATETIME
47 (* FIXME precedence of COMMA and JOIN *)
52 %nonassoc NUM_BINARY_OP
56 %type <Syntax.expr> expr
58 %start <RA.Schema.t * Stmt.params * Stmt.kind> input
62 input: statement EOF { $1 }
64 if_not_exists: IF NOT EXISTS { }
65 if_exists: IF EXISTS {}
66 temporary: either(GLOBAL,LOCAL)? TEMPORARY { }
68 statement: CREATE ioption(temporary) TABLE ioption(if_not_exists) name=IDENT
69 table_def=sequence_(column_def1) table_def_done
71 let schema = List.filter_map (function `Attr a -> Some a | `Constraint _ -> None) table_def in
72 let () = Tables.add (name,schema) in
75 | ALTER TABLE name=IDENT action=alter_action
77 begin match action with
78 | `Add (col,pos) -> Tables.alter_add name col pos
79 | `Drop col -> Tables.alter_drop name col
84 | DROP TABLE if_exists? name=IDENT
89 | CREATE either(TABLE,VIEW) name=IDENT AS select=select_stmt
96 { let (s,p) = $1 in s,p,Select }
97 | insert_cmd table=IDENT cols=sequence(IDENT)? VALUES (*sequence(expr)?*)
99 let s = Tables.get_schema table in
100 let s = match cols with
101 | Some cols -> RA.Schema.project cols s
104 [], [], Insert (Some s,table)
106 | insert_cmd table=IDENT SET ss=separated_nonempty_list(COMMA,set_column)
108 let t = Tables.get table in
109 let (cols,exprs) = Syntax.split_column_assignments (snd t) ss in
110 let p1 = Syntax.get_params_l [t] (snd t) exprs in
111 (*List.iter (fun e -> print_endline (Syntax.expr_to_string e)) exprs;*)
112 [], p1, Insert (None,table)
114 | update_cmd table=IDENT SET ss=separated_nonempty_list(COMMA,set_column) w=where?
116 let t = Tables.get table in
117 let p2 = get_params_opt [t] (snd t) w in
118 let (cols,exprs) = Syntax.split_column_assignments (snd t) ss in
119 let p1 = Syntax.get_params_l [t] (snd t) exprs in
120 [], p1 @ p2, Update table
122 | DELETE FROM table=IDENT w=where?
124 let t = Tables.get table in
125 let p = get_params_opt [t] (snd t) w in
129 (* ignoring everything after RPAREN (NB one look-ahead token) *)
130 table_def_done: table_def_done1 RPAREN IGNORED* { Parser_state.mode_normal () }
131 table_def_done1: { Parser_state.mode_ignore () }
133 select_stmt: select_core list(preceded(compound_op,select_core)) o=loption(order) p4=loption(limit)
135 let (s1,p1,tbls) = $1 in
136 let (s2l,p2l) = List.split (List.map (fun (s,p,_) -> s,p) $2) in
137 (* ignoring tables in compound statements - they cannot be used in ORDER BY *)
138 let schema = List.fold_left RA.Schema.compound s1 s2l in
139 let p3 = Syntax.get_params_l tbls schema o in
140 (* RA.Schema.check_unique schema; *)
141 schema,(p1@(List.flatten p2l)@p3@p4)
144 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
150 let (tbls,p2,joined_schema) = Syntax.join t in
151 let p1 = Syntax.params_of_columns tbls joined_schema r in
152 let p3 = Syntax.get_params_opt tbls joined_schema w in
153 let p4 = Syntax.get_params_l tbls joined_schema g in
154 let p5 = Syntax.get_params_opt tbls joined_schema h in
155 (Syntax.infer_schema r tbls joined_schema, p1 @ p2 @ p3 @ p4 @ p5, tbls)
158 table_list: src=source joins=join_source* { (src,joins) }
160 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
161 | CROSS JOIN src=source { src,`Cross }
162 | qualified_join src=source cond=join_cond { src,cond }
164 qualified_join: COMMA | maybe_join_type JOIN { }
166 join_cond: ON e=expr { `Search e }
167 | USING l=sequence(IDENT) { `Using l }
170 source1: IDENT { Tables.get $1,[] }
171 | LPAREN s=select_core RPAREN { let (s,p,_) = s in ("",s),p }
173 source: src=source1 alias=maybe_as
176 | Some name -> let ((n,s),p) = src in ((name,s),p)
180 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
182 update_cmd: UPDATE {}
183 | UPDATE OR CONFLICT_ALGO {} ;
185 select_type: DISTINCT | ALL { }
187 int_or_param: INTEGER { [] }
188 | PARAM { [($1,Some Int)] }
190 limit: LIMIT p=int_or_param { p }
191 | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 } (* Named? *)
192 | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
194 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
195 order_type: DESC | ASC { }
197 where: WHERE e=expr { e }
198 group: GROUP BY l=separated_nonempty_list(COMMA,expr) { l }
199 having: HAVING e=expr { e }
202 | IDENT DOT ASTERISK { Syntax.AllOf $1 }
203 | ASTERISK { Syntax.All }
204 | e=expr m=maybe_as { Syntax.Expr (e,m) }
206 maybe_as: AS? name=IDENT { Some name }
209 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
211 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
212 | ADD index_type IDENT? sequence(IDENT) { `None }
213 | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
214 index_type: INDEX | FULLTEXT { }
215 alter_pos: AFTER col=IDENT { `After col }
218 drop_behavior: CASCADE | RESTRICT { }
220 column_def: name=IDENT t=sql_type? column_def_extra*
221 { RA.attr name (match t with Some x -> x | None -> Int) }
223 column_def1: c=column_def { `Attr c }
224 | pair(CONSTRAINT,IDENT)? c=table_constraint_1 { `Constraint c }
226 on_conflict: ON CONFLICT algo=CONFLICT_ALGO { algo }
227 column_def_extra: PRIMARY KEY { Some PrimaryKey }
228 | NOT NULL { Some NotNull }
230 | UNIQUE { Some Unique }
231 | AUTOINCREMENT { Some Autoincrement }
232 | on_conflict { None }
233 | CHECK LPAREN expr RPAREN { None }
234 | DEFAULT default_value { None } (* FIXME check type with column *)
235 | COLLATE IDENT { None }
237 default_value: literal_value | datetime_value { }
239 (* FIXME check columns *)
241 | some_key IDENT? key_arg { [] }
242 | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)? { [] }
243 | CHECK LPAREN expr RPAREN { [] }
245 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
246 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
248 set_column: name=IDENT EQUAL e=expr { name,e }
250 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
252 anyall: ANY | ALL | SOME { }
254 mnot(X): NOT x = X | x = X { x }
257 expr numeric_bin_op expr %prec PLUS { `Func ((Some Int),[$1;$3]) }
258 | expr boolean_bin_op expr %prec AND { `Func ((Some Bool),[$1;$3]) }
259 | e1=expr comparison_op anyall? e2=expr %prec EQUAL { `Func ((Some Bool),[e1;e2]) }
260 | expr CONCAT_OP expr { `Func ((Some Text),[$1;$3]) }
261 | e1=expr mnot(LIKE_OP) e2=expr e3=escape?
262 { `Func (None,(List.filter_valid [Some e1; Some e2; e3])) }
263 | unary_op expr { $2 }
264 | LPAREN expr RPAREN { $2 }
265 | IDENT { `Column ($1,None) }
266 | t=IDENT DOT c=IDENT
267 | IDENT DOT t=IDENT DOT c=IDENT { `Column (c,Some t) }
268 | v=literal_value | v=datetime_value { v }
269 | e1=expr mnot(IN) l=sequence(expr) { `Func (None,e1::l) }
270 | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
272 `Func (None,e1::select_value select)
274 | e1=expr IN table=IDENT { Tables.check(table); e1 }
275 | LPAREN select=select_stmt RPAREN
277 `Func (None,select_value select)
279 | PARAM { `Param ($1,None) }
280 | f=FUNCTION LPAREN p=func_params RPAREN { `Func (f,p) }
281 | expr TEST_NULL { $1 }
282 | expr mnot(BETWEEN) expr AND expr { `Func ((Some Int),[$1;$3;$5]) }
283 | mnot(EXISTS) LPAREN select=select_stmt RPAREN { `Func ((Some Bool),params_of select) }
285 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { `Value Datetime }
288 | TEXT { `Value Text }
289 | BLOB { `Value Blob }
290 | INTEGER { `Value Int }
291 | FLOAT { `Value Float }
294 | TIMESTAMP TEXT { `Value Datetime }
296 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
297 func_params: expr_list { $1 }
300 escape: ESCAPE expr { $2 }
301 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_BINARY_OP { }
302 comparison_op: EQUAL | COMPARISON_OP { }
303 boolean_bin_op: AND | OR { }
311 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
313 | NATIONAL? text VARYING? charset? collate? { Text }
314 | T_FLOAT PRECISION? { Float }
316 | T_DATETIME | DATE | TIME | TIMESTAMP { Datetime }
318 binary: T_BLOB | BINARY | BINARY VARYING { }
319 text: T_TEXT | CHARACTER { }
321 %inline either(X,Y): X | Y { }
323 %inline sequence_(X): LPAREN l=separated_nonempty_list(COMMA,X) { l }
324 %inline sequence(X): l=sequence_(X) RPAREN { l }
326 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
327 collate: COLLATE IDENT { }
329 sql_type: t=sql_type_flavor
330 | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
331 | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
334 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
336 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }