From 1e86e06eb6c1bc078e16d78c7247595126329bcd Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Tue, 4 Jun 2024 07:22:06 -0300 Subject: [PATCH] orm: fix subquery without where expr (#21598) --- cmd/tools/vtest-self.v | 2 + vlib/db/sqlite/parent_child_test.v | 83 ++++++++++++++++++++++++++ vlib/v/gen/c/orm.v | 93 +++++++++++++++--------------- 3 files changed, 133 insertions(+), 45 deletions(-) create mode 100644 vlib/db/sqlite/parent_child_test.v diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 820be2cdea6f52..af69652725c096 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -167,6 +167,7 @@ const skip_with_fsanitize_memory = [ 'vlib/orm/orm_option_time_test.v', 'vlib/db/sqlite/sqlite_test.v', 'vlib/db/sqlite/sqlite_orm_test.v', + 'vlib/db/sqlite/parent_child_test.v', 'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v', 'vlib/v/tests/orm_enum_test.v', 'vlib/v/tests/orm_sub_struct_test.v', @@ -238,6 +239,7 @@ const skip_on_ubuntu_musl = [ 'vlib/db/sqlite/sqlite_test.v', 'vlib/db/sqlite/sqlite_orm_test.v', 'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v', + 'vlib/db/sqlite/parent_child_test.v', 'vlib/orm/orm_test.v', 'vlib/orm/orm_sql_or_blocks_test.v', 'vlib/orm/orm_create_and_drop_test.v', diff --git a/vlib/db/sqlite/parent_child_test.v b/vlib/db/sqlite/parent_child_test.v new file mode 100644 index 00000000000000..8d9cc604ef9589 --- /dev/null +++ b/vlib/db/sqlite/parent_child_test.v @@ -0,0 +1,83 @@ +import db.sqlite + +struct Parent { + id int @[primary; sql: serial] + name string + children []Child @[fkey: 'parent_id'] +} + +struct Child { + id int @[primary; sql: serial] + name string + parent_id int + babies []Baby @[fkey: 'child_id'] +} + +struct Baby { + id int @[primary; sql: serial] + name string + child_id int +} + +fn test_main() { + db := sqlite.connect(':memory:')! + + sql db { + create table Parent + create table Child + create table Baby + } or { panic(err) } + + new_parent := Parent{ + name: 'first parent' + children: [ + Child{ + name: 'first child' + }, + Child{ + name: 'second_child' + }, + ] + } + + sql db { + insert new_parent into Parent + } or { panic(err) } + + babies := [ + Baby{ + name: 'first baby' + child_id: 1 + }, + Baby{ + name: 'second baby' + child_id: 1 + }, + Baby{ + name: 'third baby' + child_id: 2 + }, + Baby{ + name: 'fourth baby' + child_id: 2 + }, + Baby{ + name: 'fifth baby' + child_id: 2 + }, + ] + + for v in babies { + sql db { + insert v into Baby + } or { panic(err) } + } + + parent := sql db { + select from Parent + } or { panic(err) } + + assert parent[0].children[0].id == 1 + assert parent[0].children[1].id == 2 + assert parent[0].children.len == 2 +} diff --git a/vlib/v/gen/c/orm.v b/vlib/v/gen/c/orm.v index d769c917d02285..facc66975cf5fc 100644 --- a/vlib/v/gen/c/orm.v +++ b/vlib/v/gen/c/orm.v @@ -1068,53 +1068,56 @@ fn (mut g Gen) write_orm_select(node ast.SqlExpr, connection_var_name string, re verror('missing fkey attribute') } sub := node.sub_structs[field.typ] - mut where_expr := sub.where_expr as ast.InfixExpr - mut left_where_expr := where_expr.left as ast.Ident - mut right_where_expr := where_expr.right as ast.Ident - left_where_expr.name = fkey - right_where_expr.name = tmp - where_expr.left = left_where_expr - where_expr.right = ast.SelectorExpr{ - pos: right_where_expr.pos - field_name: primary_field.name - is_mut: false - expr: right_where_expr - expr_type: (right_where_expr.info as ast.IdentVar).typ - typ: (right_where_expr.info as ast.IdentVar).typ - scope: unsafe { nil } - } - mut sql_expr_select_array := ast.SqlExpr{ - typ: field.typ.set_flag(.result) - is_count: sub.is_count - db_expr: sub.db_expr - has_where: sub.has_where - has_offset: sub.has_offset - offset_expr: sub.offset_expr - has_order: sub.has_order - order_expr: sub.order_expr - has_desc: sub.has_desc - is_array: true - is_generated: true - pos: sub.pos - has_limit: sub.has_limit - limit_expr: sub.limit_expr - table_expr: sub.table_expr - fields: sub.fields - where_expr: where_expr - } + if sub.has_where { + mut where_expr := sub.where_expr as ast.InfixExpr + mut left_where_expr := where_expr.left as ast.Ident + mut right_where_expr := where_expr.right as ast.Ident + left_where_expr.name = fkey + right_where_expr.name = tmp + where_expr.left = left_where_expr + where_expr.right = ast.SelectorExpr{ + pos: right_where_expr.pos + field_name: primary_field.name + is_mut: false + expr: right_where_expr + expr_type: (right_where_expr.info as ast.IdentVar).typ + typ: (right_where_expr.info as ast.IdentVar).typ + scope: unsafe { nil } + } - sub_result_var := g.new_tmp_var() - sub_result_c_typ := g.typ(sub.typ) - g.writeln('${sub_result_c_typ} ${sub_result_var};') - g.write_orm_select(sql_expr_select_array, connection_var_name, sub_result_var) - g.writeln('if (!${sub_result_var}.is_error) {') - if field.typ.has_flag(.option) { - g.writeln('\t${field_var}.state = 0;') - g.writeln('\t*(${g.base_type(field.typ)}*)${field_var}.data = *(${g.base_type(field.typ)}*)${sub_result_var}.data;') - } else { - g.writeln('\t${field_var} = *(${unwrapped_c_typ}*)${sub_result_var}.data;') + mut sql_expr_select_array := ast.SqlExpr{ + typ: field.typ.set_flag(.result) + is_count: sub.is_count + db_expr: sub.db_expr + has_where: sub.has_where + has_offset: sub.has_offset + offset_expr: sub.offset_expr + has_order: sub.has_order + order_expr: sub.order_expr + has_desc: sub.has_desc + is_array: true + is_generated: true + pos: sub.pos + has_limit: sub.has_limit + limit_expr: sub.limit_expr + table_expr: sub.table_expr + fields: sub.fields + where_expr: where_expr + } + + sub_result_var := g.new_tmp_var() + sub_result_c_typ := g.typ(sub.typ) + g.writeln('${sub_result_c_typ} ${sub_result_var};') + g.write_orm_select(sql_expr_select_array, connection_var_name, sub_result_var) + g.writeln('if (!${sub_result_var}.is_error) {') + if field.typ.has_flag(.option) { + g.writeln('\t${field_var}.state = 0;') + g.writeln('\t*(${g.base_type(field.typ)}*)${field_var}.data = *(${g.base_type(field.typ)}*)${sub_result_var}.data;') + } else { + g.writeln('\t${field_var} = *(${unwrapped_c_typ}*)${sub_result_var}.data;') + } + g.writeln('}') } - g.writeln('}') } else if field.typ.has_flag(.option) { prim_var := g.new_tmp_var() g.writeln('orm__Primitive *${prim_var} = &${array_get_call_code};')