Merge branch 'master' of git@lemon:sql2cpp
[sqlgg.git] / sql_parser.mly
blob44f15155e94924a5df89cfe22537eb7ba2bb2758
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 COMPOUND_OP AS
34        CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL REPLACE
35 %token UNIQUE PRIMARY KEY AUTOINCREMENT ON CONFLICT
36 %token NUM_BINARY_OP PLUS MINUS
37 %token T_INTEGER T_BLOB T_TEXT
39 %type <Syntax.expr> expr
41 %start <RA.Scheme.t * Stmt.params * Stmt.kind> input
45 input: statement EOF { $1 } ;
47 statement: CREATE TABLE name=IDENT LPAREN scheme=column_defs RPAREN
48               { let () = Tables.add (name,scheme) in ([],[],Create name) }
49          | CREATE TABLE name=IDENT AS select=select_stmt
50               { 
51                 let (s,p) = select in
52                 Tables.add (name,s);
53                 ([],p,Create name)
54               }
55          | select_stmt
56               { let (s,p) = $1 in s,p,Select }
57          | insert_cmd table=IDENT cols=columns_list? VALUES
58               { 
59                 let s = Tables.get_scheme table in
60                 let s = match cols with
61                   | Some cols -> RA.Scheme.project cols s
62                   | None -> s
63                 in
64                 let p = Syntax.scheme_as_params s in
65                 [],p,Insert table
66               }
67          | update_cmd table=IDENT SET assignments=separated_nonempty_list(COMMA,set_column) w=where?
68               { 
69                 let t = Tables.get table in
70                 let p2 = get_params_opt [t] w in
71                 let (cols,exprs) = List.split assignments in
72                 let _ = RA.Scheme.project cols (snd t) in (* validates columns *)
73                 let p1 = Syntax.get_params_l [t] exprs in
74                 [], p1 @ p2, Update table
75               }
76          | DELETE FROM table=IDENT w=where?
77               { 
78                 let p = get_params_opt [Tables.get table] w in
79                 [], p, Delete table
80               }
81               
82 columns_list: LPAREN cols=separated_nonempty_list(COMMA,IDENT) RPAREN { cols }
84 select_stmt: select_core list(preceded(COMPOUND_OP,select_core)) o=loption(order) p4=loption(limit)
85               { let (s1,p1,tbls) = $1 in
86                 let (s2,p2) = List.split (List.map (fun (s,p,_) -> s,p) $2) in (* ignore tables in compound statements - they cannot be used in ORDER BY *)
87                 let p3 = Syntax.get_params_l tbls o in
88                 List.fold_left RA.Scheme.compound s1 s2,(p1@(List.flatten p2)@p3@p4) }
90 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
91              FROM t=table_list
92              w=where?
93               {
94                 let (tbls,p2) = t in
95                 let p1 = Syntax.params_of_columns tbls r in
96                 let p3 = Syntax.get_params_opt tbls w in
97                 (Syntax.get_scheme r tbls, p1 @ p2 @ p3, tbls)
98               }
100 table_list: source join_source* 
101     { 
102       let (s,p) = List.split $2 in 
103       (fst $1::s, List.flatten (snd $1::p))
104     }
105 join_source: join_op s=source p=loption(join_args) 
106     { 
107       (* FIXME more tables in scope *)
108       (fst s,snd s @ Syntax.get_params_l [fst s] p)
109     }
110 source: IDENT { Tables.get $1,[] }
111       | LPAREN s=select_core RPAREN { let (s,p,_) = s in ("",s),p }
112 join_op: COMMA | NATURAL? JOIN_TYPE1? JOIN_TYPE2? JOIN { } ;
113 join_args: ON e=expr { [e] }
114          | USING LPAREN l=separated_nonempty_list(COMMA,IDENT) RPAREN { List.map (fun name -> `Column (name,None)) l }
116 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
118 update_cmd: UPDATE {}
119           | UPDATE OR CONFLICT_ALGO {} ;
121 select_type: DISTINCT | ALL { }
123 int_or_param: INTEGER { [] }
124             | PARAM { [($1,Some Sql.Type.Int)] }
126 limit: LIMIT p=int_or_param { p }
127      | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 }
128      | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
130 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
131 order_type: DESC | ASC { }
133 where: WHERE e=expr { e }
135 column1:
136        | IDENT DOT ASTERISK { Syntax.AllOf $1 }
137        | ASTERISK { Syntax.All }
138        | expr maybe_as { let e = $1 in Syntax.Expr (e,$2) }
140 maybe_as: option(AS) name=IDENT { Some name }
141         | { None }
143 column_defs: separated_nonempty_list(COMMA,column_def1) { $1 }
144 column_def1: IDENT sql_type column_def_extra* { RA.attr $1 $2 } ;
145 column_def_extra: PRIMARY KEY { Some Constraint.PrimaryKey }
146                 | NOT NULL { Some Constraint.NotNull }
147                 | UNIQUE { Some Constraint.Unique }
148                 | AUTOINCREMENT { Some Constraint.Autoincrement }
149                 | ON CONFLICT CONFLICT_ALGO { None } ;
150                 | DEFAULT INTEGER { None }
152 set_column: name=IDENT EQUAL e=expr { name,e }
154 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
156 expr:
157      expr numeric_bin_op expr { `Func ((Some Int),[$1;$3]) }
158     | expr CONCAT_OP expr { `Func ((Some Text),[$1;$3]) }
159 (*     | expr NOT? LIKE_OP expr (*escape?*) { Sub [$1;$4] } *)
160 (*     | unary_op expr { $2 } *)
161     | LPAREN expr RPAREN { $2 }
162     | IDENT { `Column ($1,None) }
163     | t=IDENT DOT c=IDENT
164     | IDENT DOT t=IDENT DOT c=IDENT { `Column (c,Some t) }
165     | INTEGER { `Value Int }
166 (*     | FLOAT { `Value Float } *)
167     | TEXT { `Value Text }
168     | BLOB { `Value Blob }
169     | PARAM { `Param ($1,None) }
170     | FUNCTION LPAREN func_params RPAREN { `Func ($1,$3) }
171     | expr TEST_NULL { $1 }
172     | expr BETWEEN expr AND expr { `Func ((Some Int),[$1;$3;$5]) }
174 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
175 func_params: expr_list { $1 }
176            | ASTERISK { [] } ;
177 escape: ESCAPE expr { $2 }
178 numeric_bin_op: EQUAL | PLUS | MINUS | ASTERISK | AND | OR | NUM_BINARY_OP { } 
180 unary_op: EXCL { }
181         | PLUS { }
182         | MINUS { }
183         | TILDE { }
184         | NOT { } ;
186 sql_type: T_INTEGER  { Type.Int }
187         | T_BLOB { Type.Blob }
188         | T_TEXT { Type.Text } ;