15 let def_param_name name (id,t) =
18 | Next | Numbered _ -> name
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
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 CROSS 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 T_FLOAT T_BOOLEAN
43 (* FIXME precedence of COMMA and JOIN *)
48 %nonassoc NUM_BINARY_OP
52 %type <Syntax.expr> expr
54 %start <RA.Scheme.t * Stmt.params * Stmt.kind> input
58 input: statement EOF { $1 } ;
60 statement: CREATE TABLE name=IDENT LPAREN scheme=column_defs RPAREN
61 { let () = Tables.add (name,scheme) in ([],[],Create name) }
62 | CREATE TABLE name=IDENT AS select=select_stmt
69 { let (s,p) = $1 in s,p,Select }
70 | insert_cmd table=IDENT cols=columns_list? VALUES
72 let s = Tables.get_scheme table in
73 let s = match cols with
74 | Some cols -> RA.Scheme.project cols s
77 let p = Syntax.scheme_as_params s in
80 | update_cmd table=IDENT SET assignments=separated_nonempty_list(COMMA,set_column) w=where?
82 let t = Tables.get table in
83 let p2 = get_params_opt [t] (snd t) w in
84 let (cols,exprs) = List.split assignments in
85 let _ = RA.Scheme.project cols (snd t) in (* validates columns *)
86 let p1 = Syntax.get_params_l [t] (snd t) exprs in
87 [], p1 @ p2, Update table
89 | DELETE FROM table=IDENT w=where?
91 let t = Tables.get table in
92 let p = get_params_opt [t] (snd t) w in
96 columns_list: LPAREN cols=separated_nonempty_list(COMMA,IDENT) RPAREN { cols }
98 select_stmt: select_core list(preceded(compound_op,select_core)) o=loption(order) p4=loption(limit)
100 let (s1,p1,tbls) = $1 in
101 let (s2l,p2l) = List.split (List.map (fun (s,p,_) -> s,p) $2) in
102 (* ignoring tables in compound statements - they cannot be used in ORDER BY *)
103 let scheme = List.fold_left RA.Scheme.compound s1 s2l in
104 let p3 = Syntax.get_params_l tbls scheme o in
105 (* RA.Scheme.check_unique scheme; *)
106 scheme,(p1@(List.flatten p2l)@p3@p4)
109 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
115 let (tbls,p2,joined_scheme) = Syntax.join t in
116 let p1 = Syntax.params_of_columns tbls joined_scheme r in
117 let p3 = Syntax.get_params_opt tbls joined_scheme w in
118 let p4 = Syntax.get_params_l tbls joined_scheme g in
119 let p5 = Syntax.get_params_opt tbls joined_scheme h in
120 (Syntax.infer_scheme r tbls joined_scheme, p1 @ p2 @ p3 @ p4 @ p5, tbls)
123 table_list: src=source joins=join_source* { (src,joins) }
125 join_source: NATURAL maybe_join_type JOIN src=source { src,`Natural }
126 | CROSS JOIN src=source { src,`Cross }
127 | qualified_join src=source cond=join_cond { src,cond }
129 qualified_join: COMMA | maybe_join_type JOIN { }
131 join_cond: ON e=expr { `Search e }
132 | USING LPAREN l=separated_nonempty_list(COMMA,IDENT) RPAREN { `Using l }
135 source1: IDENT { Tables.get $1,[] }
136 | LPAREN s=select_core RPAREN { let (s,p,_) = s in ("",s),p }
138 source: src=source1 alias=preceded(AS,IDENT)?
141 | Some name -> let ((n,s),p) = src in ((name,s),p)
145 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
147 update_cmd: UPDATE {}
148 | UPDATE OR CONFLICT_ALGO {} ;
150 select_type: DISTINCT | ALL { }
152 int_or_param: INTEGER { [] }
153 | PARAM { [($1,Some Sql.Type.Int)] }
155 limit: LIMIT p=int_or_param { p }
156 | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 }
157 | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
159 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
160 order_type: DESC | ASC { }
162 where: WHERE e=expr { e }
163 group: GROUP BY l=separated_nonempty_list(COMMA,expr) { l }
164 having: HAVING e=expr { e }
167 | IDENT DOT ASTERISK { Syntax.AllOf $1 }
168 | ASTERISK { Syntax.All }
169 | expr maybe_as { let e = $1 in Syntax.Expr (e,$2) }
171 maybe_as: AS? name=IDENT { Some name }
174 column_defs: separated_nonempty_list(COMMA,column_def1) { $1 }
175 column_def1: name=IDENT t=sql_type? column_def_extra* { RA.attr name (match t with Some t -> t | None -> Type.Int) }
176 column_def_extra: PRIMARY KEY { Some Constraint.PrimaryKey }
177 | NOT NULL { Some Constraint.NotNull }
178 | UNIQUE { Some Constraint.Unique }
179 | AUTOINCREMENT { Some Constraint.Autoincrement }
180 | ON CONFLICT CONFLICT_ALGO { None }
181 | DEFAULT INTEGER { None }
183 set_column: name=IDENT EQUAL e=expr { name,e }
185 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
187 mnot(X): NOT x = X | x = X { x }
190 expr numeric_bin_op expr %prec PLUS { `Func ((Some Int),[$1;$3]) }
191 | expr boolean_bin_op expr %prec AND { `Func ((Some Int),[$1;$3]) }
192 | expr CONCAT_OP expr { `Func ((Some Text),[$1;$3]) }
193 | e1=expr mnot(LIKE_OP) e2=expr e3=escape?
194 { `Func (None,(List.filter_valid [Some e1; Some e2; e3])) }
195 | unary_op expr { $2 }
196 | LPAREN expr RPAREN { $2 }
197 | IDENT { `Column ($1,None) }
198 | t=IDENT DOT c=IDENT
199 | IDENT DOT t=IDENT DOT c=IDENT { `Column (c,Some t) }
200 | INTEGER { `Value Int }
201 | e1=expr mnot(IN) LPAREN l=separated_nonempty_list(COMMA,expr) RPAREN { `Func (None,e1::l) }
202 | e1=expr mnot(IN) LPAREN select=select_stmt RPAREN
204 let (s,p) = select in
205 if (List.length s <> 1) then
206 raise (RA.Scheme.Error (s,"only one column allowed for IN operator"));
207 let l = List.map (fun x -> `Param x) p in
210 | e1=expr IN table=IDENT { Tables.check(table); e1 }
211 (* | FLOAT { `Value Float } *)
212 | TEXT { `Value Text }
213 | BLOB { `Value Blob }
214 | PARAM { `Param ($1,None) }
215 | FUNCTION LPAREN func_params RPAREN { `Func ($1,$3) }
216 | expr TEST_NULL { $1 }
217 | expr BETWEEN expr AND expr { `Func ((Some Int),[$1;$3;$5]) }
219 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
220 func_params: expr_list { $1 }
222 escape: ESCAPE expr { $2 }
223 numeric_bin_op: EQUAL | PLUS | MINUS | ASTERISK | NUM_BINARY_OP { }
224 boolean_bin_op: AND | OR { }
232 sql_type_flavor: T_INTEGER { Type.Int }
233 | T_BLOB { Type.Blob }
234 | T_TEXT { Type.Text }
235 | T_FLOAT { Type.Float }
236 | T_BOOLEAN { Type.Bool }
238 sql_type: t=sql_type_flavor
239 | t=sql_type_flavor LPAREN INTEGER RPAREN
240 | t=sql_type_flavor LPAREN INTEGER COMMA INTEGER RPAREN
243 compound_op: UNION ALL? | EXCEPT | INTERSECT { }
245 maybe_join_type: JOIN_TYPE1? JOIN_TYPE2? { }