From 2fb9bdce9a3f9647ed0c91d9da70177866a99752 Mon Sep 17 00:00:00 2001 From: walking devel <104449470+walkingdevel@users.noreply.github.com> Date: Tue, 17 Jan 2023 18:21:10 +0000 Subject: [PATCH] cgen, checker: add panic in ORM for invalid queries, when there are no `or {}` blocks, add type checking for the `fkey` attribute, add tests (#16977) --- cmd/tools/vtest-self.v | 25 +++++- vlib/orm/orm_create_and_drop_test.v | 79 +++++++++++++++++ vlib/orm/orm_insert_test.v | 87 +++++++++++++++++++ vlib/orm/orm_test.v | 69 ++++++--------- vlib/v/checker/orm.v | 53 +++++++++++ vlib/v/gen/c/sql.v | 37 +++++--- vlib/v/slow_tests/inout/compiler_test.v | 9 ++ ...anic_for_insert_into_not_created_table.out | 5 ++ ...panic_for_insert_into_not_created_table.vv | 18 ++++ ...anic_for_select_from_not_created_table.out | 5 ++ ...panic_for_select_from_not_created_table.vv | 16 ++++ ...r_for_select_from_not_created_table_test.v | 17 ++++ vlib/v/tests/orm_sub_array_struct_test.v | 8 +- vlib/v/tests/orm_sub_struct_test.v | 3 + 14 files changed, 370 insertions(+), 61 deletions(-) create mode 100644 vlib/orm/orm_create_and_drop_test.v create mode 100644 vlib/orm/orm_insert_test.v create mode 100644 vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.out create mode 100644 vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.vv create mode 100644 vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.out create mode 100644 vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.vv create mode 100644 vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 53ad78cd72..f64f1d77e5 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -84,6 +84,7 @@ const ( 'vlib/x/json2/json2_test.v', ] skip_test_files = [ + 'do_not_remove', 'cmd/tools/vdoc/html_tag_escape_test.v', /* can't locate local module: markdown */ 'cmd/tools/vdoc/tests/vdoc_file_test.v', /* fails on Windows; order of output is not as expected */ 'vlib/context/onecontext/onecontext_test.v', @@ -91,9 +92,10 @@ const ( 'vlib/db/mysql/mysql_orm_test.v' /* mysql not installed */, 'vlib/db/pg/pg_orm_test.v' /* pg not installed */, ] + // These tests are too slow to be run in the CI on each PR/commit + // in the sanitized modes: skip_fsanitize_too_slow = [ - // These tests are too slow to be run in the CI on each PR/commit - // in the sanitized modes: + 'do_not_remove', 'vlib/v/compiler_errors_test.v', 'vlib/v/doc/doc_test.v', 'vlib/v/fmt/fmt_test.v', @@ -109,6 +111,7 @@ const ( 'vlib/v/slow_tests/valgrind/valgrind_test.v', ] skip_with_fsanitize_memory = [ + 'do_not_remove', 'vlib/net/tcp_simple_client_server_test.v', 'vlib/net/http/cookie_test.v', 'vlib/net/http/http_test.v', @@ -120,6 +123,8 @@ const ( 'vlib/net/tcp_test.v', 'vlib/orm/orm_test.v', 'vlib/orm/orm_sql_or_blocks_test.v', + 'vlib/orm/orm_create_and_drop_test.v', + 'vlib/orm/orm_insert_test.v', 'vlib/db/sqlite/sqlite_test.v', 'vlib/db/sqlite/sqlite_orm_test.v', 'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v', @@ -128,6 +133,7 @@ const ( 'vlib/v/tests/orm_joined_tables_select_test.v', 'vlib/v/tests/sql_statement_inside_fn_call_test.v', 'vlib/v/tests/orm_stmt_wrong_return_checking_test.v', + 'vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v', 'vlib/vweb/tests/vweb_test.v', 'vlib/vweb/csrf/csrf_test.v', 'vlib/vweb/request_test.v', @@ -141,13 +147,20 @@ const ( 'vlib/v/tests/fn_literal_type_test.v', ] skip_with_fsanitize_address = [ + 'do_not_remove', 'vlib/net/websocket/websocket_test.v', + 'vlib/orm/orm_create_and_drop_test.v', + 'vlib/orm/orm_insert_test.v', 'vlib/v/tests/websocket_logger_interface_should_compile_test.v', 'vlib/v/tests/orm_sub_array_struct_test.v', + 'vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v', ] skip_with_fsanitize_undefined = [ 'do_not_remove', + 'vlib/orm/orm_create_and_drop_test.v', + 'vlib/orm/orm_insert_test.v', 'vlib/v/tests/orm_sub_array_struct_test.v', + 'vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v', ] skip_with_werror = [ 'do_not_remove', @@ -160,11 +173,13 @@ const ( 'do_not_remove', ] skip_on_musl = [ + 'do_not_remove', 'vlib/v/slow_tests/profile/profile_test.v', 'vlib/gg/draw_fns_api_test.v', 'vlib/v/tests/skip_unused/gg_code.vv', ] skip_on_ubuntu_musl = [ + 'do_not_remove', //'vlib/v/gen/js/jsgen_test.v', 'vlib/net/http/cookie_test.v', 'vlib/net/http/http_test.v', @@ -175,10 +190,13 @@ const ( 'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v', 'vlib/orm/orm_test.v', 'vlib/orm/orm_sql_or_blocks_test.v', + 'vlib/orm/orm_create_and_drop_test.v', + 'vlib/orm/orm_insert_test.v', 'vlib/v/tests/orm_sub_struct_test.v', 'vlib/v/tests/orm_sub_array_struct_test.v', 'vlib/v/tests/orm_joined_tables_select_test.v', 'vlib/v/tests/orm_stmt_wrong_return_checking_test.v', + 'vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v', 'vlib/v/tests/sql_statement_inside_fn_call_test.v', 'vlib/clipboard/clipboard_test.v', 'vlib/vweb/tests/vweb_test.v', @@ -208,6 +226,7 @@ const ( 'vlib/v/tests/const_and_global_with_same_name_test.v', // error C2099: initializer is not a constant ] skip_on_windows = [ + 'do_not_remove', 'vlib/context/cancel_test.v', 'vlib/context/deadline_test.v', 'vlib/context/empty_test.v', @@ -215,6 +234,7 @@ const ( 'vlib/orm/orm_test.v', 'vlib/v/tests/orm_sub_struct_test.v', 'vlib/v/tests/orm_joined_tables_select_test.v', + 'vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v', 'vlib/net/websocket/ws_test.v', 'vlib/net/unix/unix_test.v', 'vlib/net/unix/use_net_and_net_unix_together_test.v', @@ -248,6 +268,7 @@ const ( 'do_not_remove', ] skip_on_non_amd64_or_arm64 = [ + 'do_not_remove', // closures aren't implemented yet: 'vlib/v/tests/closure_test.v', 'vlib/context/cancel_test.v', diff --git a/vlib/orm/orm_create_and_drop_test.v b/vlib/orm/orm_create_and_drop_test.v new file mode 100644 index 0000000000..a13d116b70 --- /dev/null +++ b/vlib/orm/orm_create_and_drop_test.v @@ -0,0 +1,79 @@ +import db.sqlite + +struct Parent { + id int [primary; sql: serial] + children []Child [fkey: 'parent_id'] + notes []Note [fkey: 'owner_id'] +} + +struct Child { +mut: + id int [primary; sql: serial] + parent_id int +} + +struct Note { +mut: + id int [primary; sql: serial] + owner_id int +} + +fn test_create_only_one_table() { + mut db := sqlite.connect(':memory:') or { panic(err) } + + sql db { + create table Parent + } + + mut is_child_created := true + mut is_note_created := true + + _ := sql db { + select count from Child + } or { is_child_created = false } + + _ := sql db { + select count from Note + } or { is_note_created = false } + + assert is_child_created == false + assert is_note_created == false +} + +fn test_drop_only_one_table() { + mut db := sqlite.connect(':memory:') or { panic(err) } + + sql db { + create table Parent + } + sql db { + create table Child + } + sql db { + create table Note + } + + mut is_parent_dropped := false + mut is_child_dropped := false + mut is_note_dropped := false + + sql db { + drop table Parent + } + + _ := sql db { + select count from Parent + } or { is_parent_dropped = true } + + _ := sql db { + select count from Child + } or { is_child_dropped = true } + + _ := sql db { + select count from Note + } or { is_note_dropped = true } + + assert is_parent_dropped + assert is_child_dropped == false + assert is_note_dropped == false +} diff --git a/vlib/orm/orm_insert_test.v b/vlib/orm/orm_insert_test.v new file mode 100644 index 0000000000..670b259430 --- /dev/null +++ b/vlib/orm/orm_insert_test.v @@ -0,0 +1,87 @@ +import db.sqlite + +struct Parent { + id int [primary; sql: serial] + name string + children []Child [fkey: 'parent_id'] + notes []Note [fkey: 'owner_id'] +} + +struct Child { +mut: + id int [primary; sql: serial] + parent_id int + name string +} + +struct Note { +mut: + id int [primary; sql: serial] + owner_id int + text string +} + +fn test_orm_insert_with_multiple_child_elements() { + mut db := sqlite.connect(':memory:') or { panic(err) } + + sql db { + create table Parent + } + sql db { + create table Child + } + sql db { + create table Note + } + + new_parent := Parent{ + name: 'test' + children: [ + Child{ + name: 'Lisa' + }, + Child{ + name: 'Steve' + }, + ] + notes: [ + Note{ + text: 'First note' + }, + Note{ + text: 'Second note' + }, + Note{ + text: 'Third note' + }, + ] + } + + sql db { + insert new_parent into Parent + } + + parent := sql db { + select from Parent where id == 1 + } + + assert parent.children.len == new_parent.children.len + assert parent.notes.len == new_parent.notes.len + + children_count := sql db { + select count from Child + } + assert children_count == new_parent.children.len + + note_count := sql db { + select count from Note + } + assert note_count == new_parent.notes.len + + assert parent.children[0].name == 'Lisa' + assert parent.children[1].name == 'Steve' + + assert parent.notes[0].text == 'First note' + assert parent.notes[1].text == 'Second note' + assert parent.notes[2].text == 'Third note' +} diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v index 63fa6590c9..034e821116 100644 --- a/vlib/orm/orm_test.v +++ b/vlib/orm/orm_test.v @@ -1,5 +1,3 @@ -// import os -// import term // import db.mysql // import db.pg import time @@ -34,21 +32,13 @@ struct TestTime { fn test_orm() { db := sqlite.connect(':memory:') or { panic(err) } - db.exec('drop table if exists User') - - // db := pg.connect(host: 'localhost', port: 5432, user: 'louis', password: 'abc', dbname: 'orm') or { panic(err) } - /* - mut db := mysql.Connection{ - host: '127.0.0.1' - username: 'root' - password: 'pw' - dbname: 'v' - } - db.connect() or { panic(err) }*/ sql db { create table Module } + sql db { + create table User + } name := 'Peter' @@ -84,38 +74,41 @@ fn test_orm() { } assert nr_all_users == 3 println('nr_all_users=${nr_all_users}') - // + nr_users1 := sql db { select count from User where id == 1 } assert nr_users1 == 1 println('nr_users1=${nr_users1}') - // + nr_peters := sql db { select count from User where id == 2 && name == 'Peter' } assert nr_peters == 1 println('nr_peters=${nr_peters}') - // + nr_peters2 := sql db { select count from User where id == 2 && name == name } assert nr_peters2 == 1 + nr_peters3 := sql db { select count from User where name == name } assert nr_peters3 == 1 + peters := sql db { select from User where name == name } assert peters.len == 1 assert peters[0].name == 'Peter' + one_peter := sql db { select from User where name == name limit 1 } assert one_peter.name == 'Peter' assert one_peter.id == 2 - // + user := sql db { select from User where id == 1 } @@ -123,7 +116,7 @@ fn test_orm() { assert user.name == 'Sam' assert user.id == 1 assert user.age == 29 - // + users := sql db { select from User where id > 0 } @@ -132,13 +125,13 @@ fn test_orm() { assert users[0].name == 'Sam' assert users[1].name == 'Peter' assert users[1].age == 31 - // + users2 := sql db { select from User where id < 0 } println(users2) assert users2.len == 0 - // + users3 := sql db { select from User where age == 29 || age == 31 } @@ -146,13 +139,13 @@ fn test_orm() { assert users3.len == 2 assert users3[0].age == 29 assert users3[1].age == 31 - // + missing_user := sql db { select from User where id == 8777 } println('missing_user:') println(missing_user) // zero struct - // + new_user := User{ name: 'New user' age: 30 @@ -161,7 +154,6 @@ fn test_orm() { insert new_user into User } - // db.insert(user2) x := sql db { select from User where id == 4 } @@ -169,18 +161,18 @@ fn test_orm() { assert x.age == 30 assert x.id == 4 assert x.name == 'New user' - // + kate := sql db { select from User where id == 3 } assert kate.is_customer == true - // + customer := sql db { select from User where is_customer == true limit 1 } assert customer.is_customer == true assert customer.name == 'Kate' - // + sql db { update User set age = 31 where name == 'Kate' } @@ -190,7 +182,7 @@ fn test_orm() { } assert kate2.age == 31 assert kate2.name == 'Kate' - // + sql db { update User set age = 32, name = 'Kate N' where name == 'Kate' } @@ -200,18 +192,7 @@ fn test_orm() { } assert kate3.age == 32 assert kate3.name == 'Kate N' - // - /* - sql db { - update User set age = age + 1, name = 'Kate N' where name == 'Kate' - } - kate3 = sql db { - select from User where id == 3 - } - println(kate3) - assert kate3.age == 32 - assert kate3.name == 'Kate N' - */ + new_age := 33 sql db { update User set age = new_age, name = 'Kate N' where id == 3 @@ -222,7 +203,7 @@ fn test_orm() { } assert kate3.age == 33 assert kate3.name == 'Kate N' - // + foo := Foo{34} sql db { update User set age = foo.age, name = 'Kate N' where id == 3 @@ -233,25 +214,25 @@ fn test_orm() { } assert kate3.age == 34 assert kate3.name == 'Kate N' - // + no_user := sql db { select from User where id == 30 } assert no_user.name == '' // TODO optional assert no_user.age == 0 - // + two_users := sql db { select from User limit 2 } assert two_users.len == 2 assert two_users[0].id == 1 - // + y := sql db { select from User limit 2 offset 1 } assert y.len == 2 assert y[0].id == 2 - // + offset_const := 2 z := sql db { select from User order by id limit 2 offset offset_const diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index a126d47deb..79cd57bc1b 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -5,6 +5,10 @@ module checker import v.ast import v.token +const ( + fkey_attr_name = 'fkey' +) + fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { c.inside_sql = true defer { @@ -156,6 +160,8 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { || (c.table.sym(it.typ).kind == .array && c.table.sym(c.table.sym(it.typ).array_info().elem_type).kind == .struct_)) && c.table.get_type_name(it.typ) != 'time.Time') { + c.check_orm_struct_field_attributes(f) + typ := if c.table.sym(f.typ).kind == .struct_ { f.typ } else if c.table.sym(f.typ).kind == .array { @@ -163,6 +169,7 @@ 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 @@ -204,6 +211,52 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { return ast.void_type } +fn (mut c Checker) check_orm_struct_field_attributes(field ast.StructField) { + field_type := c.table.sym(field.typ) + mut has_fkey_attr := false + + for attr in field.attrs { + if attr.name == checker.fkey_attr_name { + if field_type.kind != .array && field_type.kind != .struct_ { + c.error('The `${checker.fkey_attr_name}` attribute must be used only with arrays and structures', + attr.pos) + return + } + + if !attr.has_arg { + c.error('The `${checker.fkey_attr_name}` attribute must have an argument', + attr.pos) + return + } + + if attr.kind != .string { + c.error('`${checker.fkey_attr_name}` attribute must be string. Try [${checker.fkey_attr_name}: \'${attr.arg}\'] instead of [${checker.fkey_attr_name}: ${attr.arg}]', + attr.pos) + return + } + + field_struct_type := if field_type.info is ast.Array { + c.table.sym(field_type.info.elem_type) + } else { + field_type + } + + field_struct_type.find_field(attr.arg) or { + c.error('`${field_struct_type.name}` struct has no field with name `${attr.arg}`', + attr.pos) + return + } + + has_fkey_attr = true + } + } + + if field_type.kind == .array && !has_fkey_attr { + c.error('A field that holds an array must be defined with the `${checker.fkey_attr_name}` attribute', + field.pos) + } +} + fn (mut c Checker) fetch_and_verify_orm_fields(info ast.Struct, pos token.Pos, table_name string) []ast.StructField { fields := info.fields.filter( (it.typ in [ast.string_type, ast.bool_type] || int(it.typ) in ast.number_type_idxs diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v index e6565a2f67..647ece359d 100644 --- a/vlib/v/gen/c/sql.v +++ b/vlib/v/gen/c/sql.v @@ -3,6 +3,7 @@ module c import v.ast +import v.token import v.util enum SqlExprSide { @@ -29,7 +30,6 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string, or_expr ast.OrExpr table_name := g.get_table_name(node.table_expr) g.sql_table_name = g.table.sym(node.table_expr.typ).name res := g.new_tmp_var() - mut subs := false if node.kind != .create { mut fields := []ast.StructField{} @@ -54,15 +54,13 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string, or_expr ast.OrExpr if node.kind == .create { g.write('${result_name}_void ${res} = orm__Connection_name_table[${expr}._typ]._method_') g.sql_create_table(node, expr, table_name) - subs = true } else if node.kind == .drop { g.write('${result_name}_void ${res} = orm__Connection_name_table[${expr}._typ]._method_') g.writeln('drop(${expr}._object, _SLIT("${table_name}"));') - subs = true } else if node.kind == .insert { arr := g.new_tmp_var() g.writeln('Array_orm__Primitive ${arr} = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0);') - g.sql_insert(node, expr, table_name, arr, res, '', false, '', or_expr) + g.sql_insert(node, expr, table_name, arr, res, '', '', or_expr) } else if node.kind == .update { g.write('${result_name}_void ${res} = orm__Connection_name_table[${expr}._typ]._method_') g.sql_update(node, expr, table_name) @@ -70,13 +68,11 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string, or_expr ast.OrExpr g.write('${result_name}_void ${res} = orm__Connection_name_table[${expr}._typ]._method_') g.sql_delete(node, expr, table_name) } + if or_expr.kind == .block { g.or_block(res, or_expr, ast.int_type.set_flag(.result)) - } - if subs { - for _, sub in node.sub_structs { - g.sql_stmt_line(sub, expr, or_expr) - } + } else if or_expr.kind == .absent { + g.write_error_handling_for_orm_result(node.pos, res) } } @@ -121,7 +117,7 @@ fn (mut g Gen) sql_create_table(node ast.SqlStmtLine, expr string, table_name st g.writeln('));') } -fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, last_ids_arr string, res string, pid string, is_array bool, fkey string, or_expr ast.OrExpr) { +fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, last_ids_arr string, res string, pid string, fkey string, or_expr ast.OrExpr) { mut subs := []ast.SqlStmtLine{} mut arrs := []ast.SqlStmtLine{} mut fkeys := []string{} @@ -240,16 +236,13 @@ fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, arr.fields = fff.clone() unsafe { fff.free() } g.sql_insert(arr, expr, g.get_table_name(arr.table_expr), last_ids, res_, - id_name, true, fkeys[i], or_expr) + id_name, fkeys[i], or_expr) g.writeln('}') } } } fn (mut g Gen) sql_update(node ast.SqlStmtLine, expr string, table_name string) { - // println(table_name) - // println(expr) - // println(node) g.write('update(${expr}._object, _SLIT("${table_name}"), (orm__QueryData){') g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),') g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),') @@ -649,6 +642,8 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as g.or_block(tmp_left, node.or_expr, node.typ.set_flag(.result)) g.writeln('else {') g.indent++ + } else if node.or_expr.kind == .absent { + g.write_error_handling_for_orm_result(node.pos, '_o${res}') } g.writeln('Array_Array_orm__Primitive ${res} = (*(Array_Array_orm__Primitive*)_o${res}.data);') @@ -849,3 +844,17 @@ fn (mut g Gen) get_field_name(field ast.StructField) string { } return name } + +fn (mut g Gen) write_error_handling_for_orm_result(expr_pos &token.Pos, result_var_name string) { + g.writeln('if (${result_var_name}.is_error) {') + + if g.pref.is_debug { + g.write_v_source_line_info(expr_pos) + paline, pafile, pamod, pafn := g.panic_debug_info(expr_pos) + g.write('\tpanic_debug(${paline}, tos3("${pafile}"), tos3("${pamod}"), tos3("${pafn}"), IError_str(${result_var_name}.err) );') + } else { + g.writeln('\t_v_panic(IError_str(${result_var_name}.err));') + } + + g.writeln('}') +} diff --git a/vlib/v/slow_tests/inout/compiler_test.v b/vlib/v/slow_tests/inout/compiler_test.v index 31dc0b3f97..dac54a02ad 100644 --- a/vlib/v/slow_tests/inout/compiler_test.v +++ b/vlib/v/slow_tests/inout/compiler_test.v @@ -9,6 +9,8 @@ import v.util.vtest const turn_off_vcolors = os.setenv('VCOLORS', 'never', true) +const v_ci_ubuntu_musl = os.getenv('V_CI_UBUNTU_MUSL').len > 0 + const skip_files = [ 'do_not_remove_this', 'tmpl_parse_html.vv', // skipped, due to a V template compilation problem after b42c824 @@ -36,6 +38,13 @@ fn test_all() { println(term.bright_yellow('SKIP')) continue } + if v_ci_ubuntu_musl { + if fname.contains('orm_') { + // the ORM programs use db.sqlite, which is not easy to install in a way usable by ubuntu-musl, so just skip them: + println(term.bright_yellow('SKIP on ubuntu musl')) + continue + } + } program := path tname := rand.ulid() compilation := os.execute('${os.quoted_path(vexe)} -o ${tname} -cflags "-w" -cg ${os.quoted_path(program)}') diff --git a/vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.out b/vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.out new file mode 100644 index 0000000000..12362174ee --- /dev/null +++ b/vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.out @@ -0,0 +1,5 @@ +================ V panic ================ + module: main + function: main() + message: db.sqlite.SQLError: no such table: User (1) (INSERT INTO `User` (`id`, `name`) VALUES (?1, ?2);); code: 1 + file: vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.vv:16 diff --git a/vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.vv b/vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.vv new file mode 100644 index 0000000000..e4aa1c2c06 --- /dev/null +++ b/vlib/v/slow_tests/inout/orm_panic_for_insert_into_not_created_table.vv @@ -0,0 +1,18 @@ +import db.sqlite + +struct User { + id int + name string +} + +fn main() { + db := sqlite.connect(':memory:') or { panic(err) } + + user := User{ + name: 'test' + } + + sql db { + insert user into User + } +} diff --git a/vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.out b/vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.out new file mode 100644 index 0000000000..d26f191f06 --- /dev/null +++ b/vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.out @@ -0,0 +1,5 @@ +================ V panic ================ + module: main + function: main() + message: db.sqlite.SQLError: no such table: User (1) (SELECT `id`, `name` FROM `User`;); code: 1 + file: vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.vv:11 diff --git a/vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.vv b/vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.vv new file mode 100644 index 0000000000..23dddda0c0 --- /dev/null +++ b/vlib/v/slow_tests/inout/orm_panic_for_select_from_not_created_table.vv @@ -0,0 +1,16 @@ +import db.sqlite + +struct User { + id int + name string +} + +fn main() { + db := sqlite.connect(':memory:') or { panic(err) } + + users := sql db { + select from User + } + + println(users) +} diff --git a/vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v b/vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v new file mode 100644 index 0000000000..036f756a1a --- /dev/null +++ b/vlib/v/tests/orm_handle_error_for_select_from_not_created_table_test.v @@ -0,0 +1,17 @@ +import db.sqlite + +struct User { + id int + name string +} + +fn test_or_block_error_handling_of_an_invalid_query() { + db := sqlite.connect(':memory:') or { panic(err) } + + users := sql db { + select from User + } or { []User{} } + + println(users) + assert true +} diff --git a/vlib/v/tests/orm_sub_array_struct_test.v b/vlib/v/tests/orm_sub_array_struct_test.v index 999fde2593..365fd8af32 100644 --- a/vlib/v/tests/orm_sub_array_struct_test.v +++ b/vlib/v/tests/orm_sub_array_struct_test.v @@ -18,6 +18,9 @@ fn test_orm_array() { sql db { create table Parent } + sql db { + create table Child + } par := Parent{ name: 'test' @@ -54,6 +57,9 @@ fn test_orm_relationship() { sql db { create table Parent } + sql db { + create table Child + } mut child := Child{ name: 'abc' @@ -111,5 +117,5 @@ fn test_orm_relationship() { select from Child } - assert children.len == 0 + assert children.len == 2 } diff --git a/vlib/v/tests/orm_sub_struct_test.v b/vlib/v/tests/orm_sub_struct_test.v index d550b6c8b1..46909fac40 100644 --- a/vlib/v/tests/orm_sub_struct_test.v +++ b/vlib/v/tests/orm_sub_struct_test.v @@ -15,6 +15,9 @@ fn test_orm_sub_structs() { sql db { create table Upper } + sql db { + create table SubStruct + } upper_1 := Upper{ sub: SubStruct{