diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 093be48531774a..244efdb00be17c 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -135,6 +135,7 @@ const ( 'vlib/orm/orm_result_test.v', 'vlib/orm/orm_custom_operators_test.v', 'vlib/orm/orm_fk_test.v', + 'vlib/orm/orm_references_test.v', 'vlib/db/sqlite/sqlite_test.v', 'vlib/db/sqlite/sqlite_orm_test.v', 'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v', @@ -162,6 +163,7 @@ const ( 'vlib/orm/orm_create_and_drop_test.v', 'vlib/orm/orm_insert_test.v', 'vlib/orm/orm_insert_reserved_name_test.v', + 'vlib/orm/orm_references_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', @@ -171,6 +173,7 @@ const ( 'vlib/orm/orm_create_and_drop_test.v', 'vlib/orm/orm_insert_test.v', 'vlib/orm/orm_insert_reserved_name_test.v', + 'vlib/orm/orm_references_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', 'vlib/v/tests/project_with_cpp_code/compiling_cpp_files_with_a_cplusplus_compiler_test.v', // fails compilation with: undefined reference to vtable for __cxxabiv1::__function_type_info' @@ -215,6 +218,7 @@ const ( 'vlib/orm/orm_result_test.v', 'vlib/orm/orm_custom_operators_test.v', 'vlib/orm/orm_fk_test.v', + 'vlib/orm/orm_references_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', diff --git a/vlib/orm/orm.v b/vlib/orm/orm.v index 943a0d45ec3f76..cb55344f5e3ae8 100644 --- a/vlib/orm/orm.v +++ b/vlib/orm/orm.v @@ -463,6 +463,8 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, mut is_unique := false mut is_skip := false mut unique_len := 0 + mut references_table := '' + mut references_field := '' mut field_name := sql_field_name(field) mut ctyp := sql_from_v(sql_field_type(field)) or { field_name = '${field_name}_id' @@ -512,6 +514,31 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, default_val = attr.arg } } + 'references' { + if attr.arg == '' { + if field.name.ends_with('_id') { + references_table = field.name.trim_right('_id') + references_field = 'id' + } else { + return error("references attribute can only be implicit if the field name ends with '_id'") + } + } else { + if attr.arg.trim(' ') == '' { + return error("references attribute needs to be in the format [references], [references: 'tablename'], or [references: 'tablename(field_id)']") + } + if attr.arg.contains('(') { + ref_table, ref_field := attr.arg.split_once('(') + if !ref_field.ends_with(')') { + return error("explicit references attribute should be written as [references: 'tablename(field_id)']") + } + references_table = ref_table + references_field = ref_field[..ref_field.len - 1] + } else { + references_table = attr.arg + references_field = 'id' + } + } + } else {} } } @@ -541,6 +568,9 @@ pub fn orm_table_gen(table string, q string, defaults bool, def_unique_len int, f += ')' unique_fields << f } + if references_table != '' { + stmt += ' REFERENCES ${references_table} (${references_field})' + } fs << stmt } diff --git a/vlib/orm/orm_references_test.v b/vlib/orm/orm_references_test.v new file mode 100644 index 00000000000000..2e64769e23d44f --- /dev/null +++ b/vlib/orm/orm_references_test.v @@ -0,0 +1,33 @@ +import db.sqlite + +struct Boat { + id int [primary; sql: serial] + color_id int [references] + another1_id int [references: 'size'] + another2_id int [references: 'size(secondary_id)'] +} + +struct Color { + id int [primary; sql: serial] + hex string +} + +struct Size { + id int [primary; sql: serial] + secondary_id int +} + +fn test_references_constraint() { + db := sqlite.connect(':memory:') or { panic(err) } + + sql db { + create table Boat + create table Color + create table Size + } or { panic(err) } + + // this pragma returns a row for each foreign key constraint on a table + pragma_result := db.exec('pragma foreign_key_list(boat)') or { panic('nope') } + + assert pragma_result.len == 3 +}