From b8e070b2a170a7341c34c8c8324f21dfbf6b3ece Mon Sep 17 00:00:00 2001 From: Louis Schmieder Date: Tue, 4 May 2021 11:35:18 +0200 Subject: [PATCH] orm: fix multi substructs (#9941) --- cmd/tools/vtest-self.v | 2 + examples/database/orm.v | 12 +++--- vlib/v/checker/checker.v | 6 ++- vlib/v/gen/c/sql.v | 34 +++++++++++----- vlib/v/tests/orm_sub_array_struct_test.v | 50 ++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 16 deletions(-) create mode 100644 vlib/v/tests/orm_sub_array_struct_test.v diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 13ed975a1d..8fed9e885f 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -20,6 +20,7 @@ const ( 'vlib/orm/orm_test.v', 'vlib/sqlite/sqlite_test.v', 'vlib/v/tests/orm_sub_struct_test.v', + 'vlib/v/tests/orm_sub_array_struct_test.v', 'vlib/vweb/tests/vweb_test.v', 'vlib/vweb/request_test.v', 'vlib/vweb/route_test.v', @@ -56,6 +57,7 @@ const ( 'vlib/sqlite/sqlite_test.v', 'vlib/orm/orm_test.v', 'vlib/v/tests/orm_sub_struct_test.v', + 'vlib/v/tests/orm_sub_array_struct_test.v', 'vlib/clipboard/clipboard_test.v', 'vlib/vweb/tests/vweb_test.v', 'vlib/vweb/request_test.v', diff --git a/examples/database/orm.v b/examples/database/orm.v index 734137560a..ae55f84828 100644 --- a/examples/database/orm.v +++ b/examples/database/orm.v @@ -19,9 +19,9 @@ struct User { } struct Parent { - id int [primary; sql: serial] - name string - chields []Chield [fkey: 'parent_id'] + id int [primary; sql: serial] + name string + children []Chield [fkey: 'parent_id'] } struct Chield { @@ -48,7 +48,7 @@ fn sqlite3_array() { par := Parent{ name: 'test' - chields: [ + children: [ Chield{ name: 'abc' }, @@ -90,7 +90,7 @@ fn mysql_array() { par := Parent{ name: 'test' - chields: [ + children: [ Chield{ name: 'abc' }, @@ -129,7 +129,7 @@ fn psql_array() { par := Parent{ name: 'test' - chields: [ + children: [ Chield{ name: 'abc' }, diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 8540122b59..10c98ee3da 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -6708,6 +6708,10 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { } else { ast.Type(0) } + mut object_var_name := '${node.object_var_name}.$f.name' + if typ != f.typ { + object_var_name = node.object_var_name + } mut n := ast.SqlStmtLine{ pos: node.pos kind: node.kind @@ -6715,7 +6719,7 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { pos: node.table_expr.pos typ: typ } - object_var_name: '${node.object_var_name}.$f.name' + object_var_name: object_var_name } tmp_inside_sql := c.inside_sql c.sql_stmt_line(mut n) diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v index 7b1704e6a4..5e383b566a 100644 --- a/vlib/v/gen/c/sql.v +++ b/vlib/v/gen/c/sql.v @@ -159,6 +159,7 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) g.writeln(');') mut arr_stmt := []ast.SqlStmtLine{} mut arr_fkeys := []string{} + mut arr_field_name := []string{} if node.kind == .insert { // build the object now (`x.name = ... x.id == ...`) for i, field in node.fields { @@ -203,6 +204,7 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) arr_stmt << node.sub_structs[int(t)] arr_fkeys << fkey + arr_field_name << field.name } } else { g.writeln('sqlite3_bind_int($g.sql_stmt_name, ${i + 0} , $x); // stmt') @@ -224,7 +226,7 @@ fn (mut g Gen) sqlite3_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) id_name := g.new_tmp_var() g.writeln('int $id_name = string_int((*(string*)array_get((*(sqlite__Row*)array_get($res, 0)).vals, 0)));') - g.sql_arr_stmt(arr_stmt, arr_fkeys, id_name, db_expr) + g.sql_arr_stmt(arr_stmt, arr_fkeys, arr_field_name, id_name, db_expr) } } @@ -449,6 +451,7 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) { g.writeln('memset($bind, 0, sizeof(MYSQL_BIND)*$g.sql_i);') mut arr_stmt := []ast.SqlStmtLine{} mut arr_fkeys := []string{} + mut arr_field_name := []string{} if node.kind == .insert { for i, field in node.fields { if g.get_sql_field_type(field) == ast.Type(-1) { @@ -510,6 +513,7 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) { arr_stmt << node.sub_structs[int(t)] arr_fkeys << fkey + arr_field_name << field.name } } else { t, sym := g.mysql_buffer_typ_from_field(field) @@ -549,7 +553,7 @@ fn (mut g Gen) mysql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) { g.writeln('int $id_name = string_int(tos_clone(${rs}_row[0]));') g.writeln('mysql_free_result($rs);') - g.sql_arr_stmt(arr_stmt, arr_fkeys, id_name, db_expr) + g.sql_arr_stmt(arr_stmt, arr_fkeys, arr_field_name, id_name, db_expr) } } @@ -826,6 +830,7 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) { mut arr_stmt := []ast.SqlStmtLine{} mut arr_fkeys := []string{} + mut arr_field_name := []string{} if node.kind == .insert { for i, field in node.fields { if g.get_sql_field_type(field) == ast.Type(-1) { @@ -874,6 +879,7 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) { arr_stmt << node.sub_structs[int(t)] arr_fkeys << fkey + arr_field_name << field.name } } else { g.sql_buf = strings.new_builder(100) @@ -894,8 +900,7 @@ fn (mut g Gen) psql_stmt(node ast.SqlStmtLine, typ SqlType, db_expr ast.Expr) { g.writeln('if (${res}.state != 0) { IError err = ${res}.err; eprintln(_STR("\\000%.*s", 2, IError_str(err))); }') id_name := g.new_tmp_var() g.writeln('int $id_name = string_int((*(string*)array_get((*(pg__Row*)${res}.data).vals, 0)));') - - g.sql_arr_stmt(arr_stmt, arr_fkeys, id_name, db_expr) + g.sql_arr_stmt(arr_stmt, arr_fkeys, arr_field_name, id_name, db_expr) } } @@ -1162,23 +1167,34 @@ fn (mut g Gen) sql_select_arr(field ast.StructField, node ast.SqlExpr, primary s } } -fn (mut g Gen) sql_arr_stmt(arr_stmt []ast.SqlStmtLine, arr_fkeys []string, id_name string, db_expr ast.Expr) { +fn (mut g Gen) sql_arr_stmt(arr_stmt []ast.SqlStmtLine, arr_fkeys []string, arr_field_name []string, id_name string, db_expr ast.Expr) { for i, s in arr_stmt { cnt := g.new_tmp_var() - g.writeln('for (int $cnt = 0; $cnt < ${s.object_var_name}.len; $cnt++) {') + g.writeln('for (int $cnt = 0; $cnt < ${s.object_var_name}.${arr_field_name[i]}.len; $cnt++) {') name := g.table.get_type_symbol(s.table_expr.typ).cname tmp_var := g.new_tmp_var() - g.writeln('\t$name $tmp_var = (*($name*)array_get($s.object_var_name, $cnt));') + g.writeln('\t$name $tmp_var = (*($name*)array_get(${s.object_var_name}.${arr_field_name[i]}, $cnt));') + mut sub_structs := map[int]ast.SqlStmtLine{} + + for key, sub in s.sub_structs { + sub_structs[key] = ast.SqlStmtLine{ + pos: sub.pos + kind: sub.kind + table_expr: sub.table_expr + object_var_name: tmp_var + fields: sub.fields + sub_structs: sub.sub_structs + } + } stmt := ast.SqlStmtLine{ pos: s.pos kind: s.kind table_expr: s.table_expr object_var_name: tmp_var fields: s.fields - sub_structs: s.sub_structs + sub_structs: sub_structs } - tmp_fkey := g.sql_fkey tmp_parent_id := g.sql_parent_id g.sql_fkey = arr_fkeys[i] diff --git a/vlib/v/tests/orm_sub_array_struct_test.v b/vlib/v/tests/orm_sub_array_struct_test.v new file mode 100644 index 0000000000..8442ba5269 --- /dev/null +++ b/vlib/v/tests/orm_sub_array_struct_test.v @@ -0,0 +1,50 @@ +import sqlite + +struct Parent { + id int [primary; sql: serial] + name string + chields []Chield [fkey: 'parent_id'] +} + +struct Chield { + id int [primary; sql: serial] + parent_id int + name string +} + +fn test_orm_array() { + mut db := sqlite.connect(':memory:') or { panic(err) } + sql db { + create table Parent + } + + par := Parent{ + name: 'test' + chields: [ + Chield{ + name: 'abc' + }, + Chield{ + name: 'def' + }, + ] + } + + sql db { + insert par into Parent + } + + parent := sql db { + select from Parent where id == 1 + } + + sql db { + drop table Chield + drop table Parent + } + + assert parent.name == par.name + assert parent.chields.len == par.chields.len + assert parent.chields[0].name == 'abc' + assert parent.chields[1].name == 'def' +}