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 sqlite
struct Modules {
struct Module {
id int
user_id int
name string
url string
//name string
//url string
//nr_downloads int
}
struct User {
id int
age int
name string
}
fn test_orm_sqlite() {
db := sqlite.connect(':memory:') or { panic(err) }
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'
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 ('Kate')")
nr_all_users := sql db {
@ -48,6 +49,14 @@ fn test_orm_sqlite() {
select count from User where id == 1 && name == name
}
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.str() == 'ab'
///
$if !windows {
// TODO msvc bug
sb = strings.new_builder(10)
sb.write('123456')
assert sb.cut_last(2) == '56'
assert sb.str() == '1234'
}
///
/*
sb = strings.new_builder(10)

View File

@ -9,11 +9,11 @@ import v.errors
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr |
CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None |
OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr |
StringInterLiteral | StringLiteral | StructInit | Type | TypeOf
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | CharLiteral |
ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr |
InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr |
PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
StructInit | Type | TypeOf
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl |
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
@ -44,7 +44,6 @@ pub struct ExprStmt {
pub:
expr Expr
pos token.Position
// treat like expr (dont add trailing `;`)
// is used for `x++` in `for x:=1; ; x++`
is_expr bool
pub mut:
@ -808,6 +807,7 @@ pub:
table_name string
where_expr Expr
has_where bool
fields []table.Field
}
[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 {
assign_stmt.right_types = right_type_sym0.mr_info().types
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 right_first is ast.CallExpr {
@ -1338,7 +1339,9 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
if is_decl {
left_type = c.table.mktyp(right_type)
// 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 {
// Make sure the variable is mutable
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] {
c.error('cannot modify blank `_` identifier', it.pos)
}
} else {
if is_decl {
c.check_valid_snake_case(it.name, 'variable name', it.pos)
}
else {
if is_decl { c.check_valid_snake_case(it.name, 'variable name', it.pos) }
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
mut ident_var_info := it.var_info()
ident_var_info.typ = left_type
@ -1366,12 +1370,12 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
ast.PrefixExpr {
// Do now allow `*x = y` outside `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 {}
}
left_type_unwrapped := c.unwrap_generic(left_type)
right_type_unwrapped := c.unwrap_generic(right_type)
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.
.plus_assign {
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() {
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 {
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 {
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() {
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 {
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
}
ast.SqlExpr {
if it.has_where {
c.expr(it.where_expr)
}
return it.typ
return c.sql_expr(it)
}
ast.StringLiteral {
if it.language == .c {
@ -2564,6 +2569,13 @@ fn (c &Checker) fileis(s string) bool {
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) {
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

View File

@ -5,6 +5,7 @@ module gen
import v.ast
import strings
import v.table
// pg,mysql etc
const (
@ -26,8 +27,17 @@ fn (mut g Gen) sql_expr(node ast.SqlExpr) {
cur_line := g.go_before_stmt(0)
mut q := 'select '
if node.is_count {
// select count(*) from User
// `select count(*) from User`
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 {
q += ' where '
@ -37,7 +47,7 @@ fn (mut g Gen) sql_expr(node ast.SqlExpr) {
db_name := g.new_tmp_var()
g.writeln('\n\t// sql')
// 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($db_name, tos_lit("$q')
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.writeln(binds)
g.writeln('puts(sqlite3_errmsg(${db_name}.conn));')
//
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) {

View File

@ -28,6 +28,16 @@ fn (mut p Parser) sql_expr() ast.SqlExpr {
if has_where {
p.next()
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)
// /////////
@ -75,5 +85,6 @@ fn (mut p Parser) sql_expr() ast.SqlExpr {
table_name: table_name
where_expr: where_expr
has_where: has_where
fields: fields
}
}