1// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. 2// Use of this source code is governed by an MIT license 3// that can be found in the LICENSE file. 4module parser 5 6import v.ast 7import v.table 8 9fn (mut p Parser) sql_expr() ast.Expr { 10 // `sql db {` 11 pos := p.tok.position() 12 p.check_name() 13 db_expr := p.expr(0) 14 p.check(.lcbr) 15 p.check(.key_select) 16 n := p.check_name() 17 is_count := n == 'count' 18 mut typ := table.void_type 19 if is_count { 20 p.check_name() // from 21 typ = table.int_type 22 } 23 table_type := p.parse_type() // `User` 24 mut where_expr := ast.Expr{} 25 has_where := p.tok.kind == .name && p.tok.lit == 'where' 26 mut query_one := false // one object is returned, not an array 27 if has_where { 28 p.next() 29 where_expr = p.expr(0) 30 // `id == x` means that a single object is returned 31 if !is_count && where_expr is ast.InfixExpr { 32 e := where_expr as ast.InfixExpr 33 if e.op == .eq && e.left is ast.Ident { 34 ident := e.left as ast.Ident 35 if ident.name == 'id' { 36 query_one = true 37 } 38 } 39 } 40 } 41 mut has_limit := false 42 mut limit_expr := ast.Expr{} 43 mut has_offset := false 44 mut offset_expr := ast.Expr{} 45 mut has_order := false 46 mut order_expr := ast.Expr{} 47 mut has_desc := false 48 if p.tok.kind == .name && p.tok.lit == 'order' { 49 p.check_name() // `order` 50 order_pos := p.tok.position() 51 if p.tok.kind == .name && p.tok.lit == 'by' { 52 p.check_name() // `by` 53 } else { 54 p.error_with_pos('use `order by` in ORM queries', order_pos) 55 } 56 has_order = true 57 order_expr = p.expr(0) 58 if p.tok.kind == .name && p.tok.lit == 'desc' { 59 p.check_name() // `desc` 60 has_desc = true 61 } 62 } 63 if p.tok.kind == .name && p.tok.lit == 'limit' { 64 // `limit 1` means that a single object is returned 65 p.check_name() // `limit` 66 if p.tok.kind == .number && p.tok.lit == '1' { 67 query_one = true 68 } 69 has_limit = true 70 limit_expr = p.expr(0) 71 } 72 if p.tok.kind == .name && p.tok.lit == 'offset' { 73 p.check_name() // `offset` 74 has_offset = true 75 offset_expr = p.expr(0) 76 } 77 if !query_one && !is_count { 78 // return an array 79 typ = table.new_type(p.table.find_or_register_array(table_type, 1, p.mod)) 80 } else if !is_count { 81 // return a single object 82 // TODO optional 83 // typ = table_type.set_flag(.optional) 84 typ = table_type 85 } 86 p.check(.rcbr) 87 return ast.SqlExpr{ 88 is_count: is_count 89 typ: typ 90 db_expr: db_expr 91 table_type: table_type 92 where_expr: where_expr 93 has_where: has_where 94 has_limit: has_limit 95 limit_expr: limit_expr 96 has_offset: has_offset 97 offset_expr: offset_expr 98 has_order: has_order 99 order_expr: order_expr 100 has_desc: has_desc 101 is_array: !query_one 102 pos: pos 103 } 104} 105 106// insert user into User 107// update User set nr_oders=nr_orders+1 where id == user_id 108fn (mut p Parser) sql_stmt() ast.SqlStmt { 109 pos := p.tok.position() 110 p.inside_match = true 111 defer { 112 p.inside_match = false 113 } 114 // `sql db {` 115 p.check_name() 116 db_expr := p.expr(0) 117 // println(typeof(db_expr)) 118 p.check(.lcbr) 119 // kind := ast.SqlExprKind.select_ 120 // 121 mut n := p.check_name() // insert 122 mut kind := ast.SqlStmtKind.insert 123 if n == 'delete' { 124 kind = .delete 125 } else if n == 'update' { 126 kind = .update 127 } 128 mut inserted_var_name := '' 129 mut table_name := '' 130 if kind != .delete { 131 expr := p.expr(0) 132 match expr { 133 ast.Ident { 134 if kind == .insert { 135 inserted_var_name = expr.name 136 } else if kind == .update { 137 table_name = expr.name 138 } 139 } 140 else { 141 p.error('can only insert variables') 142 } 143 } 144 } 145 n = p.check_name() // into 146 mut updated_columns := []string{} 147 mut update_exprs := []ast.Expr{cap: 5} 148 if kind == .insert && n != 'into' { 149 p.error('expecting `into`') 150 } else if kind == .update { 151 if n != 'set' { 152 p.error('expecting `set`') 153 } 154 for { 155 column := p.check_name() 156 updated_columns << column 157 p.check(.assign) 158 update_exprs << p.expr(0) 159 if p.tok.kind == .comma { 160 p.check(.comma) 161 } else { 162 break 163 } 164 } 165 } else if kind == .delete && n != 'from' { 166 p.error('expecting `from`') 167 } 168 mut table_type := table.Type(0) 169 mut where_expr := ast.Expr{} 170 if kind == .insert { 171 table_type = p.parse_type() // `User` 172 sym := p.table.get_type_symbol(table_type) 173 // info := sym.info as table.Struct 174 // fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type]) 175 table_name = sym.name 176 } else if kind == .update { 177 if !p.pref.is_fmt { 178 // NB: in vfmt mode, v parses just a single file and table_name may not have been registered 179 idx := p.table.find_type_idx(p.prepend_mod(table_name)) 180 table_type = table.new_type(idx) 181 } 182 p.check_sql_keyword('where') 183 where_expr = p.expr(0) 184 } else if kind == .delete { 185 table_type = p.parse_type() 186 sym := p.table.get_type_symbol(table_type) 187 table_name = sym.name 188 p.check_sql_keyword('where') 189 where_expr = p.expr(0) 190 } 191 p.check(.rcbr) 192 return ast.SqlStmt{ 193 db_expr: db_expr 194 table_name: table_name 195 table_type: table_type 196 object_var_name: inserted_var_name 197 pos: pos 198 updated_columns: updated_columns 199 update_exprs: update_exprs 200 kind: kind 201 where_expr: where_expr 202 } 203} 204 205fn (mut p Parser) check_sql_keyword(name string) { 206 if p.check_name() != name { 207 p.error('orm: expecting `$name`') 208 } 209} 210