diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v index ec6682b1dc..a34c41a5df 100644 --- a/vlib/orm/orm_test.v +++ b/vlib/orm/orm_test.v @@ -80,12 +80,20 @@ fn test_orm_sqlite() { assert users3.len == 2 assert users3[0].age == 29 assert users3[1].age == 31 + // - //user2 := User{} - //x := sql db { - //insert user2 into User - //} + new_user := User{name:'New user' age:30} + sql db { + insert new_user into User + } //db.insert(user2) + x := sql db { + select from User where id == 4 + } + println(x) + assert x.age == 30 + assert x.id == 4 + assert x.name == 'New user' } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 01625bcfce..a18f440837 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -17,8 +17,8 @@ pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | C pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt | - GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | StructDecl | TypeDecl | - UnsafeStmt + GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | SqlInsertExpr | + StructDecl | TypeDecl | UnsafeStmt pub type ScopeObject = ConstField | GlobalDecl | Var @@ -808,6 +808,21 @@ pub: foo int // todo } +/* +pub enum SqlExprKind { + select_ + insert + update +} +*/ +pub struct SqlInsertExpr { +pub: + db_var_name string // `db` in `sql db {` + table_name string + fields []table.Field + object_var_name string // `user` +} + pub struct SqlExpr { pub: typ table.Type @@ -820,9 +835,6 @@ pub: is_array bool } -pub struct SqlInsertExpr { -} - [inline] pub fn (expr Expr) is_blank_ident() bool { match expr { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index aaecfa5877..9f2f7e3638 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2011,6 +2011,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { ast.SqlExpr { return c.sql_expr(node) } + ast.SqlInsertExpr { + return c.sql_insert_expr(node) + } ast.StringLiteral { if node.language == .c { return table.byteptr_type @@ -2608,6 +2611,10 @@ fn (mut c Checker) sql_expr(node ast.SqlExpr) table.Type { return node.typ } +fn (mut c Checker) sql_insert_expr(node ast.SqlInsertExpr) table.Type { + return table.void_type +} + 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 diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index c20c11e27e..0077bed0fe 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -360,6 +360,7 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) { } f.writeln('') } + ast.SqlInsertExpr {} ast.StructDecl { f.struct_decl(it) } @@ -714,6 +715,7 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.write(')') } ast.SqlExpr {} + ast.SqlInsertExpr {} ast.StringLiteral { if node.is_raw { f.write('r') diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 74d7208d7d..e3f57b1db3 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -622,6 +622,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { g.const_decl(node) // } } + ast.Comment {} ast.CompIf { g.comp_if(node) } @@ -660,6 +661,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { } } ast.FnDecl { + g.tmp_count = 0 mut skip := false pos := g.out.buf.len if g.pref.build_mode == .build_module { @@ -767,6 +769,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { g.write_autofree_stmts_when_needed(node) g.return_statement(node) } + ast.SqlInsertExpr{ + g.sql_insert_expr(node) + } ast.StructDecl { name := if node.language == .c { node.name.replace('.', '__') } else { c_name(node.name) } // g.writeln('typedef struct {') @@ -790,9 +795,6 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.UnsafeStmt { g.stmts(node.stmts) } - else { - verror('cgen.stmt(): unhandled node ' + typeof(node)) - } } g.stmt_path_pos.delete(g.stmt_path_pos.len - 1) } @@ -1585,6 +1587,9 @@ fn (mut g Gen) expr(node ast.Expr) { ast.SqlExpr { g.sql_select_expr(node) } + //ast.SqlInsertExpr { + //g.sql_insert_expr(node) + //} ast.StringLiteral { if node.is_raw { escaped_val := node.val.replace_each(['"', '\\"', '\\', '\\\\']) diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index f36c7368de..5069189e18 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -501,6 +501,8 @@ fn (mut g JsGen) stmt(node ast.Stmt) { } g.gen_return_stmt(it) } + ast.SqlInsertExpr{ + } ast.StructDecl { g.gen_struct_decl(it) } @@ -608,6 +610,9 @@ fn (mut g JsGen) expr(node ast.Expr) { ast.SqlExpr{ // TODO } + ast.SqlInsertExpr{ + // TODO + } ast.StringInterLiteral { g.gen_string_inter_literal(it) } diff --git a/vlib/v/gen/sql.v b/vlib/v/gen/sql.v index fb9d6ea6af..6d352bcbf2 100644 --- a/vlib/v/gen/sql.v +++ b/vlib/v/gen/sql.v @@ -13,6 +13,45 @@ const ( ) fn (mut g Gen) sql_insert_expr(node ast.SqlInsertExpr) { + g.writeln('\n\t// sql insert') + db_name := g.new_tmp_var() + g.sql_stmt_name = g.new_tmp_var() + g.writeln('${dbtype}__DB $db_name = $node.db_var_name;') + mut q := 'insert into $node.table_name (' + for i, field in node.fields { + if field.name == 'id' { + continue + } + q += '$field.name' + if i < node.fields.len - 1 { + q += ', ' + } + } + q += ') values (' + for i, field in node.fields { + if field.name == 'id' { + continue + } + q += '?${i+0}' + if i < node.fields.len - 1 { + q += ', ' + } + } + 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') + } + } + g.writeln('sqlite3_step($g.sql_stmt_name);') + g.writeln('sqlite3_finalize($g.sql_stmt_name);') } fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { @@ -48,9 +87,9 @@ fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { // g.write('${dbtype}__DB_q_int(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$q') g.sql_stmt_name = g.new_tmp_var() db_name := g.new_tmp_var() - g.writeln('\n\t// sql') + g.writeln('\n\t// sql select') // g.write('${dbtype}__DB $db_name = *(${dbtype}__DB*)${node.db_var_name}.data;') - g.writeln('${dbtype}__DB $db_name = ${node.db_var_name};') + g.writeln('${dbtype}__DB $db_name = $node.db_var_name;') // g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt(*(${dbtype}__DB*)${node.db_var_name}.data, tos_lit("$q') g.write('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("$q') if node.has_where && node.where_expr is ast.InfixExpr { @@ -118,8 +157,8 @@ fn (mut g Gen) expr_to_sql(expr ast.Expr) { // not a V variable. Need to distinguish column names from V variables. match expr { ast.InfixExpr { - g.expr_to_sql(it.left) - match it.op { + g.expr_to_sql(expr.left) + match expr.op { .eq { g.write(' = ') } .gt { g.write(' > ') } .lt { g.write(' < ') } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 5696b41a6c..64837b278e 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -518,6 +518,9 @@ 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() + } if p.peek_tok.kind == .colon { // `label:` name := p.check_name() diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index a232c0516e..df5d49b59e 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -6,12 +6,19 @@ module parser import v.ast import v.table -fn (mut p Parser) sql_expr() ast.SqlExpr { +fn (mut p Parser) sql_expr() ast.Expr { // `sql db {` p.check_name() db_var_name := p.check_name() p.check(.lcbr) + // kind := ast.SqlExprKind.select_ // + /* + if p.tok.kind == .name && p.tok.lit == 'insert' { + return p.sql_insert_expr(db_var_name) + // kind = .insert + } + */ p.check(.key_select) n := p.check_name() is_count := n == 'count' @@ -97,3 +104,35 @@ fn (mut p Parser) sql_expr() ast.SqlExpr { is_array: !query_one } } + +fn (mut p Parser) sql_insert_expr() ast.SqlInsertExpr { + // `sql db {` + p.check_name() + db_var_name := p.check_name() + p.check(.lcbr) + // kind := ast.SqlExprKind.select_ + // + p.check_name() // insert + mut object_var_name := '' + expr := p.expr(0) + match expr { + ast.Ident { object_var_name = expr.name } + else { p.error('can only insert variables') } + } + n := p.check_name() // into + if n != 'into' { + p.error('expecting `into`') + } + 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.SqlInsertExpr{ + db_var_name: db_var_name + table_name: table_name + fields: fields + object_var_name: object_var_name + } +}