1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

orm: detect type mismatching on inserting an object (#17157)

This commit is contained in:
walking devel 2023-01-30 09:26:10 +00:00 committed by GitHub
parent bb512f782e
commit a489417484
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 80 additions and 0 deletions

View File

@ -1751,6 +1751,10 @@ pub:
pos token.Pos pos token.Pos
where_expr Expr where_expr Expr
update_exprs []Expr // for `update` update_exprs []Expr // for `update`
// is_top_level indicates that a statement is parsed from code
// and is not inserted by ORM for inserting in related tables.
is_top_level bool
scope &Scope = unsafe { nil }
pub mut: pub mut:
object_var_name string // `user` object_var_name string // `user`
updated_columns []string // for `update set x=y` updated_columns []string // for `update set x=y`

View File

@ -28,6 +28,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
info := sym.info as ast.Struct info := sym.info as ast.Struct
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name) mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name)
mut sub_structs := map[int]ast.SqlExpr{} mut sub_structs := map[int]ast.SqlExpr{}
for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_ for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_
|| (c.table.sym(it.typ).kind == .array || (c.table.sym(it.typ).kind == .array
&& c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_)) && c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_))
@ -52,6 +53,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
tmp_inside_sql := c.inside_sql tmp_inside_sql := c.inside_sql
c.sql_expr(mut n) c.sql_expr(mut n)
c.inside_sql = tmp_inside_sql c.inside_sql = tmp_inside_sql
n.where_expr = ast.InfixExpr{ n.where_expr = ast.InfixExpr{
op: .eq op: .eq
pos: n.pos pos: n.pos
@ -85,6 +87,7 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
sub_structs[int(typ)] = n sub_structs[int(typ)] = n
} }
if node.is_count { if node.is_count {
fields = [ fields = [
ast.StructField{ ast.StructField{
@ -92,8 +95,10 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type {
}, },
] ]
} }
node.fields = fields node.fields = fields
node.sub_structs = sub_structs.move() node.sub_structs = sub_structs.move()
if node.has_where { if node.has_where {
c.expr(node.where_expr) c.expr(node.where_expr)
c.check_expr_has_no_fn_calls_with_non_orm_return_type(&node.where_expr) c.check_expr_has_no_fn_calls_with_non_orm_return_type(&node.where_expr)
@ -150,6 +155,23 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
defer { defer {
c.cur_orm_ts = old_ts c.cur_orm_ts = old_ts
} }
if node.kind == .insert && node.is_top_level {
inserting_object_name := node.object_var_name
inserting_object_var := node.scope.find(inserting_object_name) or {
c.error('undefined ident: `${inserting_object_name}`', node.pos)
return ast.void_type
}
if inserting_object_var.typ != node.table_expr.typ {
table_name := table_sym.name
inserting_type_name := c.table.sym(inserting_object_var.typ).name
c.error('cannot use `${inserting_type_name}` as `${table_name}`', node.pos)
return ast.void_type
}
}
if table_sym.info !is ast.Struct { if table_sym.info !is ast.Struct {
c.error('unknown type `${table_sym.name}`', node.pos) c.error('unknown type `${table_sym.name}`', node.pos)
return ast.void_type return ast.void_type

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.vv:19:10: error: cannot use `Tiddler` as `Tiddlers`
17 | sql db {
18 | create table Tiddlers
19 | insert tiddler into Tiddlers
| ~~~~~~~
20 | }
21 | }

View File

@ -0,0 +1,21 @@
import db.sqlite
import json
struct Tiddler {
id int
created int
}
struct Tiddlers {
id int [primary; sql: serial]
created string
}
fn main() {
tiddler := json.decode(Tiddler, '{}') or { Tiddler{} }
db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Tiddlers
insert tiddler into Tiddlers
}
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.vv:13:10: error: undefined ident: `bug`
11 | sql db {
12 | create table Node
13 | insert bug into Node
| ~~~
14 | }
15 | }

View File

@ -0,0 +1,15 @@
import db.sqlite
struct Node {
id int [primary; sql: serial]
text string
}
fn main() {
db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table Node
insert bug into Node
}
}

View File

@ -203,6 +203,7 @@ fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine {
typ: typ typ: typ
pos: typ_pos pos: typ_pos
} }
scope: p.scope
} }
} else if n == 'drop' { } else if n == 'drop' {
kind = .drop kind = .drop
@ -220,6 +221,7 @@ fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine {
typ: typ typ: typ
pos: typ_pos pos: typ_pos
} }
scope: p.scope
} }
} }
mut inserted_var_name := '' mut inserted_var_name := ''
@ -289,6 +291,8 @@ fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine {
update_exprs: update_exprs update_exprs: update_exprs
kind: kind kind: kind
where_expr: where_expr where_expr: where_expr
is_top_level: true
scope: p.scope
} }
} }