parse more CREATE TABLE
[sqlgg.git] / sql_parser.mly
blobda914a9d628b1dc3dd74e54425a69dd232477b00
1 /*
2   Simple SQL parser
3 */
6 %{
7   open Printf
8   open Sql
9   open Sql.Type
10   open ListMore
11   open Stmt
12   open Syntax
13   open Operators
15   let def_param_name name (id,t) =
16     let name =
17     match id with
18     | Next | Numbered _ -> name
19     | Named x -> x
20     in
21     (Named name,t)
23   let select_value select =
24     let (s,p) = select in
25     if (List.length s <> 1) then
26       raise (RA.Scheme.Error (s,"only one column allowed for SELECT operator in this expression"));
27     List.map (fun x -> `Param x) p
31 %token <int> INTEGER
32 %token <string> IDENT TEXT BLOB
33 %token <Stmt.param_id> PARAM
34 %token <Sql.Type.t option> FUNCTION
35 %token LPAREN RPAREN COMMA EOF DOT NULL
36 %token CONFLICT_ALGO
37 %token SELECT INSERT OR INTO CREATE UPDATE TABLE VALUES WHERE ASTERISK DISTINCT ALL
38        LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP
39        EXCL TILDE NOT TEST_NULL BETWEEN AND ESCAPE USING UNION EXCEPT INTERSECT AS
40        CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL CROSS REPLACE IN GROUP HAVING
41        UNIQUE PRIMARY KEY AUTOINCREMENT ON CONFLICT TEMPORARY IF EXISTS
42        PRECISION UNSIGNED ZEROFILL VARYING CHARSET NATIONAL ASCII UNICODE COLLATE BINARY CHARACTER
43 %token NUM_BINARY_OP PLUS MINUS
44 %token T_INTEGER T_BLOB T_TEXT T_FLOAT T_BOOLEAN T_DATETIME
47 %left COMMA_JOIN
48 %left JOIN_JOIN
50 (* FIXME precedence of COMMA and JOIN *)
52 %left TEST_NULL
53 %left AND OR
54 %nonassoc EQUAL
55 %nonassoc NUM_BINARY_OP
56 %left PLUS MINUS
57 %left ASTERISK
59 %type <Syntax.expr> expr
61 %start <RA.Scheme.t * Stmt.params * Stmt.kind> input
65 input: statement EOF { $1 }
67 if_not_exists: IF NOT EXISTS { }
69 statement: CREATE ioption(TEMPORARY) TABLE ioption(if_not_exists) name=IDENT 
70            LPAREN scheme=column_defs RPAREN
71               { let () = Tables.add (name,scheme) in ([],[],Create name) }
72          | CREATE TABLE name=IDENT AS select=select_stmt
73               {
74                 let (s,p) = select in
75                 Tables.add (name,s);
76                 ([],p,Create name)
77               }
78          | select_stmt
79               { let (s,p) = $1 in s,p,Select }
80          | insert_cmd table=IDENT cols=columns_list? VALUES
81               {
82                 let s = Tables.get_scheme table in
83                 let s = match cols with
84                   | Some cols -> RA.Scheme.project cols s
85                   | None -> s
86                 in
87                 let p = Syntax.scheme_as_params s in
88                 [],p,Insert table
89               }
90          | update_cmd table=IDENT SET assignments=separated_nonempty_list(COMMA,set_column) w=where?
91               {
92                 let t = Tables.get table in
93                 let p2 = get_params_opt [t] (snd t) w in
94                 let (cols,exprs) = List.split assignments in
95                 let _ = RA.Scheme.project cols (snd t) in (* validates columns *)
96                 let p1 = Syntax.get_params_l [t] (snd t) exprs in
97                 [], p1 @ p2, Update table
98               }
99          | DELETE FROM table=IDENT w=where?
100               {
101                 let t = Tables.get table in
102                 let p = get_params_opt [t] (snd t) w in
103                 [], p, Delete table
104               }
106 columns_list: LPAREN cols=separated_nonempty_list(COMMA,IDENT) RPAREN { cols }
108 select_stmt: select_core list(preceded(compound_op,select_core)) o=loption(order) p4=loption(limit)
109               {
110                 let (s1,p1,tbls) = $1 in
111                 let (s2l,p2l) = List.split (List.map (fun (s,p,_) -> s,p) $2) in
112                 (* ignoring tables in compound statements - they cannot be used in ORDER BY *)
113                 let scheme = List.fold_left RA.Scheme.compound s1 s2l in
114                 let p3 = Syntax.get_params_l tbls scheme o in
115 (*                 RA.Scheme.check_unique scheme; *)
116                 scheme,(p1@(List.flatten p2l)@p3@p4)
117               }
119 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
120              FROM t=table_list
121              w=where?
122              g=loption(group)
123              h=having?
124               {
125                 let (tbls,p2,joined_scheme) = Syntax.join t in
126                 let p1 = Syntax.params_of_columns tbls joined_scheme r in
127                 let p3 = Syntax.get_params_opt tbls joined_scheme w in
128                 let p4 = Syntax.get_params_l tbls joined_scheme g in
129                 let p5 = Syntax.get_params_opt tbls joined_scheme h in
130                 (Syntax.infer_scheme r tbls joined_scheme, p1 @ p2 @ p3 @ p4 @ p5, tbls)
131               }
133 table_list: src=source joins=join_source* { (src,joins) }
135 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
136            | CROSS JOIN src=source { src,`Cross }
137            | qualified_join src=source cond=join_cond { src,cond }
139 qualified_join: COMMA | maybe_join_type JOIN { }
141 join_cond: ON e=expr { `Search e }
142          | USING LPAREN l=separated_nonempty_list(COMMA,IDENT) RPAREN { `Using l }
143          | (* *) { `Default }
145 source1: IDENT { Tables.get $1,[] }
146        | LPAREN s=select_core RPAREN { let (s,p,_) = s in ("",s),p }
148 source: src=source1 alias=preceded(AS,IDENT)?
149     {
150       match alias with
151       | Some name -> let ((n,s),p) = src in ((name,s),p)
152       | None -> src
153     }
155 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
157 update_cmd: UPDATE {}
158           | UPDATE OR CONFLICT_ALGO {} ;
160 select_type: DISTINCT | ALL { }
162 int_or_param: INTEGER { [] }
163             | PARAM { [($1,Some Sql.Type.Int)] }
165 limit: LIMIT p=int_or_param { p }
166      | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 } (* Named? *)
167      | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
169 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
170 order_type: DESC | ASC { }
172 where: WHERE e=expr { e }
173 group: GROUP BY l=separated_nonempty_list(COMMA,expr) { l }
174 having: HAVING e=expr { e }
176 column1:
177        | IDENT DOT ASTERISK { Syntax.AllOf $1 }
178        | ASTERISK { Syntax.All }
179        | expr maybe_as { let e = $1 in Syntax.Expr (e,$2) }
181 maybe_as: AS? name=IDENT { Some name }
182         | { None }
184 column_defs: separated_nonempty_list(COMMA,column_def1) { $1 }
185 column_def1: name=IDENT t=sql_type? column_def_extra* { RA.attr name (match t with Some t -> t | None -> Type.Int) }
186 column_def_extra: PRIMARY KEY { Some Constraint.PrimaryKey }
187                 | NOT NULL { Some Constraint.NotNull }
188                 | UNIQUE { Some Constraint.Unique }
189                 | AUTOINCREMENT { Some Constraint.Autoincrement }
190                 | ON CONFLICT CONFLICT_ALGO { None }
191                 | DEFAULT INTEGER { None }
193 set_column: name=IDENT EQUAL e=expr { name,e }
195 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
197 mnot(X): NOT x = X | x = X { x }
199 expr:
200      expr numeric_bin_op expr %prec PLUS { `Func ((Some Int),[$1;$3]) }
201     | expr boolean_bin_op expr %prec AND { `Func ((Some Int),[$1;$3]) }
202     | expr CONCAT_OP expr { `Func ((Some Text),[$1;$3]) }
203     | e1=expr mnot(LIKE_OP) e2=expr e3=escape?
204       { `Func (None,(List.filter_valid [Some e1; Some e2; e3])) }
205     | unary_op expr { $2 }
206     | LPAREN expr RPAREN { $2 }
207     | IDENT { `Column ($1,None) }
208     | t=IDENT DOT c=IDENT
209     | IDENT DOT t=IDENT DOT c=IDENT { `Column (c,Some t) }
210     | INTEGER { `Value Int }
211     | e1=expr mnot(IN) LPAREN l=separated_nonempty_list(COMMA,expr) RPAREN { `Func (None,e1::l) }
212     | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
213       {
214         `Func (None,e1::select_value select)
215       }
216     | e1=expr IN table=IDENT { Tables.check(table); e1 }
217     | LPAREN select=select_stmt RPAREN
218       {
219         `Func (None,select_value select)
220       }
221 (*     | FLOAT { `Value Float } *)
222     | TEXT { `Value Text }
223     | BLOB { `Value Blob }
224     | PARAM { `Param ($1,None) }
225     | FUNCTION LPAREN func_params RPAREN { `Func ($1,$3) }
226     | expr TEST_NULL { $1 }
227     | expr BETWEEN expr AND expr { `Func ((Some Int),[$1;$3;$5]) }
229 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
230 func_params: expr_list { $1 }
231            | ASTERISK { [] } ;
232 escape: ESCAPE expr { $2 }
233 numeric_bin_op: EQUAL | PLUS | MINUS | ASTERISK | NUM_BINARY_OP { }
234 boolean_bin_op: AND | OR { }
236 unary_op: EXCL { }
237         | PLUS { }
238         | MINUS { }
239         | TILDE { }
240         | NOT { }
242 sql_type_flavor: T_INTEGER UNSIGNED? ZEROFILL? { Type.Int }
243                | binary { Type.Blob }
244                | NATIONAL? text VARYING? charset? collate? { Type.Text }
245                | T_FLOAT PRECISION? { Type.Float }
246                | T_BOOLEAN { Type.Bool }
247                | T_DATETIME { Type.Datetime }
249 binary: T_BLOB | BINARY | BINARY VARYING { }
250 text: T_TEXT | CHARACTER { }
252 either(X,Y): X | Y { }
254 charset: CHARSET either(IDENT,BINARY) | CHARACTER SET either(IDENT,BINARY) | ASCII | UNICODE { }
255 collate: COLLATE IDENT { }
257 sql_type: t=sql_type_flavor
258         | t=sql_type_flavor LPAREN INTEGER RPAREN
259         | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
260         { t }
262 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
264 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }