orm: select where id = x

pull/5401/head
Alexander Medvednikov 2020-06-17 04:05:13 +02:00
parent ddb1770af2
commit fb5cae7376
6 changed files with 100 additions and 35 deletions

View File

@ -3,27 +3,28 @@
//import term //import term
import sqlite import sqlite
struct Modules { struct Module {
id int id int
user_id int user_id int
name string //name string
url string //url string
//nr_downloads int //nr_downloads int
} }
struct User { struct User {
id int id int
age int
name string name string
} }
fn test_orm_sqlite() { fn test_orm_sqlite() {
db := sqlite.connect(':memory:') or { panic(err) } db := sqlite.connect(':memory:') or { panic(err) }
db.exec("drop table if exists User") db.exec("drop table if exists User")
db.exec("create table User (id integer primary key, name text default '');") db.exec("create table User (id integer primary key, age int default 0, name text default '');")
name := 'sam' name := 'sam'
db.exec("insert into User (name) values ('Sam')") db.exec("insert into User (name, age) values ('Sam', 29)")
db.exec("insert into User (name) values ('Peter')") db.exec("insert into User (name) values ('Peter')")
db.exec("insert into User (name) values ('Kate')") db.exec("insert into User (name) values ('Kate')")
nr_all_users := sql db { nr_all_users := sql db {
@ -48,6 +49,14 @@ fn test_orm_sqlite() {
select count from User where id == 1 && name == name select count from User where id == 1 && name == name
} }
println('nr_sams=$nr_sams') println('nr_sams=$nr_sams')
//
user := sql db {
select from User where id == 1
}
println(user)
assert user.name == 'Sam'
assert user.id == 1
assert user.age == 29
} }

View File

@ -15,10 +15,13 @@ fn test_sb() {
assert sb.len == 2 assert sb.len == 2
assert sb.str() == 'ab' assert sb.str() == 'ab'
/// ///
sb = strings.new_builder(10) $if !windows {
sb.write('123456') // TODO msvc bug
assert sb.cut_last(2) == '56' sb = strings.new_builder(10)
assert sb.str() == '1234' sb.write('123456')
assert sb.cut_last(2) == '56'
assert sb.str() == '1234'
}
/// ///
/* /*
sb = strings.new_builder(10) sb = strings.new_builder(10)

View File

@ -9,11 +9,11 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | CharLiteral |
CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr |
OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
StringInterLiteral | StringLiteral | StructInit | Type | TypeOf StructInit | Type | TypeOf
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl | pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl |
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
@ -44,7 +44,6 @@ pub struct ExprStmt {
pub: pub:
expr Expr expr Expr
pos token.Position pos token.Position
// treat like expr (dont add trailing `;`)
// is used for `x++` in `for x:=1; ; x++` // is used for `x++` in `for x:=1; ; x++`
is_expr bool is_expr bool
pub mut: pub mut:
@ -806,8 +805,9 @@ pub:
is_count bool is_count bool
db_var_name string // `db` in `sql db {` db_var_name string // `db` in `sql db {`
table_name string table_name string
where_expr Expr where_expr Expr
has_where bool has_where bool
fields []table.Field
} }
[inline] [inline]

View File

@ -1307,8 +1307,9 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
if right_type_sym0.kind == .multi_return { if right_type_sym0.kind == .multi_return {
assign_stmt.right_types = right_type_sym0.mr_info().types assign_stmt.right_types = right_type_sym0.mr_info().types
right_len = assign_stmt.right_types.len right_len = assign_stmt.right_types.len
} else if right_type0 == table.void_type {
right_len = 0
} }
else if right_type0 == table.void_type { right_len=0 }
} }
if assign_stmt.left.len != right_len { if assign_stmt.left.len != right_len {
if right_first is ast.CallExpr { if right_first is ast.CallExpr {
@ -1338,7 +1339,9 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
if is_decl { if is_decl {
left_type = c.table.mktyp(right_type) left_type = c.table.mktyp(right_type)
// we are unwrapping here instead if check_expr_opt_call currently // we are unwrapping here instead if check_expr_opt_call currently
if left_type.has_flag(.optional) { left_type = left_type.clear_flag(.optional) } if left_type.has_flag(.optional) {
left_type = left_type.clear_flag(.optional)
}
} else { } else {
// Make sure the variable is mutable // Make sure the variable is mutable
c.fail_if_immutable(left) c.fail_if_immutable(left)
@ -1353,9 +1356,10 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
if assign_stmt.op !in [.assign, .decl_assign] { if assign_stmt.op !in [.assign, .decl_assign] {
c.error('cannot modify blank `_` identifier', it.pos) c.error('cannot modify blank `_` identifier', it.pos)
} }
} } else {
else { if is_decl {
if is_decl { c.check_valid_snake_case(it.name, 'variable name', it.pos) } c.check_valid_snake_case(it.name, 'variable name', it.pos)
}
mut scope := c.file.scope.innermost(assign_stmt.pos.pos) mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
mut ident_var_info := it.var_info() mut ident_var_info := it.var_info()
ident_var_info.typ = left_type ident_var_info.typ = left_type
@ -1366,12 +1370,12 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
ast.PrefixExpr { ast.PrefixExpr {
// Do now allow `*x = y` outside `unsafe` // Do now allow `*x = y` outside `unsafe`
if it.op == .mul && !c.inside_unsafe { if it.op == .mul && !c.inside_unsafe {
c.error('modifying variables via deferencing can only be done in `unsafe` blocks', assign_stmt.pos) c.error('modifying variables via deferencing can only be done in `unsafe` blocks',
assign_stmt.pos)
} }
} }
else {} else {}
} }
left_type_unwrapped := c.unwrap_generic(left_type) left_type_unwrapped := c.unwrap_generic(left_type)
right_type_unwrapped := c.unwrap_generic(right_type) right_type_unwrapped := c.unwrap_generic(right_type)
left_sym := c.table.get_type_symbol(left_type_unwrapped) left_sym := c.table.get_type_symbol(left_type_unwrapped)
@ -1381,9 +1385,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
.assign {} // No need to do single side check for =. But here put it first for speed. .assign {} // No need to do single side check for =. But here put it first for speed.
.plus_assign { .plus_assign {
if !left_sym.is_number() && left_type != table.string_type && !left_sym.is_pointer() { if !left_sym.is_number() && left_type != table.string_type && !left_sym.is_pointer() {
c.error('operator += not defined on left operand type `$left_sym.name`', left.position()) c.error('operator += not defined on left operand type `$left_sym.name`',
left.position())
} else if !right_sym.is_number() && right_type != table.string_type && !right_sym.is_pointer() { } else if !right_sym.is_number() && right_type != table.string_type && !right_sym.is_pointer() {
c.error('operator += not defined on right operand type `$right_sym.name`', right.position()) c.error('operator += not defined on right operand type `$right_sym.name`',
right.position())
} }
if right is ast.IntegerLiteral && right.str().int() == 1 { if right is ast.IntegerLiteral && right.str().int() == 1 {
c.error('use `++` instead of `+= 1`', assign_stmt.pos) c.error('use `++` instead of `+= 1`', assign_stmt.pos)
@ -1391,9 +1397,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
} }
.minus_assign { .minus_assign {
if !left_sym.is_number() && !left_sym.is_pointer() { if !left_sym.is_number() && !left_sym.is_pointer() {
c.error('operator -= not defined on left operand type `$left_sym.name`', left.position()) c.error('operator -= not defined on left operand type `$left_sym.name`',
left.position())
} else if !right_sym.is_number() && !right_sym.is_pointer() { } else if !right_sym.is_number() && !right_sym.is_pointer() {
c.error('operator -= not defined on right operand type `$right_sym.name`', right.position()) c.error('operator -= not defined on right operand type `$right_sym.name`',
right.position())
} }
if right is ast.IntegerLiteral && right.str().int() == 1 { if right is ast.IntegerLiteral && right.str().int() == 1 {
c.error('use `--` instead of `-= 1`', assign_stmt.pos) c.error('use `--` instead of `-= 1`', assign_stmt.pos)
@ -1973,10 +1981,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
return table.u32_type return table.u32_type
} }
ast.SqlExpr { ast.SqlExpr {
if it.has_where { return c.sql_expr(it)
c.expr(it.where_expr)
}
return it.typ
} }
ast.StringLiteral { ast.StringLiteral {
if it.language == .c { if it.language == .c {
@ -2564,6 +2569,13 @@ fn (c &Checker) fileis(s string) bool {
return c.file.path.contains(s) return c.file.path.contains(s)
} }
fn (mut c Checker) sql_expr(node ast.SqlExpr) table.Type {
if node.has_where {
c.expr(node.where_expr)
}
return node.typ
}
fn (mut c Checker) fn_decl(it ast.FnDecl) { 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 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 // loop thru each generic type and generate a function

View File

@ -5,6 +5,7 @@ module gen
import v.ast import v.ast
import strings import strings
import v.table
// pg,mysql etc // pg,mysql etc
const ( const (
@ -26,8 +27,17 @@ fn (mut g Gen) sql_expr(node ast.SqlExpr) {
cur_line := g.go_before_stmt(0) cur_line := g.go_before_stmt(0)
mut q := 'select ' mut q := 'select '
if node.is_count { if node.is_count {
// select count(*) from User // `select count(*) from User`
q += 'count(*) from $node.table_name' q += 'count(*) from $node.table_name'
} else {
// `select id, name, country from User`
for i, field in node.fields {
q += '$field.name'
if i < node.fields.len - 1 {
q += ', '
}
}
q += ' from $node.table_name'
} }
if node.has_where { if node.has_where {
q += ' where ' q += ' where '
@ -37,7 +47,7 @@ fn (mut g Gen) sql_expr(node ast.SqlExpr) {
db_name := g.new_tmp_var() db_name := g.new_tmp_var()
g.writeln('\n\t// sql') g.writeln('\n\t// sql')
// g.write('${dbtype}__DB $db_name = *(${dbtype}__DB*)${node.db_var_name}.data;') // g.write('${dbtype}__DB $db_name = *(${dbtype}__DB*)${node.db_var_name}.data;')
g.write('${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(*(${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') 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 { if node.has_where && node.where_expr is ast.InfixExpr {
@ -49,7 +59,27 @@ fn (mut g Gen) sql_expr(node ast.SqlExpr) {
g.sql_buf = strings.new_builder(100) g.sql_buf = strings.new_builder(100)
g.writeln(binds) g.writeln(binds)
g.writeln('puts(sqlite3_errmsg(${db_name}.conn));') g.writeln('puts(sqlite3_errmsg(${db_name}.conn));')
g.writeln('$cur_line ${dbtype}__get_int_from_stmt($g.sql_stmt_name);') //
if node.is_count {
g.writeln('$cur_line ${dbtype}__get_int_from_stmt($g.sql_stmt_name);')
} else {
// `user := sql db { select from User where id = 1 }`
tmp := g.new_tmp_var()
g.write(g.typ(node.typ))
g.writeln(' $tmp;')
g.writeln('sqlite3_step($g.sql_stmt_name);')
for i, field in node.fields {
mut func := 'sqlite3_column_int'
if field.typ == table.string_type {
func = 'sqlite3_column_text'
g.writeln('${tmp}.$field.name = tos_clone(${func}($g.sql_stmt_name, $i));')
} else {
g.writeln('${tmp}.$field.name = ${func}($g.sql_stmt_name, $i);')
}
}
g.writeln('sqlite3_finalize($g.sql_stmt_name);')
g.writeln('$cur_line $tmp; ') // `User user = tmp;`
}
} }
fn (mut g Gen) expr_to_sql(expr ast.Expr) { fn (mut g Gen) expr_to_sql(expr ast.Expr) {

View File

@ -28,6 +28,16 @@ fn (mut p Parser) sql_expr() ast.SqlExpr {
if has_where { if has_where {
p.next() p.next()
where_expr = p.expr(0) where_expr = p.expr(0)
// `id == x` means that a single object is returned
if !is_count && where_expr is ast.InfixExpr {
e := where_expr as ast.InfixExpr
if e.op == .eq && e.left is ast.Ident {
ident := e.left as ast.Ident
if ident.name == 'id' {
typ = table_type
}
}
}
} }
p.check(.rcbr) p.check(.rcbr)
// ///////// // /////////
@ -75,5 +85,6 @@ fn (mut p Parser) sql_expr() ast.SqlExpr {
table_name: table_name table_name: table_name
where_expr: where_expr where_expr: where_expr
has_where: has_where has_where: has_where
fields: fields
} }
} }