minor
[sqlgg.git] / sql_parser.mly
blob01550277198b43bc9d8609a224420ebb1f82e3f0
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)
25 %token <int> INTEGER
26 %token <string> IDENT TEXT BLOB
27 %token <Stmt.param_id> PARAM
28 %token <Sql.Type.t option> FUNCTION
29 %token LPAREN RPAREN COMMA EOF DOT NULL
30 %token CONFLICT_ALGO
31 %token SELECT INSERT OR INTO CREATE UPDATE TABLE VALUES WHERE ASTERISK DISTINCT ALL 
32        LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP
33        EXCL TILDE NOT TEST_NULL BETWEEN AND ESCAPE USING UNION EXCEPT INTERSECT AS
34        CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL REPLACE IN GROUP HAVING
35 %token UNIQUE PRIMARY KEY AUTOINCREMENT ON CONFLICT
36 %token NUM_BINARY_OP PLUS MINUS
37 %token T_INTEGER T_BLOB T_TEXT
39 %left TEST_NULL
40 %left AND OR
41 %nonassoc EQUAL
42 %nonassoc NUM_BINARY_OP
43 %left PLUS MINUS
44 %left ASTERISK
46 %type <Syntax.expr> expr
48 %start <RA.Scheme.t * Stmt.params * Stmt.kind> input
52 input: statement EOF { $1 } ;
54 statement: CREATE TABLE name=IDENT LPAREN scheme=column_defs RPAREN
55               { let () = Tables.add (name,scheme) in ([],[],Create name) }
56          | CREATE TABLE name=IDENT AS select=select_stmt
57               { 
58                 let (s,p) = select in
59                 Tables.add (name,s);
60                 ([],p,Create name)
61               }
62          | select_stmt
63               { let (s,p) = $1 in s,p,Select }
64          | insert_cmd table=IDENT cols=columns_list? VALUES
65               { 
66                 let s = Tables.get_scheme table in
67                 let s = match cols with
68                   | Some cols -> RA.Scheme.project cols s
69                   | None -> s
70                 in
71                 let p = Syntax.scheme_as_params s in
72                 [],p,Insert table
73               }
74          | update_cmd table=IDENT SET assignments=separated_nonempty_list(COMMA,set_column) w=where?
75               { 
76                 let t = Tables.get table in
77                 let p2 = get_params_opt [t] w in
78                 let (cols,exprs) = List.split assignments in
79                 let _ = RA.Scheme.project cols (snd t) in (* validates columns *)
80                 let p1 = Syntax.get_params_l [t] exprs in
81                 [], p1 @ p2, Update table
82               }
83          | DELETE FROM table=IDENT w=where?
84               { 
85                 let p = get_params_opt [Tables.get table] w in
86                 [], p, Delete table
87               }
88               
89 columns_list: LPAREN cols=separated_nonempty_list(COMMA,IDENT) RPAREN { cols }
91 select_stmt: select_core list(preceded(compound_op,select_core)) o=loption(order) p4=loption(limit)
92               { 
93                 let (s1,p1,tbls) = $1 in
94                 let (s2l,p2l) = List.split (List.map (fun (s,p,_) -> s,p) $2) in 
95                 (* ignoring tables in compound statements - they cannot be used in ORDER BY *)
96                 let p3 = Syntax.get_params_l tbls o in
97                 let scheme = List.fold_left RA.Scheme.compound s1 s2l in
98                 RA.Scheme.check_unique scheme;
99                 scheme,(p1@(List.flatten p2l)@p3@p4) 
100               }
102 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
103              FROM t=table_list
104              w=where?
105              g=loption(group)
106              h=having?
107               {
108                 let (tbls,p2) = t in
109                 let p1 = Syntax.params_of_columns tbls r in
110                 let p3 = Syntax.get_params_opt tbls w in
111                 let p4 = Syntax.get_params_l tbls g in
112                 let p5 = Syntax.get_params_opt tbls h in
113                 (Syntax.get_scheme r tbls, p1 @ p2 @ p3 @ p4 @ p5, tbls)
114               }
116 table_list: source join_source* 
117     { 
118       let (s,p) = List.split $2 in 
119       (fst $1::s, List.flatten (snd $1::p))
120     }
121 join_source: join_op s=source p=loption(join_args) 
122     { 
123       (* FIXME more tables in scope *)
124       (fst s,snd s @ Syntax.get_params_l [fst s] p)
125     }
126 source1: IDENT { Tables.get $1,[] }
127        | LPAREN s=select_core RPAREN { let (s,p,_) = s in ("",s),p }
128 source: src=source1 alias=preceded(AS,IDENT)? 
129     {
130       match alias with
131       | Some name -> let ((n,s),p) = src in ((name,s),p)
132       | None -> src
133     }
134 join_op: COMMA | NATURAL? JOIN_TYPE1? JOIN_TYPE2? JOIN { } ;
135 join_args: ON e=expr { [e] }
136          | USING LPAREN l=separated_nonempty_list(COMMA,IDENT) RPAREN { List.map (fun name -> `Column (name,None)) l }
138 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
140 update_cmd: UPDATE {}
141           | UPDATE OR CONFLICT_ALGO {} ;
143 select_type: DISTINCT | ALL { }
145 int_or_param: INTEGER { [] }
146             | PARAM { [($1,Some Sql.Type.Int)] }
148 limit: LIMIT p=int_or_param { p }
149      | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 }
150      | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
152 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
153 order_type: DESC | ASC { }
155 where: WHERE e=expr { e }
156 group: GROUP BY l=separated_nonempty_list(COMMA,expr) { l }
157 having: HAVING e=expr { e }
159 column1:
160        | IDENT DOT ASTERISK { Syntax.AllOf $1 }
161        | ASTERISK { Syntax.All }
162        | expr maybe_as { let e = $1 in Syntax.Expr (e,$2) }
164 maybe_as: AS? name=IDENT { Some name }
165         | { None }
167 column_defs: separated_nonempty_list(COMMA,column_def1) { $1 }
168 column_def1: name=IDENT t=sql_type? column_def_extra* { RA.attr name (match t with Some t -> t | None -> Type.Int) }
169 column_def_extra: PRIMARY KEY { Some Constraint.PrimaryKey }
170                 | NOT NULL { Some Constraint.NotNull }
171                 | UNIQUE { Some Constraint.Unique }
172                 | AUTOINCREMENT { Some Constraint.Autoincrement }
173                 | ON CONFLICT CONFLICT_ALGO { None }
174                 | DEFAULT INTEGER { None }
176 set_column: name=IDENT EQUAL e=expr { name,e }
178 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
180 mnot(X): NOT x = X | x = X { x }
182 expr:
183      expr numeric_bin_op expr %prec PLUS { `Func ((Some Int),[$1;$3]) }
184     | expr boolean_bin_op expr %prec AND { `Func ((Some Int),[$1;$3]) }
185     | expr CONCAT_OP expr { `Func ((Some Text),[$1;$3]) }
186     | e1=expr mnot(LIKE_OP) e2=expr e3=escape?
187       { `Func (None,(List.filter_valid [Some e1; Some e2; e3])) }
188     | unary_op expr { $2 }
189     | LPAREN expr RPAREN { $2 }
190     | IDENT { `Column ($1,None) }
191     | t=IDENT DOT c=IDENT
192     | IDENT DOT t=IDENT DOT c=IDENT { `Column (c,Some t) }
193     | INTEGER { `Value Int }
194     | e1=expr mnot(IN) LPAREN l=separated_nonempty_list(COMMA,expr) RPAREN { `Func (None,e1::l) }
195     | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN 
196       { 
197         let (s,p) = select in
198         if (List.length s <> 1) then
199           raise (RA.Scheme.Error (s,"only one column allowed for IN operator"));
200         let l = List.map (fun x -> `Param x) p in
201         `Func (None,e1::l)
202       }
203     | e1=expr IN table=IDENT { Tables.check(table); e1 }
204 (*     | FLOAT { `Value Float } *)
205     | TEXT { `Value Text }
206     | BLOB { `Value Blob }
207     | PARAM { `Param ($1,None) }
208     | FUNCTION LPAREN func_params RPAREN { `Func ($1,$3) }
209     | expr TEST_NULL { $1 }
210     | expr BETWEEN expr AND expr { `Func ((Some Int),[$1;$3;$5]) }
212 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
213 func_params: expr_list { $1 }
214            | ASTERISK { [] } ;
215 escape: ESCAPE expr { $2 }
216 numeric_bin_op: EQUAL | PLUS | MINUS | ASTERISK | NUM_BINARY_OP { } 
217 boolean_bin_op: AND | OR { }
219 unary_op: EXCL { }
220         | PLUS { }
221         | MINUS { }
222         | TILDE { }
223         | NOT { }
225 sql_type_flavor: T_INTEGER  { Type.Int }
226                | T_BLOB { Type.Blob }
227                | T_TEXT { Type.Text }
229 sql_type: t=sql_type_flavor
230         | t=sql_type_flavor LPAREN INTEGER RPAREN
231         | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
232         { t }
234 compound_op: UNION ALL? | EXCEPT | INTERSECT { }