From b280e08ee0d7e987d8aef26ede6921da230ddd1e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 25 Jun 2020 17:12:32 +0200 Subject: [PATCH] orm: `update` cgen --- vlib/orm/orm_test.v | 8 ++- vlib/v/ast/ast.v | 3 + vlib/v/checker/checker.v | 30 +++++----- vlib/v/gen/sql.v | 123 +++++++++++++++++++++++++-------------- vlib/v/parser/sql.v | 12 ++-- 5 files changed, 109 insertions(+), 67 deletions(-) diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v index b2de07953f..9a3265f7f8 100644 --- a/vlib/orm/orm_test.v +++ b/vlib/orm/orm_test.v @@ -116,11 +116,15 @@ fn test_orm_sqlite() { assert customer.is_customer == true assert customer.name == 'Kate' // - /* sql db { update User set age = 31 where name == 'Kate' } - */ + kate2 := sql db { + select from User where id == 3 + } + println(kate2) + assert kate2.age == 31 + assert kate2.name == 'Kate' } struct User { diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 6593f3684d..6d7c8b8b9c 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -822,6 +822,9 @@ pub: object_var_name string // `user` table_type table.Type pos token.Position + where_expr Expr + updated_columns []string //for `update set x=y` + update_exprs []Expr//for `update` pub mut: table_name string fields []table.Field diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 8b52a5e0b2..bd2f9745a2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -319,8 +319,8 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) { if !c.check_types(field_expr_type, field.typ) { field_expr_type_sym := c.table.get_type_symbol(field_expr_type) field_type_sym := c.table.get_type_symbol(field.typ) - c.error('default expression for field `$field.name` ' + - 'has type `$field_expr_type_sym.name`, but should be `$field_type_sym.name`', field.default_expr.position()) + c.error('default expression for field `$field.name` ' + 'has type `$field_expr_type_sym.name`, but should be `$field_type_sym.name`', + field.default_expr.position()) } } } @@ -413,9 +413,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { expr_type := c.expr(field.expr) expr_type_sym := c.table.get_type_symbol(expr_type) field_type_sym := c.table.get_type_symbol(info_field.typ) - if !c.check_types(expr_type, info_field.typ) && expr_type != table.void_type && - expr_type_sym.kind != .placeholder { - + if !c.check_types(expr_type, info_field.typ) && expr_type != table.void_type && + expr_type_sym.kind != .placeholder { c.error('!cannot assign $expr_type_sym.kind `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`', field.pos) } @@ -535,8 +534,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { if infix_expr.op in [.div, .mod] { if (infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() == '0') || - (infix_expr.right is ast.FloatLiteral && - infix_expr.right.str().f64() == 0.0) { + (infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0) { oper := if infix_expr.op == .div { 'division' } else { 'modulo' } c.error('$oper by zero', right_pos) } @@ -825,7 +823,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { } if method := c.table.type_find_method(left_type_sym, method_name) { if !method.is_pub && !c.is_builtin_mod && !c.pref.is_test && - left_type_sym.mod != c.mod && left_type_sym.mod != '' { // method.mod != c.mod { + left_type_sym.mod != c.mod && + left_type_sym.mod != '' { // method.mod != c.mod { // If a private method is called outside of the module // its receiver type is defined in, show an error. // println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod') @@ -836,7 +835,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { // call_expr.is_mut = true } if method.return_type == table.void_type && - method.ctdefine.len > 0 && method.ctdefine !in c.pref.compile_defines { + method.ctdefine.len > 0 && + method.ctdefine !in c.pref.compile_defines { call_expr.should_be_skipped = true } nr_args := if method.args.len == 0 { 0 } else { method.args.len - 1 } @@ -1020,7 +1020,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { } call_expr.return_type = f.return_type if f.return_type == table.void_type && - f.ctdefine.len > 0 && f.ctdefine !in c.pref.compile_defines { + f.ctdefine.len > 0 && + f.ctdefine !in c.pref.compile_defines { call_expr.should_be_skipped = true } if f.language != .v || call_expr.language != .v { @@ -2339,7 +2340,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol ast.EnumVal { key = expr.val } else { key = expr.str() } } - val := if key in branch_exprs { branch_exprs[key] } /**/ else { 0 } + val := if key in branch_exprs { branch_exprs[key] } else { 0 } if val == 1 { c.error('match case `$key` is handled more than once', branch.pos) } @@ -2714,7 +2715,6 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) table.Type { return node.typ } - fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) table.Type { sym := c.table.get_type_symbol(node.table_type) info := sym.info as table.Struct @@ -2736,8 +2736,6 @@ fn (c &Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Positio return fields } - - fn (mut c Checker) fn_decl(it ast.FnDecl) { if it.is_generic && c.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion // loop thru each generic type and generate a function @@ -2774,8 +2772,8 @@ fn (mut c Checker) fn_decl(it ast.FnDecl) { } sym.methods.delete(idx) // - c.error('cannot define new methods on non-local `$sym.name` (' + - 'current module is `$c.mod`, `$sym.name` is from `$sym.mod`)', it.pos) + c.error('cannot define new methods on non-local `$sym.name` (' + 'current module is `$c.mod`, `$sym.name` is from `$sym.mod`)', + it.pos) } } if it.language == .v { diff --git a/vlib/v/gen/sql.v b/vlib/v/gen/sql.v index 07d371e420..aacc2da99d 100644 --- a/vlib/v/gen/sql.v +++ b/vlib/v/gen/sql.v @@ -11,51 +11,84 @@ const ( dbtype = 'sqlite' ) -enum SqlExprSide { left right } +enum SqlExprSide { + left + right +} fn (mut g Gen) sql_stmt(node ast.SqlStmt) { + g.sql_i = 0 g.writeln('\n\t// sql insert') db_name := g.new_tmp_var() g.sql_stmt_name = g.new_tmp_var() g.write('${dbtype}__DB $db_name = ') g.expr(node.db_expr) g.writeln(';') - mut q := 'insert into $node.table_name (' - for i, field in node.fields { - if field.name == 'id' { - continue + g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("') + if node.kind == .insert { + g.write('insert into $node.table_name (') + } else { + g.write('update $node.table_name set ') } - q += '$field.name' - if i < node.fields.len - 1 { - q += ', ' + if node.kind == .insert { + for i, field in node.fields { + if field.name == 'id' { + continue + } + g.write(field.name) + if i < node.fields.len - 1 { + g.write(', ') + } + } + g.write( ') values (') + for i, field in node.fields { + if field.name == 'id' { + continue + } + g.write('?${i+0}') + if i < node.fields.len - 1 { + g.write(', ') + } + } + g.write(')') + } else if node.kind == .update { + for i, col in node.updated_columns { + g.write(' $col = ') + g.expr_to_sql(node.update_exprs[i]) + } + g.write(' where ') + + } + + + if node.kind == .update { + g.expr_to_sql(node.where_expr) + } + + g.writeln('"));') + if node.kind == .insert { + for i, field in node.fields { + if field.name == 'id' { + continue + } + x := '${node.object_var_name}.$field.name' + if field.typ == table.string_type { + g.writeln('sqlite3_bind_text($g.sql_stmt_name, ${i+0}, ${x}.str, ${x}.len, 0);') + } else { + g.writeln('sqlite3_bind_int($g.sql_stmt_name, ${i+0}, $x); // stmt') + } } } - q += ') values (' - for i, field in node.fields { - if field.name == 'id' { - continue + else if node.kind == .update { + } - q += '?${i+0}' - if i < node.fields.len - 1 { - q += ', ' - } - } - q += ')' - println(q) - g.writeln('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("$q"));') - for i, field in node.fields { - if field.name == 'id' { - continue - } - x := '${node.object_var_name}.$field.name' - if field.typ == table.string_type { - g.writeln('sqlite3_bind_text($g.sql_stmt_name, ${i+0}, ${x}.str, ${x}.len, 0);') - } else { - g.writeln('sqlite3_bind_int($g.sql_stmt_name, ${i+0}, $x); //insertl') - } - } + + // Dump all sql parameters generated by our custom expr handler + binds := g.sql_buf.str() + g.sql_buf = strings.new_builder(100) + g.writeln(binds) g.writeln('sqlite3_step($g.sql_stmt_name);') - g.writeln('puts(sqlite3_errmsg(${db_name}.conn));') + g.writeln('if (strcmp(sqlite3_errmsg(${db_name}.conn), "not an error") != 0) puts(sqlite3_errmsg(${db_name}.conn)); ') g.writeln('sqlite3_finalize($g.sql_stmt_name);') } @@ -107,7 +140,7 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { binds := g.sql_buf.str() g.sql_buf = strings.new_builder(100) g.writeln(binds) - g.writeln('puts(sqlite3_errmsg(${db_name}.conn));') + g.writeln('if (strcmp(sqlite3_errmsg(${db_name}.conn), "not an error") != 0) puts(sqlite3_errmsg(${db_name}.conn)); ') // if node.is_count { g.writeln('$cur_line ${dbtype}__get_int_from_stmt($g.sql_stmt_name);') @@ -127,7 +160,7 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { g.writeln('\t$elem_type_str $tmp = ($elem_type_str) {') // sym := g.table.get_type_symbol(array_info.elem_type) - info := sym.info as table.Struct + info := sym.info as table.Struct for field in info.fields { g.zero_struct_field(field) } @@ -139,7 +172,7 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { // If we don't, string values are going to be nil etc for fields that are not returned // by the db engine. sym := g.table.get_type_symbol(node.typ) - info := sym.info as table.Struct + info := sym.info as table.Struct for field in info.fields { g.zero_struct_field(field) } @@ -148,7 +181,7 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { // g.writeln('int _step_res$tmp = sqlite3_step($g.sql_stmt_name);') if node.is_array { - //g.writeln('\tprintf("step res=%d\\n", _step_res$tmp);') + // g.writeln('\tprintf("step res=%d\\n", _step_res$tmp);') g.writeln('\tif (_step_res$tmp == SQLITE_DONE) break;') g.writeln('\tif (_step_res$tmp == SQLITE_ROW) ;') // another row g.writeln('\telse if (_step_res$tmp != SQLITE_OK) break;') @@ -177,12 +210,10 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { fn (mut g Gen) sql_bind_int(val string) { g.sql_buf.writeln('sqlite3_bind_int($g.sql_stmt_name, $g.sql_i, $val);') - } -fn (mut g Gen) sql_bind_string(val string, len string) { +fn (mut g Gen) sql_bind_string(val, len string) { g.sql_buf.writeln('sqlite3_bind_text($g.sql_stmt_name, $g.sql_i, $val, $len, 0);') - } fn (mut g Gen) expr_to_sql(expr ast.Expr) { @@ -221,13 +252,17 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { // true/false literals were added to Sqlite 3.23 (2018-04-02) // but lots of apps/distros use older sqlite (e.g. Ubuntu 18.04 LTS ) g.inc_sql_i() - g.sql_bind_int(if it.val { '1' } else { '0' }) + g.sql_bind_int(if it.val { + '1' + } else { + '0' + }) } ast.Ident { // `name == user_name` => `name == ?1` // for left sides just add a string, for right sides, generate the bindings if g.sql_side == .left { - //println("sql gen left $expr.name") + // println("sql gen left $expr.name") g.write(expr.name) } else { g.inc_sql_i() @@ -235,11 +270,9 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { typ := info.typ if typ == table.string_type { g.sql_bind_string('${expr.name}.str', '${expr.name}.len') - } - else if typ == table.int_type { + } else if typ == table.int_type { g.sql_bind_int(expr.name) - } - else { + } else { verror('bad sql type $typ') } } diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index f901a5c5a4..7f8461d843 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -60,11 +60,9 @@ fn (mut p Parser) sql_expr() ast.Expr { is_count: is_count typ: typ db_expr: db_expr - //table_name: table_name table_type: table_type where_expr: where_expr has_where: has_where - //fields: fields is_array: !query_one pos: pos } @@ -109,6 +107,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt { } n = p.check_name() // into mut updated_columns := []string{} + mut update_exprs := []ast.Expr{cap: 5} if kind == .insert && n != 'into' { p.error('expecting `into`') } else if kind == .update { @@ -118,9 +117,10 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt { column := p.check_name() updated_columns << column p.check(.assign) - p.expr(0) + update_exprs << p.expr(0) } mut table_type := table.Type(0) + mut where_expr := ast.Expr{} if kind == .insert { table_type = p.parse_type() // `User` sym := p.table.get_type_symbol(table_type) @@ -131,7 +131,7 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt { idx := p.table.find_type_idx(table_name) table_type = table.new_type(idx) p.check_sql_keyword('where') - p.expr(0) + where_expr = p.expr(0) } p.check(.rcbr) return ast.SqlStmt{ @@ -140,6 +140,10 @@ fn (mut p Parser) sql_stmt() ast.SqlStmt { table_type: table_type object_var_name: inserted_var_name pos: pos + updated_columns: updated_columns + update_exprs: update_exprs + kind: kind + where_expr: where_expr } }