month+1
[sqlgg.git] / sql_parser.mly
blob7b15731c29ceee9f59d0fdc0373829a127a5a68f
1 /*
2   Simple SQL parser
3 */
6 %{
7   open Printf
8   open Sql.Constraint
9   open Sql.Type
10   open ListMore
11   open Stmt
12   open Syntax
13   open Operators
15   let params_of select = List.map (fun x -> `Param x) (snd select)
17   let select_value select =
18     let (s,p) = select in
19     if (List.length s <> 1) then
20       raise (RA.Schema.Error (s,"only one column allowed for SELECT operator in this expression"));
21     params_of select
25 %token <int> INTEGER
26 %token <string> IDENT TEXT BLOB
27 %token <float> FLOAT
28 %token <Stmt.param_id> PARAM
29 %token <Sql.Type.t option> FUNCTION
30 %token LPAREN RPAREN COMMA EOF DOT NULL
31 %token CONFLICT_ALGO
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
44 %left COMMA_JOIN
45 %left JOIN_JOIN
47 (* FIXME precedence of COMMA and JOIN *)
49 %left TEST_NULL
50 %left AND OR
51 %nonassoc EQUAL
52 %nonassoc NUM_BINARY_OP
53 %left PLUS MINUS
54 %left ASTERISK
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
70               {
71                 let schema = List.filter_map (function `Attr a -> Some a | `Constraint _ -> None) table_def in
72                 let () = Tables.add (name,schema) in
73                 ([],[],Create name)
74               }
75          | ALTER TABLE name=IDENT action=alter_action
76               {
77                 begin match action with
78                 | `Add (col,pos) -> Tables.alter_add name col pos
79                 | `Drop col -> Tables.alter_drop name col
80                 | `None -> ()
81                 end;
82                 ([],[],Alter name)
83               }
84          | DROP TABLE if_exists? name=IDENT
85               {
86                 Tables.drop name;
87                 ([],[],Drop name)
88               }
89          | CREATE either(TABLE,VIEW) name=IDENT AS select=select_stmt
90               {
91                 let (s,p) = select in
92                 Tables.add (name,s);
93                 ([],p,Create name)
94               }
95          | select_stmt
96               { let (s,p) = $1 in s,p,Select }
97          | insert_cmd table=IDENT cols=sequence(IDENT)? VALUES (*sequence(expr)?*)
98               {
99                 let s = Tables.get_schema table in
100                 let s = match cols with
101                   | Some cols -> RA.Schema.project cols s
102                   | None -> s
103                 in
104                 [], [], Insert (Some s,table)
105               }
106          | insert_cmd table=IDENT SET ss=separated_nonempty_list(COMMA,set_column)
107               {
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)
113               }
114          | update_cmd table=IDENT SET ss=separated_nonempty_list(COMMA,set_column) w=where?
115               {
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
121               }
122          | DELETE FROM table=IDENT w=where?
123               {
124                 let t = Tables.get table in
125                 let p = get_params_opt [t] (snd t) w in
126                 [], p, Delete table
127               }
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 other=list(preceded(compound_op,select_core)) o=loption(order) p4=loption(limit)
134               {
135                 let (s1,p1,tbls) = $1 in
136                 let (s2l,p2l) = List.split (List.map (fun (s,p,_) -> s,p) other) 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 all_columns = List.fold_left RA.Schema.cross [] (List.map snd tbls) in
140                 let p3 = Syntax.get_params_l tbls all_columns o in
141 (*                 RA.Schema.check_unique schema; *)
142                 schema,(p1@(List.flatten p2l)@p3@p4)
143               }
145 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
146              FROM t=table_list
147              w=where?
148              g=loption(group)
149              h=having?
150               {
151                 let (tbls,p2,joined_schema) = Syntax.join t in
152                 let p1 = Syntax.params_of_columns tbls joined_schema r in
153                 let p3 = Syntax.get_params_opt tbls joined_schema w in
154                 let p4 = Syntax.get_params_l tbls joined_schema g in
155                 let p5 = Syntax.get_params_opt tbls joined_schema h in
156                 (Syntax.infer_schema r tbls joined_schema, p1 @ p2 @ p3 @ p4 @ p5, tbls)
157               }
159 table_list: src=source joins=join_source* { (src,joins) }
161 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
162            | CROSS JOIN src=source { src,`Cross }
163            | qualified_join src=source cond=join_cond { src,cond }
165 qualified_join: COMMA | maybe_join_type JOIN { }
167 join_cond: ON e=expr { `Search e }
168          | USING l=sequence(IDENT) { `Using l }
169          | (* *) { `Default }
171 source1: IDENT { Tables.get $1,[] }
172        | LPAREN s=select_core RPAREN { let (s,p,_) = s in ("",s),p }
174 source: src=source1 alias=maybe_as
175     {
176       match alias with
177       | Some name -> let ((n,s),p) = src in ((name,s),p)
178       | None -> src
179     }
181 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
183 update_cmd: UPDATE {}
184           | UPDATE OR CONFLICT_ALGO {} ;
186 select_type: DISTINCT | ALL { }
188 int_or_param: INTEGER { [] }
189             | PARAM { [($1,Some Int)] }
191 limit: LIMIT p=int_or_param { p }
192      | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 } (* Named? *)
193      | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
195 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
196 order_type: DESC | ASC { }
198 where: WHERE e=expr { e }
199 group: GROUP BY l=separated_nonempty_list(COMMA,expr) { l }
200 having: HAVING e=expr { e }
202 column1:
203        | IDENT DOT ASTERISK { Syntax.AllOf $1 }
204        | ASTERISK { Syntax.All }
205        | e=expr m=maybe_as { Syntax.Expr (e,m) }
207 maybe_as: AS? name=IDENT { Some name }
208         | { None }
210 maybe_parenth(X): x=X | LPAREN x=X RPAREN { x }
212 alter_action: ADD COLUMN? col=maybe_parenth(column_def) pos=alter_pos { `Add (col,pos) }
213             | ADD index_type IDENT? sequence(IDENT) { `None }
214             | DROP COLUMN? col=IDENT drop_behavior? { `Drop col } (* FIXME behavior? *)
215 index_type: INDEX | FULLTEXT { }
216 alter_pos: AFTER col=IDENT { `After col }
217          | FIRST { `First }
218          | { `Last }
219 drop_behavior: CASCADE | RESTRICT { }
221 column_def: name=IDENT t=sql_type? column_def_extra*
222     { RA.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 }
230                 | NULL { None }
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: literal_value | datetime_value { }
240 (* FIXME check columns *)
241 table_constraint_1:
242       | some_key IDENT? key_arg { [] }
243       | FOREIGN KEY IDENT? sequence(IDENT) REFERENCES IDENT sequence(IDENT)? { [] }
244       | CHECK LPAREN expr RPAREN { [] }
246 some_key: UNIQUE KEY? | PRIMARY? KEY | FULLTEXT KEY { }
247 key_arg: LPAREN VALUE RPAREN | sequence(IDENT) { }
249 set_column: name=IDENT EQUAL e=expr { name,e }
251 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
253 anyall: ANY | ALL | SOME { }
255 mnot(X): NOT x = X | x = X { x }
257 expr:
258      expr numeric_bin_op expr %prec PLUS { `Func ((Some Int),[$1;$3]) }
259     | expr boolean_bin_op expr %prec AND { `Func ((Some Bool),[$1;$3]) }
260     | e1=expr comparison_op anyall? e2=expr %prec EQUAL { `Func ((Some Bool),[e1;e2]) }
261     | expr CONCAT_OP expr { `Func ((Some Text),[$1;$3]) }
262     | e1=expr mnot(LIKE_OP) e2=expr e3=escape?
263       { `Func (None,(List.filter_valid [Some e1; Some e2; e3])) }
264     | unary_op expr { $2 }
265     | LPAREN expr RPAREN { $2 }
266     | IDENT { `Column ($1,None) }
267     | t=IDENT DOT c=IDENT
268     | IDENT DOT t=IDENT DOT c=IDENT { `Column (c,Some t) }
269     | v=literal_value | v=datetime_value { v }
270     | e1=expr mnot(IN) l=sequence(expr) { `Func (None,e1::l) }
271     | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
272       {
273         `Func (None,e1::select_value select)
274       }
275     | e1=expr IN table=IDENT { Tables.check(table); e1 }
276     | LPAREN select=select_stmt RPAREN
277       {
278         `Func (None,select_value select)
279       }
280     | PARAM { `Param ($1,None) }
281     | f=FUNCTION LPAREN p=func_params RPAREN { `Func (f,p) }
282     | expr TEST_NULL { $1 }
283     | expr mnot(BETWEEN) expr AND expr { `Func ((Some Int),[$1;$3;$5]) }
284     | mnot(EXISTS) LPAREN select=select_stmt RPAREN { `Func ((Some Bool),params_of select) }
286 datetime_value: | DATETIME_FUNC | DATETIME_FUNC LPAREN INTEGER? RPAREN { `Value Datetime }
288 literal_value:
289     | TEXT { `Value Text }
290     | BLOB { `Value Blob }
291     | INTEGER { `Value Int }
292     | FLOAT { `Value Float }
293     | DATE TEXT
294     | TIME TEXT
295     | TIMESTAMP TEXT { `Value Datetime }
297 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
298 func_params: expr_list { $1 }
299            | ASTERISK { [] }
300            | (* *) { [] }
301 escape: ESCAPE expr { $2 }
302 numeric_bin_op: PLUS | MINUS | ASTERISK | NUM_BINARY_OP { }
303 comparison_op: EQUAL | COMPARISON_OP { }
304 boolean_bin_op: AND | OR { }
306 unary_op: EXCL { }
307         | PLUS { }
308         | MINUS { }
309         | TILDE { }
310         | NOT { }
312 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Int }
313                | binary { Blob }
314                | NATIONAL? text VARYING? charset? collate? { Text }
315                | T_FLOAT PRECISION? { Float }
316                | T_BOOLEAN { Bool }
317                | T_DATETIME | DATE | TIME | TIMESTAMP { Datetime }
319 binary: T_BLOB | BINARY | BINARY VARYING { }
320 text: T_TEXT | CHARACTER { }
322 %inline either(X,Y): X | Y { }
323 (* (x1,x2,...,xn) *)
324 %inline sequence_(X): LPAREN l=separated_nonempty_list(COMMA,X) { l }
325 %inline sequence(X): l=sequence_(X) RPAREN { l }
327 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
328 collate: COLLATE IDENT { }
330 sql_type: t=sql_type_flavor
331         | t=sql_type_flavor LPAREN INTEGER RPAREN UNSIGNED?
332         | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
333         { t }
335 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
337 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }