start cgi
[sqlgg.git] / sql_parser.mly
blobde502665b48feef3d3e62ca9cae9b81c1f8dee45
1 /* 
2   Simple SQL parser
3 */
6 %{
7   open Printf
8   open Sql
9   open ListMore
10   open Stmt
11   open Syntax
12   open Operators
14   let def_param_name name (id,t) = 
15     let name =
16     match id with
17     | Next | Numbered _ -> name
18     | Named x -> x
19     in
20     (Named name,t)
24 %token <int> INTEGER
25 %token <string> IDENT TEXT BLOB
26 %token <Stmt.param_id> PARAM
27 %token LPAREN RPAREN COMMA EOF DOT NULL
28 %token CONFLICT_ALGO
29 %token SELECT INSERT OR INTO CREATE UPDATE TABLE VALUES WHERE ASTERISK DISTINCT ALL 
30        LIMIT ORDER BY DESC ASC EQUAL DELETE FROM DEFAULT OFFSET SET JOIN LIKE_OP
31        EXCL TILDE NOT FUNCTION TEST_NULL BETWEEN AND ESCAPE USING COMPOUND_OP AS
32        CONCAT_OP JOIN_TYPE1 JOIN_TYPE2 NATURAL REPLACE
33 %token UNIQUE PRIMARY KEY AUTOINCREMENT ON CONFLICT
34 %token NUM_BINARY_OP PLUS MINUS
35 %token T_INTEGER T_BLOB T_TEXT
37 %type <Syntax.expr> expr
38 %type <RA.Scheme.t * Stmt.params> select_core
40 %start <RA.Scheme.t * Stmt.params * Stmt.kind> input
44 input: statement EOF { $1 } ;
46 statement: CREATE TABLE name=IDENT LPAREN scheme=column_defs RPAREN
47               { let () = Tables.add (name,scheme) in ([],[],Create name) }
48          | CREATE TABLE name=IDENT AS select=select_stmt
49               { 
50                 let (s,p) = select in
51                 Tables.add (name,s);
52                 ([],p,Create name)
53               }
54          | select_stmt
55               { let (s,p) = $1 in s,p,Select }
56          | insert_cmd table=IDENT cols=columns_list? VALUES
57               { 
58                 let s = Tables.get_scheme table in
59                 let s = match cols with
60                   | Some cols -> RA.Scheme.project cols s
61                   | None -> s
62                 in
63                 let p = Syntax.scheme_as_params s in
64                 [],p,Insert table
65               }
66          | update_cmd table=IDENT SET assignments=separated_nonempty_list(COMMA,set_column) w=option(where)
67               { 
68                 let p2 = get_params_opt w in
69                 let (cols,exprs) = List.split assignments in
70                 let _ = RA.Scheme.project cols (Tables.get_scheme table) in (* validates columns *)
71                 let p1 = Syntax.get_params_l exprs in
72                 [], p1 @ p2, Update table
73               }
74          | DELETE FROM table=IDENT w=where?
75               { 
76                 let p = get_params_opt w in
77                 [], p, Delete table
78               }
79               
80 columns_list: LPAREN cols=separated_nonempty_list(COMMA,IDENT) RPAREN { cols }
82 select_stmt: select_core list(preceded(COMPOUND_OP,select_core)) o=loption(order) p4=loption(limit)
83               { let (s1,p1) = $1
84                 and (s2,p2) = List.split $2
85                 and p3 = Syntax.get_params_l o
86                 in
87                 List.fold_left RA.Scheme.compound s1 s2,(p1@(List.flatten p2)@p3@p4) }
89 select_core: SELECT select_type? r=separated_nonempty_list(COMMA,column1)
90              FROM t=table_list
91              w=where?
92               {
93                 let p1 = Syntax.params_of_columns r in
94                 let p3 = Syntax.get_params_opt w in
95                 let (tbls,p2) = t in
96                 (Syntax.get_scheme r tbls, p1 @ p2 @ p3) 
97               }
99 table_list: source join_source* { let (s,p) = List.split $2 in (fst $1::s, List.flatten (snd $1::p)) }
100 join_source: join_op s=source p=loption(join_args) { (fst s,snd s @ Syntax.get_params_l p) }
101 source: IDENT { Tables.get $1,[] }
102       | LPAREN s=select_core RPAREN { let (s,p) = s in ("",s),p }
103 join_op: COMMA | NATURAL? JOIN_TYPE1? JOIN_TYPE2? JOIN { } ;
104 join_args: ON e=expr { [e] }
105          | USING LPAREN l=separated_nonempty_list(COMMA,IDENT) RPAREN { List.map (fun name -> Column (name,None)) l }
107 insert_cmd: INSERT OR CONFLICT_ALGO INTO | INSERT INTO | REPLACE INTO { }
109 update_cmd: UPDATE {}
110           | UPDATE OR CONFLICT_ALGO {} ;
112 select_type: DISTINCT | ALL { }
114 int_or_param: INTEGER { [] }
115             | PARAM { [($1,Some Sql.Type.Int)] }
117 limit: LIMIT p=int_or_param { p }
118      | LIMIT p1=int_or_param COMMA p2=int_or_param { p1 @ p2 }
119      | LIMIT p1=int_or_param OFFSET p2=int_or_param { p1 @ p2 }
121 order: ORDER BY l=separated_nonempty_list(COMMA,terminated(expr,order_type?)) { l }
122 order_type: DESC | ASC { }
124 where: WHERE e=expr { e }
126 column1:
127        | IDENT DOT ASTERISK { Syntax.AllOf $1 }
128        | ASTERISK { Syntax.All }
129        | expr maybe_as { let e = $1 in Syntax.Expr (e,$2) }
131 maybe_as: option(AS) name=IDENT { Some name }
132         | { None }
134 column_defs: separated_nonempty_list(COMMA,column_def1) { $1 }
135 column_def1: IDENT sql_type column_def_extra* { RA.attr $1 $2 } ;
136 column_def_extra: PRIMARY KEY { Some Constraint.PrimaryKey }
137                 | NOT NULL { Some Constraint.NotNull }
138                 | UNIQUE { Some Constraint.Unique }
139                 | AUTOINCREMENT { Some Constraint.Autoincrement }
140                 | ON CONFLICT CONFLICT_ALGO { None } ;
141                 | DEFAULT INTEGER { None }
143 set_column: name=IDENT EQUAL e=expr { name,e }
145 (* expr: expr1 { $1 >> Syntax.expr_to_string >> prerr_endline; $1 } *)
147 expr:
148       expr binary_op expr { Sub [$1;$3] }
149 (*     | expr NOT? LIKE_OP expr (*escape?*) { Sub [$1;$4] } *)
150 (*     | unary_op expr { $2 } *)
151     | LPAREN expr RPAREN { $2 }
152     | IDENT { Column ($1,None) }
153     | t=IDENT DOT c=IDENT
154     | IDENT DOT t=IDENT DOT c=IDENT { Column (c,Some t) }
155     | INTEGER { Value Sql.Type.Int }
156     | TEXT { Value Sql.Type.Text }
157     | BLOB { Value Sql.Type.Blob }
158     | PARAM { Param ($1,None) }
159     | FUNCTION LPAREN func_params RPAREN { Sub $3 }
160     | expr TEST_NULL { $1 }
161     | expr BETWEEN expr AND expr { Sub [$1;$3;$5] }
163 expr_list: separated_nonempty_list(COMMA,expr) { $1 }
164 func_params: expr_list { $1 }
165            | ASTERISK { [] } ;
166 escape: ESCAPE expr { $2 }
167 binary_op: EQUAL | PLUS | MINUS | ASTERISK | AND | OR | NUM_BINARY_OP | CONCAT_OP { } 
169 unary_op: EXCL { }
170         | PLUS { }
171         | MINUS { }
172         | TILDE { }
173         | NOT { } ;
175 sql_type: T_INTEGER  { Type.Int }
176         | T_BLOB { Type.Blob }
177         | T_TEXT { Type.Text } ;