From bc904b84205117c87b9665d26e9d339bc436964d Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 15:41:48 +0300 Subject: [PATCH] checker: support passing generic types to orm select (fixes #24041) --- vlib/v/checker/orm.v | 41 ++++++++++--------- vlib/v/tests/orm_generic_struct_select_test.v | 30 ++++++++++++++ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index cb4d4a5e9..0d4c202bf 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -238,23 +238,20 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { return ast.void_type } + c.resolve_orm_table_expr_type(mut node.table_expr) + // To avoid panics while working with `table_expr`, // it is necessary to check if its type exists. if !c.ensure_type_exists(node.table_expr.typ, node.pos) { return ast.void_type } + // Keep the SQL expression type aligned with the concretized ORM table type. - if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 - && c.table.cur_concrete_types.len > 0 { - if c.needs_unwrap_generic_type(node.table_expr.typ) { - node.table_expr.typ = c.table.unwrap_generic_type(node.table_expr.typ, - c.table.cur_fn.generic_names, c.table.cur_concrete_types) - } - if c.needs_unwrap_generic_type(node.typ) { - node.typ = c.table.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, - c.table.cur_concrete_types) - } + resolved_node_typ := c.unwrap_generic(node.typ) + if resolved_node_typ != node.typ { + node.typ = resolved_node_typ } + table_type := node.table_expr.typ table_sym := c.table.sym(table_type) @@ -510,16 +507,13 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { c.inside_sql = false } + c.resolve_orm_table_expr_type(mut node.table_expr) + // To avoid panics while working with `table_expr`, // it is necessary to check if its type exists. if !c.ensure_type_exists(node.table_expr.typ, node.pos) { return ast.void_type } - if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 - && c.table.cur_concrete_types.len > 0 && c.needs_unwrap_generic_type(node.table_expr.typ) { - node.table_expr.typ = c.table.unwrap_generic_type(node.table_expr.typ, - c.table.cur_fn.generic_names, c.table.cur_concrete_types) - } table_type := node.table_expr.typ table_sym := c.table.sym(table_type) @@ -546,11 +540,9 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { inserting_object_type = inserting_object.typ.deref() } - if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 - && c.table.cur_concrete_types.len > 0 - && c.needs_unwrap_generic_type(inserting_object_type) { - inserting_object_type = c.table.unwrap_generic_type(inserting_object_type, - c.table.cur_fn.generic_names, c.table.cur_concrete_types) + resolved_object_type := c.unwrap_generic(inserting_object_type) + if resolved_object_type != inserting_object_type { + inserting_object_type = resolved_object_type } if inserting_object_type != node.table_expr.typ @@ -1219,6 +1211,13 @@ fn (mut c Checker) check_db_expr(mut db_expr ast.Expr) bool { return true } +fn (mut c Checker) resolve_orm_table_expr_type(mut type_node ast.TypeNode) { + resolved_typ := c.unwrap_generic(type_node.typ) + if resolved_typ != type_node.typ { + type_node.typ = resolved_typ + } +} + fn (mut c Checker) check_orm_table_expr_type(type_node &ast.TypeNode) bool { table_sym := c.table.sym(type_node.typ) @@ -1333,6 +1332,8 @@ fn (mut c Checker) get_non_array_type(typ_ ast.Type) (ast.Type, &ast.TypeSymbol) // It checks that the joined table type exists and is a struct, // and validates the ON expression. fn (mut c Checker) check_orm_join_clause(mut join ast.JoinClause, main_table_sym &ast.TypeSymbol) bool { + c.resolve_orm_table_expr_type(mut join.table_expr) + // Check that the joined table type exists if !c.ensure_type_exists(join.table_expr.typ, join.pos) { return false diff --git a/vlib/v/tests/orm_generic_struct_select_test.v b/vlib/v/tests/orm_generic_struct_select_test.v index 0cbd683d2..5339c6dda 100644 --- a/vlib/v/tests/orm_generic_struct_select_test.v +++ b/vlib/v/tests/orm_generic_struct_select_test.v @@ -59,6 +59,22 @@ fn run_generic_struct_orm_query[T](mut db sqlite.DB, value T) ![]DbValue[T] { return rows } +fn run_generic_table_orm_query[T](mut db sqlite.DB, value T) ![]T { + sql db { + create table T + }! + + sql db { + insert value into T + }! + + rows := sql db { + select from T + }! + + return rows +} + fn test_orm_generic_struct_select_in_generic_fn() { rows := run_generic_orm_query('test') or { panic(err) } assert rows.len == 1 @@ -83,3 +99,17 @@ fn test_orm_generic_struct_select_with_inferred_struct_type() { assert rows.len == 1 assert rows[0].data.name == 'test' } + +fn test_orm_generic_table_symbol_in_generic_fn() { + mut db := sqlite.connect(':memory:')! + defer { + db.close() or {} + } + + rows := run_generic_table_orm_query(mut db, User{ + name: 'generic' + }) or { panic(err) } + + assert rows.len == 1 + assert rows[0].name == 'generic' +} -- 2.39.5