orm: `update` cgen

pull/5450/head
Alexander Medvednikov 2020-06-25 17:12:32 +02:00
parent a8b0dfb38a
commit b280e08ee0
5 changed files with 109 additions and 67 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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())
}
}
}
@ -415,7 +415,6 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.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 {
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 {

View File

@ -11,38 +11,62 @@ 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 ('
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 ')
}
if node.kind == .insert {
for i, field in node.fields {
if field.name == 'id' {
continue
}
q += '$field.name'
g.write(field.name)
if i < node.fields.len - 1 {
q += ', '
g.write(', ')
}
}
q += ') values ('
g.write( ') values (')
for i, field in node.fields {
if field.name == 'id' {
continue
}
q += '?${i+0}'
g.write('?${i+0}')
if i < node.fields.len - 1 {
q += ', '
g.write(', ')
}
}
q += ')'
println(q)
g.writeln('sqlite3_stmt* $g.sql_stmt_name = ${dbtype}__DB_init_stmt($db_name, tos_lit("$q"));')
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
@ -51,11 +75,20 @@ fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
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_bind_int($g.sql_stmt_name, ${i+0}, $x); // stmt')
}
}
}
else if node.kind == .update {
}
// 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);')
@ -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')
}
}

View File

@ -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
}
}