diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index c4275063b6..db75b07790 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -130,6 +130,7 @@ const ( 'vlib/orm/orm_string_interpolation_in_where_test.v', 'vlib/orm/orm_interface_test.v', 'vlib/orm/orm_mut_db_test.v', + 'vlib/orm/orm_result_test.v', 'vlib/db/sqlite/sqlite_test.v', 'vlib/db/sqlite/sqlite_orm_test.v', 'vlib/db/sqlite/sqlite_vfs_lowlevel_test.v', @@ -205,6 +206,7 @@ const ( 'vlib/orm/orm_string_interpolation_in_where_test.v', 'vlib/orm/orm_interface_test.v', 'vlib/orm/orm_mut_db_test.v', + 'vlib/orm/orm_result_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/doc/docs.md b/doc/docs.md index e5329878ba..174e70deb6 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -4850,18 +4850,18 @@ db := sqlite.connect('customers.db')! // ) sql db { create table Customer -} +}! // select count(*) from customers nr_customers := sql db { select count from Customer -} +}! println('number of all customers: ${nr_customers}') // V syntax can be used to build queries uk_customers := sql db { select from Customer where country == 'uk' && nr_orders > 0 -} +}! println(uk_customers.len) for customer in uk_customers { println('${customer.id} - ${customer.name}') @@ -4874,7 +4874,7 @@ new_customer := Customer{ } sql db { insert new_customer into Customer -} +}! ``` For more examples and the docs, see [vlib/orm](https://github.com/vlang/v/tree/master/vlib/orm). diff --git a/examples/database/orm.v b/examples/database/orm.v index fa3db945b9..25f284b717 100644 --- a/examples/database/orm.v +++ b/examples/database/orm.v @@ -70,16 +70,16 @@ fn sqlite3_array() ! { sql db { drop table Parent drop table Child - } + } or {} db.close() or {} } - // + sql db { create table Parent - } + }! sql db { create table Child - } + }! par := Parent{ name: 'test' children: [ @@ -93,10 +93,10 @@ fn sqlite3_array() ! { } sql db { insert par into Parent - } + }! parent := sql db { select from Parent where id == 1 - } + }! eprintln(parent) } @@ -113,16 +113,16 @@ fn msql_array() ! { defer { sql db { drop table Parent - } + } or {} db.close() } - // + db.query('drop table if exists Parent')! db.query('drop table if exists Child')! sql db { create table Parent create table Child - } + }! par := Parent{ name: 'test' children: [ @@ -136,10 +136,10 @@ fn msql_array() ! { } sql db { insert par into Parent - } + }! parent := sql db { select from Parent where id == 1 - } + }! eprintln(parent) } @@ -151,11 +151,11 @@ fn psql_array() ! { db.close() } db.exec_one('drop table if exists "Parent", "Child"') or { eprintln(err) } - // + sql db { create table Parent create table Child - } + }! par := Parent{ name: 'test' children: [ @@ -169,10 +169,10 @@ fn psql_array() ! { } sql db { insert par into Parent - } + }! parent := sql db { select from Parent where id == 1 - } + }! eprintln(parent) } @@ -182,19 +182,17 @@ fn sqlite3() ! { defer { sql db { drop table Module - } - sql db { drop table User - } + } or {} db.close() or {} } - // + sql db { create table Module - } + }! sql db { create table User - } + }! mod := Module{ name: 'test' nr_downloads: 10 @@ -206,10 +204,10 @@ fn sqlite3() ! { } sql db { insert mod into Module - } + }! modul := sql db { select from Module where id == 1 - } + }! eprintln(modul) } @@ -230,13 +228,13 @@ fn msql() ! { } conn.query('DROP TABLE IF EXISTS Module') or { eprintln(err) } conn.query('DROP TABLE IF EXISTS User') or { eprintln(err) } - // + sql conn { create table Module - } + }! sql conn { create table User - } + }! mod := Module{ name: 'test' nr_downloads: 10 @@ -248,10 +246,10 @@ fn msql() ! { } sql conn { insert mod into Module - } + }! m := sql conn { select from Module where id == 1 - } + }! eprintln(m) } @@ -266,7 +264,7 @@ fn psql() ! { sql db { create table Module create table User - } + }! mod := Module{ name: 'test' nr_downloads: 10 @@ -278,13 +276,13 @@ fn psql() ! { } sql db { insert mod into Module - } + }! modul := sql db { select from Module where id == 1 - } + }! sql db { drop table Module - } + }! eprintln(modul) } diff --git a/examples/js_dom_draw_bechmark_chart/README.md b/examples/js_dom_draw_bechmark_chart/README.md index 795479feec..64a55dc718 100644 --- a/examples/js_dom_draw_bechmark_chart/README.md +++ b/examples/js_dom_draw_bechmark_chart/README.md @@ -44,7 +44,7 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sql db { create table Task - } + }! task_model := Task{ title: 'a' @@ -55,14 +55,14 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sw.start() sql db { insert task_model into Task - } + } or { []Task{} } sw.stop() insert_stopwatchs << int(sw.end - sw.start) } sql db { drop table Task - } + }! response := Response{ insert: insert_stopwatchs diff --git a/examples/js_dom_draw_bechmark_chart/chart/README.md b/examples/js_dom_draw_bechmark_chart/chart/README.md index bf33788499..35c24a8d60 100644 --- a/examples/js_dom_draw_bechmark_chart/chart/README.md +++ b/examples/js_dom_draw_bechmark_chart/chart/README.md @@ -31,7 +31,7 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sql db { create table Task - } + } or { panic(err) } task_model := Task{ title: 'a' @@ -42,14 +42,14 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sw.start() sql db { insert task_model into Task - } + } or { []Task{} } sw.stop() insert_stopwatchs << int(sw.end - sw.start) } sql db { drop table Task - } + } or { panic(err) } response := Response{ insert: insert_stopwatchs diff --git a/examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v b/examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v index bf7d8b084b..6c74f85a5d 100644 --- a/examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v +++ b/examples/js_dom_draw_bechmark_chart/v_vweb_orm/src/main.v @@ -50,7 +50,7 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sql db { create table Task - } + } or { panic(err) } task_model := Task{ title: 'a' @@ -62,7 +62,7 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sw.start() sql db { insert task_model into Task - } + } or { panic(err) } sw.stop() insert_stopwatchs << int(sw.end - sw.start) } @@ -72,7 +72,7 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sw.start() result := sql db { select from Task - } + } or { []Task{} } sw.stop() eprintln(result) select_stopwatchs << int(sw.end - sw.start) @@ -83,14 +83,14 @@ pub fn (mut app App) sqlite_memory(count int) vweb.Result { sw.start() sql db { update Task set title = 'b', status = 'finish' where id == i - } + } or { panic(err) } sw.stop() update_stopwatchs << int(sw.end - sw.start) } sql db { drop table Task - } + } or { panic(err) } response := Response{ insert: insert_stopwatchs diff --git a/examples/vweb_fullstack/src/auth_services.v b/examples/vweb_fullstack/src/auth_services.v index db3da637bc..798b935477 100644 --- a/examples/vweb_fullstack/src/auth_services.v +++ b/examples/vweb_fullstack/src/auth_services.v @@ -36,7 +36,7 @@ fn (mut app App) service_auth(username string, password string) !string { users := sql db { select from User where username == username - } + }! user := users.first() if user.username != username { return error('user not found') diff --git a/examples/vweb_fullstack/src/product_service.v b/examples/vweb_fullstack/src/product_service.v index 15f55bf4e8..19b71cdcc1 100644 --- a/examples/vweb_fullstack/src/product_service.v +++ b/examples/vweb_fullstack/src/product_service.v @@ -37,7 +37,7 @@ fn (mut app App) service_get_all_products_from(user_id int) ![]Product { results := sql db { select from Product where user_id == user_id - } + }! return results } diff --git a/examples/vweb_fullstack/src/user_services.v b/examples/vweb_fullstack/src/user_services.v index 222c3d62f7..a190bb2c26 100644 --- a/examples/vweb_fullstack/src/user_services.v +++ b/examples/vweb_fullstack/src/user_services.v @@ -42,7 +42,7 @@ fn (mut app App) service_get_all_user() ![]User { results := sql db { select from User - } + }! return results } @@ -59,7 +59,7 @@ fn (mut app App) service_get_user(id int) !User { results := sql db { select from User where id == id - } + }! return results.first() } diff --git a/examples/vweb_orm_jwt/src/auth_services.v b/examples/vweb_orm_jwt/src/auth_services.v index 82e2c15ac5..b7471805b0 100644 --- a/examples/vweb_orm_jwt/src/auth_services.v +++ b/examples/vweb_orm_jwt/src/auth_services.v @@ -33,7 +33,7 @@ fn (mut app App) service_auth(username string, password string) !string { users := sql db { select from User where username == username - } + }! if users.len == 0 { return error('user not found') diff --git a/examples/vweb_orm_jwt/src/user_controllers.v b/examples/vweb_orm_jwt/src/user_controllers.v index 37fafc333d..e64221f955 100644 --- a/examples/vweb_orm_jwt/src/user_controllers.v +++ b/examples/vweb_orm_jwt/src/user_controllers.v @@ -66,7 +66,7 @@ pub fn (mut app App) delete() vweb.Result { sql db { drop table User - } + } or { panic(err) } return app.text('Successfully deleted table') } diff --git a/examples/vweb_orm_jwt/src/user_services.v b/examples/vweb_orm_jwt/src/user_services.v index a579b9c1f5..fa95b5e78b 100644 --- a/examples/vweb_orm_jwt/src/user_services.v +++ b/examples/vweb_orm_jwt/src/user_services.v @@ -33,7 +33,7 @@ fn (mut app App) service_add_user(username string, password string) !User { users := sql db { select from User where username == username limit 1 - } + }! return users.first() } @@ -50,7 +50,7 @@ fn (mut app App) service_get_user_by_id(user_id int) !User { users := sql db { select from User where id == user_id - } + }! return users.first() } @@ -67,7 +67,7 @@ fn (mut app App) service_get_all_user() ![]User { results := sql db { select from User - } + }! return results } @@ -84,7 +84,7 @@ fn (mut app App) service_get_by_username(username string) !User { results := sql db { select from User where username == username - } + }! if results.len == 0 { return error('User not found') diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v index f12753241f..1a220cbe31 100644 --- a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v +++ b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v @@ -9,5 +9,5 @@ struct Article { pub fn (app &App) find_all_articles() []Article { return sql app.db { select from Article - } + } or { []Article{} } } diff --git a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v index b0d5c0c4d8..864897ec3b 100644 --- a/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v +++ b/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v @@ -18,7 +18,7 @@ fn main() { } sql app.db { create table Article - } + }! vweb.run(app, 8081) } @@ -61,7 +61,7 @@ pub fn (mut app App) new_article(title string, text string) vweb.Result { println(article) sql app.db { insert article into Article - } + } or {} return app.redirect('/') } diff --git a/vlib/db/mysql/mysql_orm_test.v b/vlib/db/mysql/mysql_orm_test.v index 5087ee3643..b349a971d2 100644 --- a/vlib/db/mysql/mysql_orm_test.v +++ b/vlib/db/mysql/mysql_orm_test.v @@ -116,10 +116,10 @@ fn test_mysql_orm() { */ sql db { create table TestCustomSqlType - } + }! mut result_custom_sql := db.query(" - SELECT DATA_TYPE, COLUMN_TYPE + SELECT DATA_TYPE, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TestCustomSqlType' ORDER BY ORDINAL_POSITION @@ -161,7 +161,7 @@ fn test_mysql_orm() { sql db { drop table TestCustomSqlType - } + }! assert result_custom_sql.maps() == information_schema_custom_sql @@ -184,19 +184,19 @@ fn test_mysql_orm() { sql db { create table TestTimeType - } + }! sql db { insert model into TestTimeType - } + }! results := sql db { select from TestTimeType where username == 'hitalo' - } + }! sql db { drop table TestTimeType - } + }! assert results[0].username == model.username assert results[0].created_at == model.created_at @@ -207,7 +207,7 @@ fn test_mysql_orm() { */ sql db { create table TestDefaultAtribute - } + }! mut result_defaults := db.query(" SELECT COLUMN_DEFAULT @@ -222,7 +222,7 @@ fn test_mysql_orm() { sql db { drop table TestDefaultAtribute - } + }! information_schema_column_default_sql := [{ 'COLUMN_DEFAULT': '' diff --git a/vlib/db/pg/pg_orm_test.v b/vlib/db/pg/pg_orm_test.v index f6f2162fbd..8e81722769 100644 --- a/vlib/db/pg/pg_orm_test.v +++ b/vlib/db/pg/pg_orm_test.v @@ -137,7 +137,7 @@ fn test_pg_orm() { sql db { create table TestCustomSqlType - } + }! mut result_custom_sql := db.exec(" SELECT DATA_TYPE @@ -157,7 +157,7 @@ fn test_pg_orm() { sql db { drop table TestCustomSqlType - } + }! assert information_schema_data_types_results == information_schema_custom_sql @@ -180,19 +180,19 @@ fn test_pg_orm() { sql db { create table TestTimeType - } + }! sql db { insert model into TestTimeType - } + }! results := sql db { select from TestTimeType where username == 'hitalo' - } + }! sql db { drop table TestTimeType - } + }! assert results[0].username == model.username assert results[0].created_at == model.created_at @@ -203,7 +203,7 @@ fn test_pg_orm() { */ sql db { create table TestDefaultAtribute - } + }! mut result_defaults := db.exec(" SELECT column_default @@ -221,6 +221,6 @@ fn test_pg_orm() { } sql db { drop table TestDefaultAtribute - } + }! assert ['gen_random_uuid()', '', 'CURRENT_TIMESTAMP'] == information_schema_defaults_results } diff --git a/vlib/db/sqlite/sqlite_orm_test.v b/vlib/db/sqlite/sqlite_orm_test.v index 94f780cff2..dc31f62244 100644 --- a/vlib/db/sqlite/sqlite_orm_test.v +++ b/vlib/db/sqlite/sqlite_orm_test.v @@ -101,7 +101,7 @@ fn test_sqlite_orm() { sql db { create table TestCustomSqlType - } + }! mut result_custom_sql, mut exec_custom_code := db.exec(' pragma table_info(TestCustomSqlType); @@ -119,14 +119,14 @@ fn test_sqlite_orm() { sql db { drop table TestCustomSqlType - } + }! /** test default attribute */ sql db { create table TestDefaultAtribute - } + }! mut result_default_sql, mut code := db.exec(' pragma table_info(TestDefaultAtribute); @@ -147,11 +147,11 @@ fn test_sqlite_orm() { sql db { insert test_default_atribute into TestDefaultAtribute - } + }! test_default_atributes := sql db { select from TestDefaultAtribute limit 1 - } + }! result_test_default_atribute := test_default_atributes.first() assert result_test_default_atribute.name == 'Hitalo' @@ -164,7 +164,7 @@ fn test_sqlite_orm() { sql db { drop table TestDefaultAtribute - } + }! } fn test_get_affected_rows_count() { @@ -204,26 +204,26 @@ fn test_get_affected_rows_count() { all := sql db { select from EntityToTest - } + }! assert 1 == all.len sql db { update EntityToTest set smth = '2' where id == 1 - } + }! assert db.get_affected_rows_count() == 1 sql db { update EntityToTest set smth = '2' where id == 2 - } + }! assert db.get_affected_rows_count() == 0 sql db { delete from EntityToTest where id == 2 - } + }! assert db.get_affected_rows_count() == 0 sql db { delete from EntityToTest where id == 1 - } + }! assert db.get_affected_rows_count() == 1 } diff --git a/vlib/db/sqlite/sqlite_test.v b/vlib/db/sqlite/sqlite_test.v index 4ab454361f..21a4ebeb80 100644 --- a/vlib/db/sqlite/sqlite_test.v +++ b/vlib/db/sqlite/sqlite_test.v @@ -19,10 +19,10 @@ fn (back Host) get_users() []User { return [] } -fn create_host(db Connection) Host { +fn create_host(db Connection) !Host { sql db { create table User - } + }! return Host{ db: db @@ -87,6 +87,6 @@ fn test_can_access_sqlite_result_consts() { } fn test_alias_db() { - create_host(sqlite.connect(':memory:')!) + create_host(sqlite.connect(':memory:')!)! assert true } diff --git a/vlib/orm/orm_create_and_drop_test.v b/vlib/orm/orm_create_and_drop_test.v index a13d116b70..a163b3bd60 100644 --- a/vlib/orm/orm_create_and_drop_test.v +++ b/vlib/orm/orm_create_and_drop_test.v @@ -23,18 +23,24 @@ fn test_create_only_one_table() { 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 } + } or { + is_child_created = false + 0 + } _ := sql db { select count from Note - } or { is_note_created = false } + } or { + is_note_created = false + 0 + } assert is_child_created == false assert is_note_created == false @@ -45,13 +51,13 @@ fn test_drop_only_one_table() { 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 @@ -59,19 +65,28 @@ fn test_drop_only_one_table() { sql db { drop table Parent - } + }! _ := sql db { select count from Parent - } or { is_parent_dropped = true } + } or { + is_parent_dropped = true + 0 + } _ := sql db { select count from Child - } or { is_child_dropped = true } + } or { + is_child_dropped = true + 0 + } _ := sql db { select count from Note - } or { is_note_dropped = true } + } or { + is_note_dropped = true + 0 + } assert is_parent_dropped assert is_child_dropped == false diff --git a/vlib/orm/orm_fn_calls_test.v b/vlib/orm/orm_fn_calls_test.v index 3994bd4733..0d4c3e20bd 100644 --- a/vlib/orm/orm_fn_calls_test.v +++ b/vlib/orm/orm_fn_calls_test.v @@ -15,7 +15,7 @@ fn test_fn_calls() { sql db { create table User - } + }! first_user := User{ name: 'first' @@ -30,18 +30,18 @@ fn test_fn_calls() { sql db { insert first_user into User insert second_user into User - } + }! users_with_acceptable_age := sql db { select from User where age >= get_acceptable_age() - } + }! assert users_with_acceptable_age.len == 1 assert users_with_acceptable_age.first().name == 'first' users_with_non_acceptable_age := sql db { select from User where age < get_acceptable_age() - } + }! assert users_with_non_acceptable_age.len == 1 assert users_with_non_acceptable_age.first().name == 'second' diff --git a/vlib/orm/orm_insert_reserved_name_test.v b/vlib/orm/orm_insert_reserved_name_test.v index 8b42884ca1..3fd5f7d2b9 100644 --- a/vlib/orm/orm_insert_reserved_name_test.v +++ b/vlib/orm/orm_insert_reserved_name_test.v @@ -14,20 +14,20 @@ fn test_insert_with_reserved_name() { } sql db { create table Bad - } + }! sql db { insert bad into Bad - } + }! sql db { insert bad into Bad insert bad into Bad insert bad into Bad - } + }! rows := sql db { select from Bad - } + }! assert rows.len == 4 } diff --git a/vlib/orm/orm_insert_test.v b/vlib/orm/orm_insert_test.v index 0833d8c52e..62a42b198d 100644 --- a/vlib/orm/orm_insert_test.v +++ b/vlib/orm/orm_insert_test.v @@ -25,10 +25,10 @@ struct Account { id int [primary; sql: serial] } -pub fn insert_parent(db sqlite.DB, mut parent Parent) { +pub fn insert_parent(db sqlite.DB, mut parent Parent) ! { sql db { insert parent into Parent - } + }! } fn test_insert_empty_object() { @@ -39,11 +39,11 @@ fn test_insert_empty_object() { sql db { create table Account insert account into Account - } + }! accounts := sql db { select from Account - } + }! assert accounts.len == 1 } @@ -55,17 +55,17 @@ fn test_orm_insert_mut_object() { create table Parent create table Child create table Note - } + }! mut parent := Parent{ name: 'test' } - insert_parent(db, mut parent) + insert_parent(db, mut parent)! parents := sql db { select from Parent - } + }! assert parents.len == 1 } @@ -77,7 +77,7 @@ fn test_orm_insert_with_multiple_child_elements() { create table Parent create table Child create table Note - } + }! new_parent := Parent{ name: 'test' @@ -104,11 +104,11 @@ fn test_orm_insert_with_multiple_child_elements() { sql db { insert new_parent into Parent - } + }! parents := sql db { select from Parent where id == 1 - } + }! parent := parents.first() assert parent.children.len == new_parent.children.len @@ -116,12 +116,12 @@ fn test_orm_insert_with_multiple_child_elements() { 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' diff --git a/vlib/orm/orm_interface_test.v b/vlib/orm/orm_interface_test.v index ca72766ec0..5f8844311b 100644 --- a/vlib/orm/orm_interface_test.v +++ b/vlib/orm/orm_interface_test.v @@ -12,7 +12,7 @@ fn test_orm_interface() { sql db { create table User - } + }! user := User{ name: 'test' @@ -20,11 +20,11 @@ fn test_orm_interface() { sql db { insert user into User - } + }! users := sql db { select from User - } + }! assert users.len == 1 assert users.first().name == user.name diff --git a/vlib/orm/orm_last_id_test.v b/vlib/orm/orm_last_id_test.v index 9376837fa4..75be85983d 100644 --- a/vlib/orm/orm_last_id_test.v +++ b/vlib/orm/orm_last_id_test.v @@ -10,7 +10,7 @@ fn test_last_id() { sql db { create table User - } + }! first_user := User{ name: 'first' @@ -23,7 +23,7 @@ fn test_last_id() { sql db { insert first_user into User insert second_user into User - } + }! last_id := db.last_id() diff --git a/vlib/orm/orm_mut_db_test.v b/vlib/orm/orm_mut_db_test.v index 1c05d5eb93..3bc2d4306a 100644 --- a/vlib/orm/orm_mut_db_test.v +++ b/vlib/orm/orm_mut_db_test.v @@ -5,10 +5,10 @@ struct User { name string } -fn get_users(mut db sqlite.DB) []User { +fn get_users(mut db sqlite.DB) ![]User { return sql db { select from User - } + }! } fn test_orm_mut_db() { @@ -16,7 +16,7 @@ fn test_orm_mut_db() { sql db { create table User - } + }! first_user := User{ name: 'first' @@ -28,9 +28,9 @@ fn test_orm_mut_db() { sql db { insert first_user into User insert second_user into User - } + }! - users := get_users(mut db) + users := get_users(mut db)! assert users.len == 2 } diff --git a/vlib/orm/orm_result_test.v b/vlib/orm/orm_result_test.v new file mode 100644 index 0000000000..38b5238125 --- /dev/null +++ b/vlib/orm/orm_result_test.v @@ -0,0 +1,82 @@ +import db.sqlite + +struct Account { + id int [primary; sql: serial] + name string +} + +struct Note { + id int [primary; sql: serial] + content string +} + +fn test_catch_table_is_not_created() { + mut db := sqlite.connect(':memory:')! + + mut is_inserted := true + + account := Account{} + + sql db { + insert account into Account + } or { is_inserted = false } + + assert !is_inserted +} + +fn test_catch_one_of_queries() { + mut db := sqlite.connect(':memory:')! + + sql db { + create table Account + }! + + account := Account{} + + sql db { + insert account into Account + }! + + mut are_updated := true + + sql db { + update Account set name = 'test' where id == 1 + update Note set content = 'test' where id == 1 + } or { are_updated = false } + + assert !are_updated +} + +fn test_print_results() { + mut db := sqlite.connect(':memory:')! + + sql db { + create table Account + }! + + account := Account{} + + sql db { + insert account into Account + }! + + count := sql db { + select count from Account + }! + + println(count) + + user := sql db { + select from Account + }!.first() + + println(user) + + users := sql db { + select from Account + }! + + println(users) + + assert true +} diff --git a/vlib/orm/orm_sql_or_blocks_test.v b/vlib/orm/orm_sql_or_blocks_test.v index 906f45b9ea..7d281e635a 100644 --- a/vlib/orm/orm_sql_or_blocks_test.v +++ b/vlib/orm/orm_sql_or_blocks_test.v @@ -38,20 +38,26 @@ fn test_sql_or_block_for_insert() { user := User{1, 'bilbo'} eprintln('> inserting user 1 (first try)...') + mut is_user_inserted := true + sql db { insert user into User } or { println('user should have been inserted, but could not, err: ${err}') - assert false + is_user_inserted = false } + assert is_user_inserted + eprintln('> inserting user 1 (second try)...') sql db { insert user into User } or { - assert true println('user could not be inserted, err: ${err}') + is_user_inserted = false } + + assert !is_user_inserted eprintln('LINE: ${@LINE}') db.close()! } diff --git a/vlib/orm/orm_string_interpolation_in_where_test.v b/vlib/orm/orm_string_interpolation_in_where_test.v index ce860d48df..29f90bf69b 100644 --- a/vlib/orm/orm_string_interpolation_in_where_test.v +++ b/vlib/orm/orm_string_interpolation_in_where_test.v @@ -10,7 +10,7 @@ fn test_string_interpolation() { sql db { create table User - } + }! user_suffix := '_user' @@ -25,11 +25,11 @@ fn test_string_interpolation() { sql db { insert first_user into User insert second_user into User - } + }! users := sql db { select from User where name == 'first${user_suffix}' - } + }! assert users.len == 1 assert users.first().name == 'first${user_suffix}' diff --git a/vlib/orm/orm_test.v b/vlib/orm/orm_test.v index 2d738d9e1a..fcaaf25f44 100644 --- a/vlib/orm/orm_test.v +++ b/vlib/orm/orm_test.v @@ -39,7 +39,7 @@ fn test_use_struct_field_as_limit() { sql db { create table User - } + }! foo := Foo{ age: 10 @@ -52,11 +52,11 @@ fn test_use_struct_field_as_limit() { sql db { insert sam into User - } + }! users := sql db { select from User limit foo.age - } + }! assert users.len == 1 } @@ -66,10 +66,10 @@ fn test_orm() { sql db { create table Module - } + }! sql db { create table User - } + }! name := 'Peter' @@ -93,47 +93,47 @@ fn test_orm() { insert sam into User insert peter into User insert k into User - } + }! c := sql db { select count from User where id != 1 - } + }! assert c == 2 nr_all_users := sql db { select count from User - } + }! assert nr_all_users == 3 nr_users1 := sql db { select count from User where id == 1 - } + }! assert nr_users1 == 1 nr_peters := sql db { select count from User where id == 2 && name == 'Peter' - } + }! assert nr_peters == 1 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' mut users := sql db { select from User where name == name limit 1 - } + }! one_peter := users.first() assert one_peter.name == 'Peter' @@ -141,7 +141,7 @@ fn test_orm() { users = sql db { select from User where id == 1 - } + }! user := users.first() assert user.name == 'Sam' @@ -150,7 +150,7 @@ fn test_orm() { users = sql db { select from User where id > 0 - } + }! assert users.len == 3 assert users[0].name == 'Sam' assert users[1].name == 'Peter' @@ -158,12 +158,12 @@ fn test_orm() { users2 := sql db { select from User where id < 0 - } + }! assert users2.len == 0 users3 := sql db { select from User where age == 29 || age == 31 - } + }! assert users3.len == 2 assert users3[0].age == 29 @@ -175,11 +175,11 @@ fn test_orm() { } sql db { insert new_user into User - } + }! users = sql db { select from User where id == 4 - } + }! x := users.first() assert x.age == 30 @@ -188,14 +188,14 @@ fn test_orm() { users = sql db { select from User where id == 3 - } + }! kate := users.first() assert kate.is_customer == true users = sql db { select from User where is_customer == true limit 1 - } + }! customer := users.first() assert customer.is_customer == true @@ -203,22 +203,22 @@ fn test_orm() { sql db { update User set age = 31 where name == 'Kate' - } + }! users = sql db { select from User where id == 3 - } + }! kate2 := users.first() assert kate2.age == 31 assert kate2.name == 'Kate' sql db { update User set age = 32, name = 'Kate N' where name == 'Kate' - } + }! users = sql db { select from User where id == 3 - } + }! mut kate3 := users.first() assert kate3.age == 32 assert kate3.name == 'Kate N' @@ -226,11 +226,11 @@ fn test_orm() { new_age := 33 sql db { update User set age = new_age, name = 'Kate N' where id == 3 - } + }! users = sql db { select from User where id == 3 - } + }! kate3 = users.first() assert kate3.age == 33 @@ -239,42 +239,42 @@ fn test_orm() { foo := Foo{34} sql db { update User set age = foo.age, name = 'Kate N' where id == 3 - } + }! users = sql db { select from User where id == 3 - } + }! kate3 = users.first() assert kate3.age == 34 assert kate3.name == 'Kate N' no_user := sql db { select from User where id == 30 - } + }! assert no_user.len == 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 z := sql db { select from User order by id limit 2 offset offset_const - } + }! assert z.len == 2 assert z[0].id == 3 users = sql db { select from User order by age desc limit 1 - } + }! oldest := users.first() assert oldest.age == 34 @@ -282,17 +282,17 @@ fn test_orm() { offs := 1 users = sql db { select from User order by age desc limit 1 offset offs - } + }! second_oldest := users.first() assert second_oldest.age == 31 sql db { delete from User where age == 34 - } + }! users = sql db { select from User order by age desc limit 1 - } + }! updated_oldest := users.first() assert updated_oldest.age == 31 @@ -300,41 +300,41 @@ fn test_orm() { // db.exec('insert into User (name, age) values (NULL, 31)') users = sql db { select from User where id == 5 - } + }! assert users.len == 0 users = sql db { select from User where id == 1 - } + }! age_test := users.first() assert age_test.age == 29 sql db { update User set age = age + 1 where id == 1 - } + }! users = sql db { select from User where id == 1 - } + }! mut first := users.first() assert first.age == 30 sql db { update User set age = age * 2 where id == 1 - } + }! users = sql db { select from User where id == 1 - } + }! first = users.first() assert first.age == 60 sql db { create table TestTime - } + }! tnow := time.now() @@ -344,11 +344,11 @@ fn test_orm() { sql db { insert time_test into TestTime - } + }! data := sql db { select from TestTime where create == tnow - } + }! assert data.len == 1 assert tnow.unix == data[0].create.unix @@ -357,26 +357,26 @@ fn test_orm() { sql db { insert mod into Module - } + }! sql db { update Module set test_id = 11 where id == 1 - } + }! mut modules := sql db { select from Module where id == 1 - } + }! assert modules.first().test_id == 11 t := time.now() sql db { update Module set created = t where id == 1 - } + }! modules = sql db { select from Module where id == 1 - } + }! // Note: usually updated_time_mod.created != t, because t has // its microseconds set, while the value retrieved from the DB @@ -385,12 +385,12 @@ fn test_orm() { users = sql db { select from User where (name == 'Sam' && is_customer == true) || id == 1 - } + }! assert users.first() == first sql db { drop table Module drop table TestTime - } + }! } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index e8498f6492..f7bef9be30 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1767,9 +1767,8 @@ pub: pos token.Pos where_expr Expr update_exprs []Expr // for `update` - // is_top_level indicates that a statement is parsed from code - // and is not inserted by ORM for inserting in related tables. - is_top_level bool + // is_generated indicates a statement is generated by ORM for complex queries with related tables. + is_generated bool scope &Scope = unsafe { nil } pub mut: object_var_name string // `user` @@ -1788,8 +1787,10 @@ pub: has_offset bool has_desc bool is_array bool - or_expr OrExpr - pos token.Pos + // is_generated indicates a statement is generated by ORM for complex queries with related tables. + is_generated bool + or_expr OrExpr + pos token.Pos pub mut: typ Type db_expr Expr // `db` in `sql db {` diff --git a/vlib/v/checker/orm.v b/vlib/v/checker/orm.v index 7998b81226..21b0060e09 100644 --- a/vlib/v/checker/orm.v +++ b/vlib/v/checker/orm.v @@ -11,6 +11,8 @@ const ( v_orm_prefix = 'V ORM' ) +type ORMExpr = ast.SqlExpr | ast.SqlStmt + fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { c.inside_sql = true defer { @@ -47,12 +49,13 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { pos: node.pos has_where: true where_expr: ast.None{} - typ: typ + typ: typ.set_flag(.result) db_expr: node.db_expr table_expr: ast.TypeNode{ pos: node.table_expr.pos typ: typ } + is_generated: true } tmp_inside_sql := c.inside_sql @@ -141,33 +144,21 @@ fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { } c.expr(node.db_expr) - if node.or_expr.kind == .block { - if node.or_expr.stmts.len == 0 { - c.orm_error('or block needs to return a default value', node.or_expr.pos) - } - if node.or_expr.stmts.len > 0 && node.or_expr.stmts.last() is ast.ExprStmt { - c.expected_or_type = node.typ - } - c.stmts_ending_with_expression(node.or_expr.stmts) - c.check_expr_opt_call(node, node.typ) - c.expected_or_type = ast.void_type - } - return node.typ + c.check_orm_or_expr(node) + + return node.typ.clear_flag(.result) } fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) ast.Type { node.db_expr_type = c.table.unaliased_type(c.expr(node.db_expr)) - mut typ := ast.void_type + for mut line in node.lines { - a := c.sql_stmt_line(mut line) - if a != ast.void_type { - typ = a - } + c.sql_stmt_line(mut line) } - if node.or_expr.kind == .block { - c.stmts_ending_with_expression(node.or_expr.stmts) - } - return typ + + c.check_orm_or_expr(node) + + return ast.void_type } fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { @@ -183,7 +174,7 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { c.cur_orm_ts = old_ts } - if node.kind == .insert && node.is_top_level { + if node.kind == .insert && !node.is_generated { inserting_object_name := node.object_var_name inserting_object := node.scope.find(inserting_object_name) or { c.error('undefined ident: `${inserting_object_name}`', node.pos) @@ -238,6 +229,7 @@ fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { typ: typ } object_var_name: object_var_name + is_generated: true } tmp_inside_sql := c.inside_sql c.sql_stmt_line(mut n) @@ -468,3 +460,39 @@ fn (_ &Checker) fn_return_type_flag_to_string(typ ast.Type) string { '' } } + +fn (mut c Checker) check_orm_or_expr(expr ORMExpr) { + if expr is ast.SqlExpr { + if expr.is_generated { + return + } + } + + return_type := if expr is ast.SqlExpr { + expr.typ + } else { + ast.void_type.set_flag(.result) + } + + if expr.or_expr.kind == .absent { + if c.inside_defer { + c.error('V ORM returns a result, so it should have an `or {}` block at the end', + expr.pos) + } else { + c.error('V ORM returns a result, so it should have either an `or {}` block, or `!` at the end', + expr.pos) + } + } else { + c.check_or_expr(expr.or_expr, return_type.clear_flag(.result), return_type, if expr is ast.SqlExpr { + expr + } else { + ast.empty_expr + }) + } + + if expr.or_expr.kind == .block { + c.expected_or_type = return_type.clear_flag(.result) + c.stmts_ending_with_expression(expr.or_expr.stmts) + c.expected_or_type = ast.void_type + } +} diff --git a/vlib/v/checker/tests/orm_empty_struct.out b/vlib/v/checker/tests/orm_empty_struct.out index 4937993470..2989c16944 100644 --- a/vlib/v/checker/tests/orm_empty_struct.out +++ b/vlib/v/checker/tests/orm_empty_struct.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_empty_struct.vv:9:15: error: V ORM: select: empty field 8 | _ := sql db { 9 | select from Person | ~~~~~~ - 10 | } + 10 | }! 11 | } diff --git a/vlib/v/checker/tests/orm_empty_struct.vv b/vlib/v/checker/tests/orm_empty_struct.vv index 1ffbb5e13c..e5620bde9b 100644 --- a/vlib/v/checker/tests/orm_empty_struct.vv +++ b/vlib/v/checker/tests/orm_empty_struct.vv @@ -7,5 +7,5 @@ fn main() { db := sqlite.connect(':memory:')! _ := sql db { select from Person - } + }! } diff --git a/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.out b/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.out index 2bdefe2bee..cb09eb67e5 100644 --- a/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.out +++ b/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.vv:52:37: error: V ORM: 51 | steve := sql db { 52 | select from Parent where child == get_second_child() | ~~~~~~~~~~~~~~~~~~ - 53 | } + 53 | }! 54 | diff --git a/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.vv b/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.vv index 2adec6c34d..62411cfdee 100644 --- a/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.vv +++ b/vlib/v/checker/tests/orm_fn_call_with_wrong_return_type.vv @@ -31,7 +31,7 @@ fn main() { sql db { create table Parent create table Child - } + }! new_parent := Parent{ name: 'test' @@ -40,17 +40,17 @@ fn main() { sql db { insert new_parent into Parent - } + }! first_child := get_first_child() sql db { insert first_child into Child - } + }! steve := sql db { select from Parent where child == get_second_child() - } + }! dump(steve) } diff --git a/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.out b/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.out index 30930ed76e..ac5d53bea3 100644 --- a/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.out +++ b/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.vv:19:10: erro 18 | create table Tiddlers 19 | insert tiddler into Tiddlers | ~~~~~~~ - 20 | } + 20 | }! 21 | } diff --git a/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.vv b/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.vv index 901977cf05..8516d77ca2 100644 --- a/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.vv +++ b/vlib/v/checker/tests/orm_insert_object_with_mismatched_type_error.vv @@ -17,5 +17,5 @@ fn main() { sql db { create table Tiddlers insert tiddler into Tiddlers - } + }! } diff --git a/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.out b/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.out index 91944f02d2..e01c7d11b2 100644 --- a/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.out +++ b/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.out @@ -4,5 +4,5 @@ vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error. 17 | users := sql db { 18 | select from User where first == second | ~~~~~ - 19 | } + 19 | }! 20 | diff --git a/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.vv b/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.vv index 851035566b..b1bbad976e 100644 --- a/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.vv +++ b/vlib/v/checker/tests/orm_left_side_expr_in_infix_expr_has_no_struct_field_error.vv @@ -9,14 +9,14 @@ fn main() { sql db { create table User - } + }! first := 'first' second := 'second' users := sql db { select from User where first == second - } + }! println(users) } diff --git a/vlib/v/checker/tests/orm_limit_less_than_zero_error.out b/vlib/v/checker/tests/orm_limit_less_than_zero_error.out index ab0899e179..03efec4954 100644 --- a/vlib/v/checker/tests/orm_limit_less_than_zero_error.out +++ b/vlib/v/checker/tests/orm_limit_less_than_zero_error.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_limit_less_than_zero_error.vv:18:26: error: V ORM: `lim 17 | users := sql db { 18 | select from User limit user_limit | ~~~~~~~~~~ - 19 | } + 19 | }! 20 | diff --git a/vlib/v/checker/tests/orm_limit_less_than_zero_error.vv b/vlib/v/checker/tests/orm_limit_less_than_zero_error.vv index 2b76c70174..1723f64859 100644 --- a/vlib/v/checker/tests/orm_limit_less_than_zero_error.vv +++ b/vlib/v/checker/tests/orm_limit_less_than_zero_error.vv @@ -16,7 +16,7 @@ fn main() { users := sql db { select from User limit user_limit - } + }! println(users) } diff --git a/vlib/v/checker/tests/orm_no_default_value.out b/vlib/v/checker/tests/orm_no_default_value.out index 7df9362c1e..43d11d19b5 100644 --- a/vlib/v/checker/tests/orm_no_default_value.out +++ b/vlib/v/checker/tests/orm_no_default_value.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/orm_no_default_value.vv:11:4: error: V ORM: or block needs to return a default value +vlib/v/checker/tests/orm_no_default_value.vv:11:4: error: assignment requires a non empty `or {}` block 9 | _ := sql db { 10 | select from Person 11 | } or { diff --git a/vlib/v/checker/tests/orm_not_a_struct.out b/vlib/v/checker/tests/orm_not_a_struct.out index 66bf20aa80..2e0752f01e 100644 --- a/vlib/v/checker/tests/orm_not_a_struct.out +++ b/vlib/v/checker/tests/orm_not_a_struct.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_not_a_struct.vv:10:15: error: V ORM: the table symbol ` 9 | _ := sql db { 10 | select from Person | ~~~~~~ - 11 | } + 11 | }! 12 | } diff --git a/vlib/v/checker/tests/orm_not_a_struct.vv b/vlib/v/checker/tests/orm_not_a_struct.vv index 23f83a36b0..dd71a596fc 100644 --- a/vlib/v/checker/tests/orm_not_a_struct.vv +++ b/vlib/v/checker/tests/orm_not_a_struct.vv @@ -8,5 +8,5 @@ fn main() { db := sqlite.connect(':memory:')! _ := sql db { select from Person - } + }! } diff --git a/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.out b/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.out index da507cf321..aba3132593 100644 --- a/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.out +++ b/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.out @@ -4,10 +4,10 @@ vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.vv:14:29: erro 13 | users := sql db { 14 | select from User order by database | ~~~~~~~~ - 15 | } + 15 | }! 16 | vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.vv:17:2: error: `println` can not print void expressions - 15 | } + 15 | }! 16 | 17 | println(users) | ~~~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.vv b/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.vv index a31a0e11d6..6d6c8e153d 100644 --- a/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.vv +++ b/vlib/v/checker/tests/orm_using_non_struct_field_in_order_by_error.vv @@ -12,7 +12,7 @@ fn main() { users := sql db { select from User order by database - } + }! println(users) } diff --git a/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.out b/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.out index 3fa81fb368..8661d02de3 100644 --- a/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.out +++ b/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.vv:13:10: error: 12 | create table Node 13 | insert bug into Node | ~~~ - 14 | } + 14 | }! 15 | } diff --git a/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.vv b/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.vv index 8af032eb17..088a6f8bfe 100644 --- a/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.vv +++ b/vlib/v/checker/tests/orm_using_undefined_object_in_insert_error.vv @@ -11,5 +11,5 @@ fn main() { sql db { create table Node insert bug into Node - } + }! } diff --git a/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.out b/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.out index db8c1637e7..3b0a55cf0d 100644 --- a/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.out +++ b/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.out @@ -1,7 +1,7 @@ vlib/v/checker/tests/orm_using_undefined_var_in_where_err.vv:24:35: error: undefined variable: `email` - 22 | + 22 | 23 | _ := sql db { 24 | select from User where email == email | ~~~~~ - 25 | } + 25 | }! 26 | } diff --git a/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.vv b/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.vv index 6650ea4e40..558bdb5388 100644 --- a/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.vv +++ b/vlib/v/checker/tests/orm_using_undefined_var_in_where_err.vv @@ -14,13 +14,13 @@ fn main() { db := sqlite.connect(':memory:')! sql db { create table User - } + }! m := User{} sql db { insert m into User - } + }! _ := sql db { select from User where email == email - } + }! } diff --git a/vlib/v/checker/tests/orm_wrong_where_expr_error.out b/vlib/v/checker/tests/orm_wrong_where_expr_error.out index 5e5e897c3a..25d78169b4 100644 --- a/vlib/v/checker/tests/orm_wrong_where_expr_error.out +++ b/vlib/v/checker/tests/orm_wrong_where_expr_error.out @@ -3,5 +3,5 @@ vlib/v/checker/tests/orm_wrong_where_expr_error.vv:15:26: error: V ORM: `where` 14 | users := sql db { 15 | select from User where 3 | ^ - 16 | } + 16 | }! 17 | diff --git a/vlib/v/checker/tests/orm_wrong_where_expr_error.vv b/vlib/v/checker/tests/orm_wrong_where_expr_error.vv index c17e135ff7..d06ad2b675 100644 --- a/vlib/v/checker/tests/orm_wrong_where_expr_error.vv +++ b/vlib/v/checker/tests/orm_wrong_where_expr_error.vv @@ -9,11 +9,11 @@ fn main() { sql db { create table User - } + }! users := sql db { select from User where 3 - } + }! println(users) } diff --git a/vlib/v/gen/c/sql.v b/vlib/v/gen/c/sql.v index 37d37820a1..c32daa03f1 100644 --- a/vlib/v/gen/c/sql.v +++ b/vlib/v/gen/c/sql.v @@ -3,7 +3,6 @@ module c import v.ast -import v.token import v.util enum SqlExprSide { @@ -65,11 +64,7 @@ fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string, or_expr ast.OrExpr g.sql_delete(node, expr, table_name) } - if or_expr.kind == .block { - g.or_block(res, or_expr, ast.int_type.set_flag(.result)) - } else if or_expr.kind == .absent { - g.write_error_handling_for_orm_result(node.pos, res) - } + g.or_block(res, or_expr, ast.int_type.set_flag(.result)) } fn (mut g Gen) sql_create_table(node ast.SqlStmtLine, expr string, table_name string) { @@ -554,10 +549,10 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as } } - res := g.new_tmp_var() + select_result_var_name := g.new_tmp_var() table_name := g.get_table_name(node.table_expr) g.sql_table_name = g.table.sym(node.table_expr.typ).name - g.write('${result_name}_Array_Array_orm__Primitive _o${res} = orm__Connection_name_table[${expr}._typ]._method_select(${expr}._object, ') + g.write('${result_name}_Array_Array_orm__Primitive ${select_result_var_name} = orm__Connection_name_table[${expr}._typ]._method_select(${expr}._object, ') g.write('(orm__SelectConfig){') g.write('.table = _SLIT("${table_name}"),') g.write('.is_count = ${node.is_count},') @@ -650,38 +645,37 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as } g.writeln(');') - mut tmp_left := g.new_tmp_var() - g.writeln('${g.typ(node.typ.set_flag(.result))} ${tmp_left};') + unwrapped_typ := node.typ.clear_flag(.result) + unwrapped_c_typ := g.typ(unwrapped_typ) + c_typ := g.typ(node.typ) - if node.or_expr.kind == .block { - g.writeln('${tmp_left}.is_error = _o${res}.is_error;') - g.writeln('${tmp_left}.err = _o${res}.err;') - 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}') - } + mut non_orm_result_var_name := g.new_tmp_var() + g.writeln('${c_typ} ${non_orm_result_var_name};') + g.writeln('${non_orm_result_var_name}.is_error = ${select_result_var_name}.is_error;') + g.writeln('${non_orm_result_var_name}.err = ${select_result_var_name}.err;') + g.or_block(non_orm_result_var_name, node.or_expr, node.typ) + g.writeln('else {') + g.indent++ - g.writeln('Array_Array_orm__Primitive ${res} = (*(Array_Array_orm__Primitive*)_o${res}.data);') + select_unwrapped_result_var_name := g.new_tmp_var() + + g.writeln('Array_Array_orm__Primitive ${select_unwrapped_result_var_name} = (*(Array_Array_orm__Primitive*)${select_result_var_name}.data);') if node.is_count { - g.writeln('*(${g.typ(node.typ)}*) ${tmp_left}.data = *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get(${res}, 0)), 0))._int);') - if node.or_expr.kind == .block { - g.indent-- - g.writeln('}') - } + g.writeln('*(${unwrapped_c_typ}*) ${non_orm_result_var_name}.data = *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get(${select_unwrapped_result_var_name}, 0)), 0))._int);') + + g.indent-- + g.writeln('}') } else { tmp := g.new_tmp_var() - styp := g.typ(node.typ) idx := g.new_tmp_var() g.writeln('int ${idx} = 0;') mut typ_str := '' if node.is_array { info := g.table.sym(node.typ).array_info() typ_str = g.typ(info.elem_type) - g.writeln('${styp} ${tmp}_array = __new_array(0, ${res}.len, sizeof(${typ_str}));') - g.writeln('for (; ${idx} < ${res}.len; ${idx}++) {') + g.writeln('${unwrapped_c_typ} ${tmp}_array = __new_array(0, ${select_unwrapped_result_var_name}.len, sizeof(${typ_str}));') + g.writeln('for (; ${idx} < ${select_unwrapped_result_var_name}.len; ${idx}++) {') g.indent++ g.write('${typ_str} ${tmp} = (${typ_str}) {') inf := g.table.sym(info.elem_type).struct_info() @@ -693,7 +687,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as } g.writeln('};') } else { - g.write('${styp} ${tmp} = (${styp}){') + g.write('${unwrapped_c_typ} ${tmp} = (${unwrapped_c_typ}){') info := g.table.sym(node.typ).struct_info() for i, field in info.fields { g.zero_struct_field(field) @@ -704,10 +698,10 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as g.writeln('};') } - g.writeln('if (${res}.len > 0) {') + g.writeln('if (${select_unwrapped_result_var_name}.len > 0) {') g.indent++ for i, field in fields { - sel := '(*(orm__Primitive*) array_get((*(Array_orm__Primitive*) array_get(${res}, ${idx})), ${i}))' + sel := '(*(orm__Primitive*) array_get((*(Array_orm__Primitive*) array_get(${select_unwrapped_result_var_name}, ${idx})), ${i}))' sym := g.table.sym(field.typ) if sym.kind == .struct_ && sym.name != 'time.Time' { mut sub := node.sub_structs[int(field.typ)] @@ -758,7 +752,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as scope: 0 } mut arr := ast.SqlExpr{ - typ: field.typ + typ: field.typ.set_flag(.result) is_count: sub.is_count db_expr: sub.db_expr has_where: sub.has_where @@ -768,6 +762,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as 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 @@ -791,19 +786,19 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as g.writeln('}') } - g.write('*(${g.typ(node.typ)}*) ${tmp_left}.data = ${tmp}') + g.write('*(${unwrapped_c_typ}*) ${non_orm_result_var_name}.data = ${tmp}') if node.is_array { g.write('_array') } g.writeln(';') - if node.or_expr.kind == .block { - g.indent-- - g.writeln('}') - } + + g.indent-- + g.writeln('}') } - g.write('${left} *(${g.typ(node.typ)}*) ${tmp_left}.data') - if !g.inside_call { - g.writeln(';') + g.write('${left} *(${unwrapped_c_typ}*) ${non_orm_result_var_name}.data') + + if node.is_generated { + g.write(';') } } @@ -862,20 +857,6 @@ 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('}') -} - fn (mut g Gen) write_orm_connection_init(connection_var_name string, db_expr &ast.Expr) { db_expr_type := g.get_db_type(db_expr) or { verror('V ORM: unknown db type for ${db_expr}') } @@ -884,7 +865,7 @@ fn (mut g Gen) write_orm_connection_init(connection_var_name string, db_expr &as reference_sign := if is_pointer { '' } else { '&' } db_ctype_name = db_ctype_name.trim_right('*') - g.writeln('// orm') + g.writeln('// V ORM') g.write('orm__Connection ${connection_var_name} = ') if db_ctype_name == 'orm__Connection' { diff --git a/vlib/v/parser/sql.v b/vlib/v/parser/sql.v index 654b3ec2d6..8569bcb557 100644 --- a/vlib/v/parser/sql.v +++ b/vlib/v/parser/sql.v @@ -85,9 +85,10 @@ fn (mut p Parser) sql_expr() ast.Expr { p.inside_match = false or_expr := p.parse_sql_or_block() p.inside_match = tmp_inside_match + return ast.SqlExpr{ is_count: is_count - typ: typ + typ: typ.set_flag(.result) or_expr: or_expr db_expr: db_expr where_expr: where_expr @@ -100,6 +101,7 @@ fn (mut p Parser) sql_expr() ast.Expr { order_expr: order_expr has_desc: has_desc is_array: if is_count { false } else { true } + is_generated: false pos: pos.extend(p.prev_tok.pos()) table_expr: ast.TypeNode{ typ: table_type @@ -149,21 +151,11 @@ fn (mut p Parser) parse_sql_or_block() ast.OrExpr { mut pos := p.tok.pos() if p.tok.kind == .key_orelse { - was_inside_or_expr := p.inside_or_expr - p.inside_or_expr = true - p.next() - p.open_scope() - p.scope.register(ast.Var{ - name: 'err' - typ: ast.error_type - pos: p.tok.pos() - is_used: true - }) kind = .block - stmts = p.parse_block_no_scope(false) - pos = pos.extend(p.prev_tok.pos()) - p.close_scope() - p.inside_or_expr = was_inside_or_expr + stmts, pos = p.or_block(.with_err_var) + } else if p.tok.kind == .not { + kind = .propagate_result + p.next() } return ast.OrExpr{ @@ -198,6 +190,7 @@ fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine { pos: typ_pos } scope: p.scope + is_generated: false } } else if n == 'drop' { kind = .drop @@ -215,6 +208,7 @@ fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine { typ: typ pos: typ_pos } + is_generated: false scope: p.scope } } @@ -285,14 +279,14 @@ fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine { update_exprs: update_exprs kind: kind where_expr: where_expr - is_top_level: true + is_generated: false scope: p.scope } } fn (mut p Parser) check_sql_keyword(name string) ?bool { if p.check_name() != name { - p.error('orm: expecting `${name}`') + p.error('V ORM: expecting `${name}`') return none } return true diff --git a/vlib/v/parser/tests/orm_no_error_handler.out b/vlib/v/parser/tests/orm_no_error_handler.out new file mode 100644 index 0000000000..9e0cb75124 --- /dev/null +++ b/vlib/v/parser/tests/orm_no_error_handler.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/orm_no_error_handler.vv:8:7: error: V ORM returns a result, so it should have either an `or {}` block, or `!` at the end + 6 | db := true + 7 | + 8 | _ := sql db { + | ~~~~~~~~ + 9 | select from User + 10 | } diff --git a/vlib/v/parser/tests/orm_no_error_handler.vv b/vlib/v/parser/tests/orm_no_error_handler.vv new file mode 100644 index 0000000000..cfaa5fcad4 --- /dev/null +++ b/vlib/v/parser/tests/orm_no_error_handler.vv @@ -0,0 +1,11 @@ +struct User { + id int +} + +fn main() { + db := true + + _ := sql db { + select from User + } +} diff --git a/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.out b/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.out index 2a55dfb327..2aa58f1a83 100644 --- a/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.out +++ b/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.out @@ -3,5 +3,5 @@ vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.vv:13:73: error: un 12 | _ := sql _ { 13 | select from User where (age > 18) && (city == 'London' || username == username) | ~~~~~~~~ - 14 | } + 14 | }! 15 | } diff --git a/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.vv b/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.vv index 574fca226a..d92412f5ee 100644 --- a/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.vv +++ b/vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.vv @@ -11,5 +11,5 @@ mut: fn main() { _ := sql _ { select from User where (age > 18) && (city == 'London' || username == username) - } + }! } 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 index 12362174ee..3aa255f21a 100644 --- 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 @@ -1,5 +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 + message: 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:17 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 index e4aa1c2c06..9ff1cae0df 100644 --- 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 @@ -14,5 +14,5 @@ fn main() { 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 index d26f191f06..5b1d8dad77 100644 --- 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 @@ -1,5 +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 + message: 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:13 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 index 23dddda0c0..d0fb37dc87 100644 --- 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 @@ -10,7 +10,7 @@ fn main() { users := sql db { select from User - } + }! println(users) } diff --git a/vlib/v/tests/orm_joined_tables_select_test.v b/vlib/v/tests/orm_joined_tables_select_test.v index 071b25cd4e..f0fbc29fd1 100644 --- a/vlib/v/tests/orm_joined_tables_select_test.v +++ b/vlib/v/tests/orm_joined_tables_select_test.v @@ -19,7 +19,7 @@ pub mut: arch []GitRepoArch [fkey: 'repo_id'] } -pub fn (db &VieterDb) get_git_repos() []GitRepo { +pub fn (db &VieterDb) get_git_repos() ![]GitRepo { // NB: the query here, uses the `repo` field on GitRepo, // while GitRepo is joined to GitRepoArch, // which does *not* have a `repo` field. @@ -28,7 +28,7 @@ pub fn (db &VieterDb) get_git_repos() []GitRepo { // a lingering c.cur_orm_ts state. The fix was to save/restore c.cur_orm_ts . res := sql db.conn { select from GitRepo where repo == 'something' order by id - } + }! return res } diff --git a/vlib/v/tests/orm_sub_array_struct_test.v b/vlib/v/tests/orm_sub_array_struct_test.v index b547d3329a..15c78b3d88 100644 --- a/vlib/v/tests/orm_sub_array_struct_test.v +++ b/vlib/v/tests/orm_sub_array_struct_test.v @@ -17,10 +17,10 @@ fn test_orm_array() { mut db := sqlite.connect(':memory:') or { panic(err) } sql db { create table Parent - } + }! sql db { create table Child - } + }! par := Parent{ name: 'test' @@ -36,15 +36,15 @@ fn test_orm_array() { sql db { insert par into Parent - } + }! parents := sql db { select from Parent where id == 1 - } + }! sql db { drop table Parent - } + }! parent := parents.first() assert parent.name == par.name @@ -57,10 +57,10 @@ fn test_orm_relationship() { mut db := sqlite.connect(':memory:') or { panic(err) } sql db { create table Parent - } + }! sql db { create table Child - } + }! mut child := Child{ name: 'abc' @@ -73,11 +73,11 @@ fn test_orm_relationship() { sql db { insert par into Parent - } + }! mut parents := sql db { select from Parent where id == 1 - } + }! mut parent := parents.first() child.parent_id = parent.id @@ -85,20 +85,20 @@ fn test_orm_relationship() { sql db { insert child into Child - } + }! child.name = 'bacon' sql db { insert child into Child - } + }! assert parent.name == par.name assert parent.children.len == 0 parents = sql db { select from Parent where id == 1 - } + }! parent = parents.first() assert parent.name == par.name @@ -108,17 +108,17 @@ fn test_orm_relationship() { mut children := sql db { select from Child - } + }! assert children.len == 2 sql db { drop table Parent - } + }! children = sql db { select from Child - } + }! 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 2110b76a9e..f47cdf97f1 100644 --- a/vlib/v/tests/orm_sub_struct_test.v +++ b/vlib/v/tests/orm_sub_struct_test.v @@ -14,10 +14,10 @@ fn test_orm_sub_structs() { db := sqlite.connect(':memory:') or { panic(err) } sql db { create table Upper - } + }! sql db { create table SubStruct - } + }! upper_1 := Upper{ sub: SubStruct{ @@ -27,11 +27,11 @@ fn test_orm_sub_structs() { sql db { insert upper_1 into Upper - } + }! uppers := sql db { select from Upper where id == 1 - } + }! assert uppers.first().sub.name == upper_1.sub.name } diff --git a/vlib/v/tests/sql_statement_inside_fn_call_test.v b/vlib/v/tests/sql_statement_inside_fn_call_test.v index 88eebe00db..d3fafc184a 100644 --- a/vlib/v/tests/sql_statement_inside_fn_call_test.v +++ b/vlib/v/tests/sql_statement_inside_fn_call_test.v @@ -13,12 +13,12 @@ fn test_sql_statement_inside_fn_call() { db := sqlite.connect(':memory:') or { panic('failed') } sql db { create table Movie - } + }! m := Movie{1, 'Maria'} sql db { insert m into Movie - } - dump(x((sql db { + }! + dump(x(sql db { select from Movie where id == 1 - }).first())) + }!.first())) } diff --git a/vlib/vweb/vweb_app_test.v b/vlib/vweb/vweb_app_test.v index 941e0016ce..084973b091 100644 --- a/vlib/vweb/vweb_app_test.v +++ b/vlib/vweb/vweb_app_test.v @@ -44,7 +44,7 @@ pub fn (mut app App) new_article() vweb.Result { println(article) sql app.db { insert article into Article - } + } or {} return app.redirect('/') }