mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker, orm: don't insert an uninitialized struct in the related table. (#18093)
This commit is contained in:
parent
b255fef686
commit
ca2820da5f
@ -25,14 +25,75 @@ struct Account {
|
|||||||
id int [primary; sql: serial]
|
id int [primary; sql: serial]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Package {
|
||||||
|
id int [primary; sql: serial]
|
||||||
|
name string [unique]
|
||||||
|
author User [fkey: 'id']
|
||||||
|
}
|
||||||
|
|
||||||
|
struct User {
|
||||||
|
pub mut:
|
||||||
|
id int [primary; sql: serial]
|
||||||
|
username string [unique]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_parent(db sqlite.DB, mut parent Parent) ! {
|
pub fn insert_parent(db sqlite.DB, mut parent Parent) ! {
|
||||||
sql db {
|
sql db {
|
||||||
insert parent into Parent
|
insert parent into Parent
|
||||||
}!
|
}!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_does_not_insert_uninitialized_field() {
|
||||||
|
db := sqlite.connect(':memory:')!
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
create table User
|
||||||
|
create table Package
|
||||||
|
}!
|
||||||
|
|
||||||
|
package := Package{
|
||||||
|
name: 'xml'
|
||||||
|
// author
|
||||||
|
}
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
insert package into Package
|
||||||
|
}!
|
||||||
|
|
||||||
|
users := sql db {
|
||||||
|
select from User
|
||||||
|
}!
|
||||||
|
|
||||||
|
// users must be empty because the package doesn't have an initialized `User` structure.
|
||||||
|
assert users.len == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_insert_empty_field() {
|
||||||
|
db := sqlite.connect(':memory:')!
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
create table User
|
||||||
|
create table Package
|
||||||
|
}!
|
||||||
|
|
||||||
|
package := Package{
|
||||||
|
name: 'xml'
|
||||||
|
author: User{}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql db {
|
||||||
|
insert package into Package
|
||||||
|
}!
|
||||||
|
|
||||||
|
users := sql db {
|
||||||
|
select from User
|
||||||
|
}!
|
||||||
|
|
||||||
|
assert users.len == 1
|
||||||
|
}
|
||||||
|
|
||||||
fn test_insert_empty_object() {
|
fn test_insert_empty_object() {
|
||||||
db := sqlite.connect(':memory:') or { panic(err) }
|
db := sqlite.connect(':memory:')!
|
||||||
|
|
||||||
account := Account{}
|
account := Account{}
|
||||||
|
|
||||||
@ -49,7 +110,7 @@ fn test_insert_empty_object() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_orm_insert_mut_object() {
|
fn test_orm_insert_mut_object() {
|
||||||
db := sqlite.connect(':memory:') or { panic(err) }
|
db := sqlite.connect(':memory:')!
|
||||||
|
|
||||||
sql db {
|
sql db {
|
||||||
create table Parent
|
create table Parent
|
||||||
@ -71,7 +132,7 @@ fn test_orm_insert_mut_object() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn test_orm_insert_with_multiple_child_elements() {
|
fn test_orm_insert_with_multiple_child_elements() {
|
||||||
mut db := sqlite.connect(':memory:') or { panic(err) }
|
mut db := sqlite.connect(':memory:')!
|
||||||
|
|
||||||
sql db {
|
sql db {
|
||||||
create table Parent
|
create table Parent
|
||||||
|
@ -182,9 +182,9 @@ 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
|
||||||
}
|
}
|
||||||
|
inserting_object_name := node.object_var_name
|
||||||
|
|
||||||
if node.kind == .insert && !node.is_generated {
|
if node.kind == .insert && !node.is_generated {
|
||||||
inserting_object_name := node.object_var_name
|
|
||||||
inserting_object := node.scope.find(inserting_object_name) or {
|
inserting_object := node.scope.find(inserting_object_name) or {
|
||||||
c.error('undefined ident: `${inserting_object_name}`', node.pos)
|
c.error('undefined ident: `${inserting_object_name}`', node.pos)
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
@ -210,12 +210,19 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info := table_sym.info as ast.Struct
|
info := table_sym.info as ast.Struct
|
||||||
fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
mut fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name)
|
||||||
mut sub_structs := map[int]ast.SqlStmtLine{}
|
mut sub_structs := map[int]ast.SqlStmtLine{}
|
||||||
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_))
|
||||||
&& c.table.get_type_name(it.typ) != 'time.Time') {
|
&& c.table.get_type_name(it.typ) != 'time.Time') {
|
||||||
|
// Delete an uninitialized struct from fields and skip adding the current field
|
||||||
|
// to sub structs to skip inserting an empty struct in the related table.
|
||||||
|
if c.check_field_of_inserting_struct_is_uninitialized(node, f.name) {
|
||||||
|
fields.delete(fields.index(f))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
c.check_orm_struct_field_attributes(f)
|
c.check_orm_struct_field_attributes(f)
|
||||||
|
|
||||||
typ := if c.table.sym(f.typ).kind == .struct_ {
|
typ := if c.table.sym(f.typ).kind == .struct_ {
|
||||||
@ -529,3 +536,13 @@ fn (mut c Checker) check_db_expr(db_expr &ast.Expr) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (_ &Checker) check_field_of_inserting_struct_is_uninitialized(node &ast.SqlStmtLine, field_name string) bool {
|
||||||
|
struct_scope := node.scope.find_var(node.object_var_name) or { return false }
|
||||||
|
|
||||||
|
if struct_scope.expr is ast.StructInit {
|
||||||
|
return struct_scope.expr.fields.filter(it.name == field_name).len == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user