diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v index e8a6e789fd..7c5eb0f7d3 100644 --- a/vlib/orm/orm_test.v +++ b/vlib/orm/orm_test.v @@ -126,10 +126,9 @@ fn test_orm_sqlite() { // /* sql db { - update User set age = 31 where name = 'Kate' + update User set age = 31 where name == 'Kate' } */ - } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a8d644b255..d94ab9b31e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -343,6 +343,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { if type_sym.kind == .interface_ { c.error('cannot instantiate interface `$type_sym.name`', struct_init.pos) } + if type_sym.kind == .alias { + c.error('cannot instantiate type alias `$type_sym.name`', struct_init.pos) + return table.void_type + } if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.mod != c.mod { c.error('type `$type_sym.name` is private', struct_init.pos) } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 478b6ceef7..52e3a60556 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -501,7 +501,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { .name, .key_mut, .key_static, .mul { if p.tok.kind == .name { if p.tok.lit == 'sql' { - return p.sql_insert_expr() + return p.sql_stmt() } if p.peek_tok.kind == .colon { // `label:` diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index e6700d9c3f..8263275bd8 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -31,7 +31,7 @@ fn (mut p Parser) sql_expr() ast.Expr { sym := p.table.get_type_symbol(table_type) table_name := sym.name mut where_expr := ast.Expr{} - has_where := p.tok.kind == .name && p.tok.lit == 'where' + has_where := p.tok.kind == .name && p.tok.lit == 'where' mut query_one := false // one object is returned, not an array if has_where { p.next() @@ -47,7 +47,7 @@ fn (mut p Parser) sql_expr() ast.Expr { } } } - if p.tok.kind ==.name && p.tok.lit == 'limit' { + if p.tok.kind == .name && p.tok.lit == 'limit' { // `limit 1` means that a single object is returned p.check_name() // `limit` if p.tok.kind == .number && p.tok.lit == '1' { @@ -73,7 +73,8 @@ fn (mut p Parser) sql_expr() ast.Expr { // get only string and int fields // mut fields := []Var info := sym.info as table.Struct - fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type] && 'skip' !in it.attrs) + fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type] && + 'skip' !in it.attrs) if fields.len == 0 { p.error('V orm: select: empty fields in `$table_name`') } @@ -103,37 +104,79 @@ fn (mut p Parser) sql_expr() ast.Expr { } } -fn (mut p Parser) sql_insert_expr() ast.SqlStmt{ +// insert user into User +// update User set nr_oders=nr_orders+1 where id == user_id +fn (mut p Parser) sql_stmt() ast.SqlStmt { p.inside_match = true - defer { p.inside_match = false } + defer { + p.inside_match = false + } // `sql db {` p.check_name() db_expr := p.expr(0) - //println(typeof(db_expr)) + // println(typeof(db_expr)) p.check(.lcbr) // kind := ast.SqlExprKind.select_ // - p.check_name() // insert - mut object_var_name := '' + mut n := p.check_name() // insert + mut kind := ast.SqlStmtKind.insert + if n == 'delete' { + kind = .delete + } else if n == 'update' { + kind = .update + } + mut inserted_var_name := '' + mut table_name := '' expr := p.expr(0) match expr { - ast.Ident { object_var_name = expr.name } - else { p.error('can only insert variables') } + ast.Ident { + if kind == .insert { + inserted_var_name = expr.name + } else if kind == .update { + table_name = expr.name + } + } + else { + p.error('can only insert variables') + } } - n := p.check_name() // into - if n != 'into' { + n = p.check_name() // into + mut updated_columns := []string{} + if kind == .insert && n != 'into' { p.error('expecting `into`') + } else if kind == .update { + if n != 'set' { + p.error('expecting `set`') + } + column := p.check_name() + updated_columns << column + p.check(.assign) + p.expr(0) + } + mut table_type := table.Type(0) + if kind == .insert { + table_type = p.parse_type() // `User` + sym := p.table.get_type_symbol(table_type) + // info := sym.info as table.Struct + // fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type]) + table_name = sym.name + } else if kind == .update { + idx := p.table.find_type_idx(table_name) + table_type = table.new_type(idx) + p.check_sql_keyword('where') + p.expr(0) } - table_type := p.parse_type() // `User` - sym := p.table.get_type_symbol(table_type) - // info := sym.info as table.Struct - // fields := info.fields.filter(it.typ in [table.string_type, table.int_type, table.bool_type]) - table_name := sym.name p.check(.rcbr) return ast.SqlStmt{ db_expr: db_expr table_name: table_name table_type: table_type - object_var_name: object_var_name + object_var_name: inserted_var_name + } +} + +fn (mut p Parser) check_sql_keyword(name string) { + if p.check_name() != name { + p.error('orm: expecting `$name`') } } diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 2ff7fc1d8d..c98987e19b 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -62,11 +62,13 @@ fn (mut p Parser) struct_decl() ast.StructDecl { mut comments := []ast.Comment{} for p.tok.kind == .comment { comments << p.comment() - if p.tok.kind == .rcbr {break} + if p.tok.kind == .rcbr { + break + } } if p.tok.kind == .rcbr { end_comments = comments - break + break } if p.tok.kind == .key_pub { p.next() @@ -112,17 +114,19 @@ fn (mut p Parser) struct_decl() ast.StructDecl { } for p.tok.kind == .comment { comments << p.comment() - if p.tok.kind == .rcbr {break} + if p.tok.kind == .rcbr { + break + } } field_start_pos := p.tok.position() field_name := p.check_name() // p.warn('field $field_name') - for p.tok.kind == .comment { comments << p.comment() - if p.tok.kind == .rcbr {break} + if p.tok.kind == .rcbr { + break + } } - // println(p.tok.position()) typ := p.parse_type() // field_pos := field_start_pos.extend(p.tok.position()) @@ -139,14 +143,15 @@ fn (mut p Parser) struct_decl() ast.StructDecl { */ // Comments after type (same line) line_pos := field_pos.line_nr - for p.tok.kind == .comment && line_pos + 1 == p.tok.line_nr{ + for p.tok.kind == .comment && line_pos + 1 == p.tok.line_nr { if p.tok.lit.contains('\n') { break } comments << p.comment() - if p.tok.kind == .rcbr {break} + if p.tok.kind == .rcbr { + break + } } - mut attrs := []string{} if p.tok.kind == .lsbr { parsed_attrs := p.attributes(false) @@ -163,7 +168,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl { // p.expr(0) default_expr = p.expr(0) match default_expr { - ast.EnumVal { it.typ = typ } + ast.EnumVal { default_expr.typ = typ } // TODO: implement all types?? else {} } @@ -340,7 +345,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { p.error('interface methods cannot contain uppercase letters, use snake_case instead') } // field_names << name - args2, _ := p.fn_args() + args2, _ := p.fn_args() // TODO merge table.Arg and ast.Arg to avoid this mut args := [table.Arg{ name: 'x' typ: typ