| 1 | // vtest retry: 3 |
| 2 | // vtest build: present_sqlite3? && !windows |
| 3 | // import db.mysql |
| 4 | // import db.pg |
| 5 | import orm |
| 6 | import time |
| 7 | import db.sqlite |
| 8 | |
| 9 | const offset_const = 2 |
| 10 | |
| 11 | @[index: 'name, nr_downloads'] |
| 12 | struct Module { |
| 13 | id int @[primary; sql: serial] |
| 14 | name string @[index] |
| 15 | nr_downloads int |
| 16 | test_id u64 @[index] |
| 17 | user ?User |
| 18 | created time.Time |
| 19 | } |
| 20 | |
| 21 | @[table: 'userlist'] |
| 22 | struct User { |
| 23 | id int @[primary; sql: serial] |
| 24 | age int |
| 25 | name string @[default: 'name'; sql: 'username'] |
| 26 | is_customer bool |
| 27 | skipped_string string @[skip] |
| 28 | skipped_string2 string @[sql: '-'] |
| 29 | skipped_array []string @[skip] |
| 30 | skipped_array2 []string @[sql: '-'] |
| 31 | } |
| 32 | |
| 33 | struct Foo { |
| 34 | age int |
| 35 | } |
| 36 | |
| 37 | struct TestTime { |
| 38 | id int @[primary; sql: serial] |
| 39 | create time.Time |
| 40 | } |
| 41 | |
| 42 | struct SelectTransformUser { |
| 43 | id int @[primary; sql: serial] |
| 44 | name string @[sql_select: 'upper(name)'] |
| 45 | } |
| 46 | |
| 47 | fn test_use_struct_field_as_limit() { |
| 48 | db := sqlite.connect(':memory:') or { panic(err) } |
| 49 | |
| 50 | sql db { |
| 51 | create table User |
| 52 | }! |
| 53 | |
| 54 | foo := Foo{ |
| 55 | age: 10 |
| 56 | } |
| 57 | |
| 58 | sam := User{ |
| 59 | age: 29 |
| 60 | name: 'Sam' |
| 61 | skipped_string2: 'this should be ignored' |
| 62 | skipped_array: ['ignored', 'array'] |
| 63 | skipped_array2: ['another', 'ignored', 'array'] |
| 64 | } |
| 65 | |
| 66 | sql db { |
| 67 | insert sam into User |
| 68 | }! |
| 69 | |
| 70 | users := sql db { |
| 71 | select from User limit foo.age |
| 72 | }! |
| 73 | |
| 74 | assert users.len == 1 |
| 75 | assert users[0].name == 'Sam' |
| 76 | assert users[0].age == 29 |
| 77 | assert users[0].skipped_string == '' |
| 78 | assert users[0].skipped_string2 == '' |
| 79 | assert users[0].skipped_array == [], 'skipped because of the @[skip] tag, used for both sql and json' |
| 80 | assert users[0].skipped_array2 == [], "should be skipped, because of the sql specific @[sql: '-'] tag" |
| 81 | } |
| 82 | |
| 83 | fn test_orm_sql_select_attribute() { |
| 84 | mut db := sqlite.connect(':memory:') or { panic(err) } |
| 85 | defer { |
| 86 | db.close() or {} |
| 87 | } |
| 88 | |
| 89 | sql db { |
| 90 | create table SelectTransformUser |
| 91 | }! |
| 92 | |
| 93 | alice := SelectTransformUser{ |
| 94 | name: 'Alice' |
| 95 | } |
| 96 | |
| 97 | sql db { |
| 98 | insert alice into SelectTransformUser |
| 99 | }! |
| 100 | |
| 101 | rows := sql db { |
| 102 | select from SelectTransformUser where id == 1 |
| 103 | }! |
| 104 | |
| 105 | assert rows.len == 1 |
| 106 | assert rows[0].name == 'ALICE' |
| 107 | |
| 108 | mut qb := orm.new_query[SelectTransformUser](db) |
| 109 | qb_rows := qb.query()! |
| 110 | |
| 111 | assert qb_rows.len == 1 |
| 112 | assert qb_rows[0].name == 'ALICE' |
| 113 | } |
| 114 | |
| 115 | fn test_orm_select_specific_fields() { |
| 116 | mut db := sqlite.connect(':memory:') or { panic(err) } |
| 117 | defer { |
| 118 | db.close() or {} |
| 119 | } |
| 120 | |
| 121 | sql db { |
| 122 | create table User |
| 123 | create table SelectTransformUser |
| 124 | }! |
| 125 | |
| 126 | sam := User{ |
| 127 | age: 29 |
| 128 | name: 'Sam' |
| 129 | } |
| 130 | alice := SelectTransformUser{ |
| 131 | name: 'Alice' |
| 132 | } |
| 133 | |
| 134 | sql db { |
| 135 | insert sam into User |
| 136 | insert alice into SelectTransformUser |
| 137 | }! |
| 138 | |
| 139 | users := sql db { |
| 140 | select id, name from User where name == 'Sam' |
| 141 | }! |
| 142 | |
| 143 | assert users.len == 1 |
| 144 | assert users[0].id == 1 |
| 145 | assert users[0].name == 'Sam' |
| 146 | assert users[0].age == 0 |
| 147 | assert users[0].is_customer == false |
| 148 | |
| 149 | transformed := sql db { |
| 150 | select name from SelectTransformUser where id == 1 |
| 151 | }! |
| 152 | |
| 153 | assert transformed.len == 1 |
| 154 | assert transformed[0].name == 'ALICE' |
| 155 | assert transformed[0].id == 0 |
| 156 | } |
| 157 | |
| 158 | fn test_orm_order_by_explicit_asc() { |
| 159 | mut db := sqlite.connect(':memory:')! |
| 160 | defer { |
| 161 | db.close() or { panic(err) } |
| 162 | } |
| 163 | |
| 164 | sql db { |
| 165 | create table User |
| 166 | }! |
| 167 | |
| 168 | users := [ |
| 169 | User{ |
| 170 | age: 31 |
| 171 | name: 'Alice' |
| 172 | }, |
| 173 | User{ |
| 174 | age: 19 |
| 175 | name: 'Bob' |
| 176 | }, |
| 177 | User{ |
| 178 | age: 44 |
| 179 | name: 'Charlie' |
| 180 | }, |
| 181 | ] |
| 182 | |
| 183 | for user in users { |
| 184 | sql db { |
| 185 | insert user into User |
| 186 | }! |
| 187 | } |
| 188 | |
| 189 | // vfmt off |
| 190 | rows := sql db { |
| 191 | select from User order by age asc limit 2 |
| 192 | }! |
| 193 | // vfmt on |
| 194 | |
| 195 | assert rows.len == 2 |
| 196 | assert rows[0].name == 'Bob' |
| 197 | assert rows[0].age == 19 |
| 198 | assert rows[1].name == 'Alice' |
| 199 | assert rows[1].age == 31 |
| 200 | } |
| 201 | |
| 202 | fn test_orm() { |
| 203 | db := sqlite.connect(':memory:') or { panic(err) } |
| 204 | |
| 205 | sql db { |
| 206 | create table Module |
| 207 | }! |
| 208 | sql db { |
| 209 | create table User |
| 210 | }! |
| 211 | |
| 212 | name := 'Peter' |
| 213 | |
| 214 | sam := User{ |
| 215 | age: 29 |
| 216 | name: 'Sam' |
| 217 | } |
| 218 | |
| 219 | peter := User{ |
| 220 | age: 31 |
| 221 | name: 'Peter' |
| 222 | } |
| 223 | |
| 224 | k := User{ |
| 225 | age: 30 |
| 226 | name: 'Kate' |
| 227 | is_customer: true |
| 228 | } |
| 229 | |
| 230 | sql db { |
| 231 | insert sam into User |
| 232 | insert peter into User |
| 233 | insert k into User |
| 234 | }! |
| 235 | |
| 236 | c := sql db { |
| 237 | select count from User where id != 1 |
| 238 | }! |
| 239 | assert c == 2 |
| 240 | |
| 241 | nr_all_users := sql db { |
| 242 | select count from User |
| 243 | }! |
| 244 | assert nr_all_users == 3 |
| 245 | |
| 246 | nr_users1 := sql db { |
| 247 | select count from User where id == 1 |
| 248 | }! |
| 249 | assert nr_users1 == 1 |
| 250 | |
| 251 | nr_peters := sql db { |
| 252 | select count from User where id == 2 && name == 'Peter' |
| 253 | }! |
| 254 | assert nr_peters == 1 |
| 255 | |
| 256 | nr_peters2 := sql db { |
| 257 | select count from User where id == 2 && name == name |
| 258 | }! |
| 259 | assert nr_peters2 == 1 |
| 260 | |
| 261 | nr_peters3 := sql db { |
| 262 | select count from User where name == name |
| 263 | }! |
| 264 | assert nr_peters3 == 1 |
| 265 | |
| 266 | peters := sql db { |
| 267 | select from User where name == name |
| 268 | }! |
| 269 | assert peters.len == 1 |
| 270 | assert peters[0].name == 'Peter' |
| 271 | |
| 272 | mut users := sql db { |
| 273 | select from User where name == name limit 1 |
| 274 | }! |
| 275 | |
| 276 | one_peter := users.first() |
| 277 | assert one_peter.name == 'Peter' |
| 278 | assert one_peter.id == 2 |
| 279 | |
| 280 | users = sql db { |
| 281 | select from User where id == 1 |
| 282 | }! |
| 283 | |
| 284 | user := users.first() |
| 285 | assert user.name == 'Sam' |
| 286 | assert user.id == 1 |
| 287 | assert user.age == 29 |
| 288 | |
| 289 | users = sql db { |
| 290 | select from User where id > 0 |
| 291 | }! |
| 292 | assert users.len == 3 |
| 293 | assert users[0].name == 'Sam' |
| 294 | assert users[1].name == 'Peter' |
| 295 | assert users[1].age == 31 |
| 296 | |
| 297 | users2 := sql db { |
| 298 | select from User where id < 0 |
| 299 | }! |
| 300 | assert users2.len == 0 |
| 301 | |
| 302 | users3 := sql db { |
| 303 | select from User where age == 29 || age == 31 |
| 304 | }! |
| 305 | |
| 306 | assert users3.len == 2 |
| 307 | assert users3[0].age == 29 |
| 308 | assert users3[1].age == 31 |
| 309 | |
| 310 | new_user := User{ |
| 311 | name: 'New user' |
| 312 | age: 30 |
| 313 | } |
| 314 | sql db { |
| 315 | insert new_user into User |
| 316 | }! |
| 317 | |
| 318 | users = sql db { |
| 319 | select from User where id == 4 |
| 320 | }! |
| 321 | |
| 322 | x := users.first() |
| 323 | assert x.age == 30 |
| 324 | assert x.id == 4 |
| 325 | assert x.name == 'New user' |
| 326 | |
| 327 | users = sql db { |
| 328 | select from User where id == 3 |
| 329 | }! |
| 330 | |
| 331 | kate := users.first() |
| 332 | assert kate.is_customer == true |
| 333 | |
| 334 | users = sql db { |
| 335 | select from User where is_customer == true limit 1 |
| 336 | }! |
| 337 | |
| 338 | customer := users.first() |
| 339 | assert customer.is_customer == true |
| 340 | assert customer.name == 'Kate' |
| 341 | |
| 342 | sql db { |
| 343 | update User set age = 31 where name == 'Kate' |
| 344 | }! |
| 345 | |
| 346 | users = sql db { |
| 347 | select from User where id == 3 |
| 348 | }! |
| 349 | kate2 := users.first() |
| 350 | assert kate2.age == 31 |
| 351 | assert kate2.name == 'Kate' |
| 352 | |
| 353 | sql db { |
| 354 | update User set age = 32, name = 'Kate N' where name == 'Kate' |
| 355 | }! |
| 356 | |
| 357 | users = sql db { |
| 358 | select from User where id == 3 |
| 359 | }! |
| 360 | mut kate3 := users.first() |
| 361 | assert kate3.age == 32 |
| 362 | assert kate3.name == 'Kate N' |
| 363 | |
| 364 | new_age := 33 |
| 365 | sql db { |
| 366 | update User set age = new_age, name = 'Kate N' where id == 3 |
| 367 | }! |
| 368 | |
| 369 | users = sql db { |
| 370 | select from User where id == 3 |
| 371 | }! |
| 372 | |
| 373 | kate3 = users.first() |
| 374 | assert kate3.age == 33 |
| 375 | assert kate3.name == 'Kate N' |
| 376 | |
| 377 | foo := Foo{34} |
| 378 | sql db { |
| 379 | update User set age = foo.age, name = 'Kate N' where id == 3 |
| 380 | }! |
| 381 | |
| 382 | users = sql db { |
| 383 | select from User where id == 3 |
| 384 | }! |
| 385 | kate3 = users.first() |
| 386 | assert kate3.age == 34 |
| 387 | assert kate3.name == 'Kate N' |
| 388 | |
| 389 | no_user := sql db { |
| 390 | select from User where id == 30 |
| 391 | }! |
| 392 | |
| 393 | assert no_user.len == 0 |
| 394 | |
| 395 | two_users := sql db { |
| 396 | select from User limit 2 |
| 397 | }! |
| 398 | assert two_users.len == 2 |
| 399 | assert two_users[0].id == 1 |
| 400 | |
| 401 | y := sql db { |
| 402 | select from User limit 2 offset 1 |
| 403 | }! |
| 404 | assert y.len == 2 |
| 405 | assert y[0].id == 2 |
| 406 | |
| 407 | z := sql db { |
| 408 | select from User order by id limit 2 offset offset_const |
| 409 | }! |
| 410 | assert z.len == 2 |
| 411 | assert z[0].id == 3 |
| 412 | |
| 413 | users = sql db { |
| 414 | select from User order by age desc limit 1 |
| 415 | }! |
| 416 | |
| 417 | oldest := users.first() |
| 418 | assert oldest.age == 34 |
| 419 | |
| 420 | offs := 1 |
| 421 | users = sql db { |
| 422 | select from User order by age desc limit 1 offset offs |
| 423 | }! |
| 424 | |
| 425 | second_oldest := users.first() |
| 426 | assert second_oldest.age == 31 |
| 427 | sql db { |
| 428 | delete from User where age == 34 |
| 429 | }! |
| 430 | |
| 431 | users = sql db { |
| 432 | select from User order by age desc limit 1 |
| 433 | }! |
| 434 | updated_oldest := users.first() |
| 435 | assert updated_oldest.age == 31 |
| 436 | |
| 437 | // Remove this when pg is used |
| 438 | // db.exec('insert into User (name, age) values (NULL, 31)') |
| 439 | users = sql db { |
| 440 | select from User where id == 5 |
| 441 | }! |
| 442 | assert users.len == 0 |
| 443 | |
| 444 | users = sql db { |
| 445 | select from User where id == 1 |
| 446 | }! |
| 447 | age_test := users.first() |
| 448 | |
| 449 | assert age_test.age == 29 |
| 450 | |
| 451 | sql db { |
| 452 | update User set age = age + 1 where id == 1 |
| 453 | }! |
| 454 | |
| 455 | users = sql db { |
| 456 | select from User where id == 1 |
| 457 | }! |
| 458 | |
| 459 | mut first := users.first() |
| 460 | assert first.age == 30 |
| 461 | |
| 462 | sql db { |
| 463 | update User set age = age * 2 where id == 1 |
| 464 | }! |
| 465 | |
| 466 | users = sql db { |
| 467 | select from User where id == 1 |
| 468 | }! |
| 469 | |
| 470 | first = users.first() |
| 471 | assert first.age == 60 |
| 472 | |
| 473 | sql db { |
| 474 | create table TestTime |
| 475 | }! |
| 476 | |
| 477 | tnow := time.now() |
| 478 | |
| 479 | time_test := TestTime{ |
| 480 | create: tnow |
| 481 | } |
| 482 | |
| 483 | sql db { |
| 484 | insert time_test into TestTime |
| 485 | }! |
| 486 | |
| 487 | data := sql db { |
| 488 | select from TestTime where create == tnow |
| 489 | }! |
| 490 | |
| 491 | assert data.len == 1 |
| 492 | assert tnow.unix() == data[0].create.unix() |
| 493 | |
| 494 | mod := Module{} |
| 495 | |
| 496 | sql db { |
| 497 | insert mod into Module |
| 498 | }! |
| 499 | |
| 500 | sql db { |
| 501 | update Module set test_id = 11 where id == 1 |
| 502 | }! |
| 503 | |
| 504 | mut modules := sql db { |
| 505 | select from Module where id == 1 |
| 506 | }! |
| 507 | |
| 508 | assert modules.first().test_id == 11 |
| 509 | |
| 510 | t := time.now() |
| 511 | sql db { |
| 512 | update Module set created = t where id == 1 |
| 513 | }! |
| 514 | |
| 515 | modules = sql db { |
| 516 | select from Module where id == 1 |
| 517 | }! |
| 518 | |
| 519 | // Note: usually updated_time_mod.created != t, because t has |
| 520 | // its microseconds set, while the value retrieved from the DB |
| 521 | // has them zeroed, because the db field resolution is seconds. |
| 522 | // Note: the database also stores the time in UTC, so the |
| 523 | // comparison must be done on the unix timestamp. |
| 524 | assert modules.first().created.unix() == t.unix() |
| 525 | |
| 526 | users = sql db { |
| 527 | select from User where (name == 'Sam' && is_customer == true) || id == 1 |
| 528 | }! |
| 529 | |
| 530 | assert users.first() == first |
| 531 | |
| 532 | sql db { |
| 533 | drop table Module |
| 534 | drop table TestTime |
| 535 | }! |
| 536 | } |
| 537 | |
| 538 | fn test_distinct() { |
| 539 | db := sqlite.connect(':memory:') or { panic(err) } |
| 540 | |
| 541 | // Create table without unique constraints to allow true duplicates |
| 542 | db.exec('CREATE TABLE items (name TEXT, category TEXT)')! |
| 543 | db.exec("INSERT INTO items VALUES ('Apple', 'Fruit'), ('Apple', 'Fruit'), ('Banana', 'Fruit')")! |
| 544 | |
| 545 | without_distinct := db.exec('SELECT name, category FROM items')! |
| 546 | assert without_distinct.len == 3 |
| 547 | |
| 548 | with_distinct := db.exec('SELECT DISTINCT name, category FROM items')! |
| 549 | assert with_distinct.len == 2 |
| 550 | } |
| 551 | |