mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
532 lines
12 KiB
V
532 lines
12 KiB
V
// vtest retry: 2
|
|
import orm
|
|
import db.sqlite
|
|
import time
|
|
|
|
@[table: 'sys_users']
|
|
struct User {
|
|
id int @[primary; serial]
|
|
name string
|
|
age int
|
|
role string
|
|
status int
|
|
salary int
|
|
title string
|
|
score int
|
|
created_at ?time.Time @[sql_type: 'TIMESTAMP']
|
|
updated_at time.Time @[sql_type: 'TIMESTAMP']
|
|
type_i8 i8
|
|
type_i16 i16
|
|
type_int int
|
|
type_i64 i64
|
|
type_u8 u8
|
|
type_u16 u16
|
|
type_u32 u32
|
|
type_u64 u64
|
|
type_f32 f32
|
|
type_f64 f64
|
|
type_bool bool
|
|
type_string string
|
|
option_i8 ?i8
|
|
option_i16 ?i16
|
|
option_int ?int
|
|
option_i64 ?i64
|
|
option_u8 ?u8
|
|
option_u16 ?u16
|
|
option_u32 ?u32
|
|
option_u64 ?u64
|
|
option_f32 ?f32
|
|
option_f64 ?f64
|
|
option_bool ?bool
|
|
option_string ?string
|
|
}
|
|
|
|
// UserPart is part of User, so we can access only part of the `sys_users` table
|
|
// note: for test, we modify `created_at` field from option to require
|
|
// a `null` value in database, will map to default value of the require field in struct
|
|
@[table: 'sys_users']
|
|
struct UserPart {
|
|
id int @[primary; serial]
|
|
name string
|
|
created_at time.Time @[sql_type: 'TIMESTAMP']
|
|
updated_at time.Time @[sql_type: 'TIMESTAMP']
|
|
option_i8 ?i8 = 13 // option with default test
|
|
option_string ?string = 'this is not none'
|
|
}
|
|
|
|
fn test_orm_func_where() {
|
|
mut db := sqlite.connect(':memory:')!
|
|
defer { db.close() or {} }
|
|
mut qb := orm.new_query[User](db)
|
|
|
|
// single_condition
|
|
qb.reset()
|
|
qb.where('age > ?', 25)!
|
|
assert qb.where.fields == ['age']
|
|
assert qb.where.kinds == [.gt]
|
|
assert qb.where.data == [25]
|
|
|
|
// chain_condition
|
|
qb.reset()
|
|
qb.where('age > ?', 25)!.where('salary < ?', 1000)!
|
|
assert qb.where.fields == ['age', 'salary']
|
|
assert qb.where.kinds == [.gt, .lt]
|
|
assert qb.where.data == [25, 1000]
|
|
|
|
// and_or_combination
|
|
qb.reset()
|
|
qb.where('name = ? AND status = ? OR role = ? || id = ? && title = ?', 'Alice', 1,
|
|
'admin', 1, 'st')!
|
|
assert qb.where.fields == ['name', 'status', 'role', 'id', 'title']
|
|
assert qb.where.kinds == [.eq, .eq, .eq, .eq, .eq]
|
|
assert qb.where.is_and == [true, false, false, true]
|
|
|
|
// nested_parentheses
|
|
qb.reset()
|
|
qb.where('(salary >= ? AND (age <= ? OR title LIKE ?))', 50000, 35, '%Manager%')!
|
|
assert qb.where.parentheses == [[1, 2], [0, 2]]
|
|
|
|
// complex_nesting
|
|
qb.reset()
|
|
qb.where('((age = ? OR (salary > ? AND id < ?)) AND (name LIKE ?))', 1, 2, 3, '%test%')!
|
|
assert qb.where.parentheses == [[1, 2], [0, 2], [3, 3], [0, 3]]
|
|
|
|
// in and not in
|
|
qb.reset()
|
|
qb.where('name IN ? AND age NOT IN ?', ['Tom'], [2])!
|
|
assert qb.where.fields == ['name', 'age']
|
|
assert qb.where.kinds == [.in, .not_in]
|
|
}
|
|
|
|
fn test_orm_func_stmts() {
|
|
users := [
|
|
User{
|
|
name: 'Tom'
|
|
age: 30
|
|
role: 'admin'
|
|
status: 1
|
|
salary: 5000
|
|
title: 'manager'
|
|
score: 90
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'Alice'
|
|
age: 20
|
|
role: 'employee'
|
|
status: 2
|
|
salary: 2000
|
|
title: 'doctor'
|
|
score: 95
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'Mars'
|
|
age: 40
|
|
role: 'employer'
|
|
status: 3
|
|
salary: 1000
|
|
title: 'doctor'
|
|
score: 85
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'Kitty'
|
|
age: 18
|
|
role: 'employer'
|
|
status: 1
|
|
salary: 1500
|
|
title: 'doctor'
|
|
score: 87
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'Silly'
|
|
age: 27
|
|
role: 'employer'
|
|
status: 5
|
|
salary: 2500
|
|
title: 'doctor'
|
|
score: 81
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
// option_string: 'hello' // option with default test
|
|
},
|
|
User{
|
|
name: 'Smith'
|
|
age: 37
|
|
role: 'employer'
|
|
status: 1
|
|
salary: 4500
|
|
title: 'doctor'
|
|
score: 89
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'Bob'
|
|
age: 26
|
|
role: 'employer'
|
|
status: 2
|
|
salary: 6500
|
|
title: 'doctor'
|
|
score: 81
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'Peter'
|
|
age: 29
|
|
role: 'employer'
|
|
status: 1
|
|
salary: 3500
|
|
title: 'doctor'
|
|
score: 80
|
|
created_at: time.now()
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
// option_i8: 1 // option with default test
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'See'
|
|
age: 45
|
|
role: 'employer'
|
|
status: 2
|
|
salary: 8500
|
|
title: 'doctor'
|
|
score: 82
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
User{
|
|
name: 'John'
|
|
age: 42
|
|
role: 'employer'
|
|
status: 1
|
|
salary: 10000
|
|
title: 'doctor'
|
|
score: 88
|
|
updated_at: time.now()
|
|
type_i8: 1
|
|
type_i16: 2
|
|
type_int: 3
|
|
type_i64: 4
|
|
type_u8: 5
|
|
type_u16: 6
|
|
type_u32: 7
|
|
type_u64: 8
|
|
type_f32: 1.1
|
|
type_f64: 2.2
|
|
type_bool: true
|
|
type_string: 'hello'
|
|
option_i8: 1
|
|
option_i16: 2
|
|
option_int: 3
|
|
option_i64: 4
|
|
option_u8: 5
|
|
option_u16: 6
|
|
option_u32: 7
|
|
option_u64: 8
|
|
option_f32: 1.1
|
|
option_f64: 2.2
|
|
option_bool: true
|
|
option_string: 'hello'
|
|
},
|
|
]
|
|
mut db := sqlite.connect(':memory:')!
|
|
defer { db.close() or {} }
|
|
mut qb := orm.new_query[User](db)
|
|
|
|
// create table
|
|
qb.create()!
|
|
|
|
// insert many records
|
|
qb.insert_many(users)!
|
|
|
|
// select count(*)
|
|
mut count := qb.count()!
|
|
|
|
// last_id
|
|
mut last_id := qb.last_id()
|
|
assert count == last_id
|
|
assert count == users.len
|
|
|
|
// insert a single record
|
|
qb.insert(users[0])!
|
|
|
|
// select * from table
|
|
all_users := qb.query()!
|
|
assert all_users.len == users.len + 1
|
|
|
|
// select `name` from table
|
|
only_names := qb.select('name')!.query()!
|
|
assert only_names[0].name != ''
|
|
assert only_names[0].id == 0
|
|
assert only_names[0].age == 0
|
|
assert only_names[0].role == ''
|
|
assert only_names[0].status == 0
|
|
assert only_names[0].salary == 0
|
|
assert only_names[0].title == ''
|
|
assert only_names[0].score == 0
|
|
assert only_names[0].created_at == none
|
|
|
|
// update
|
|
qb.set('age = ?, title = ?', 71, 'boss')!.where('name = ?', 'John')!.update()!
|
|
john := qb.where('name = ?', 'John')!.query()!
|
|
assert john[0].name == 'John'
|
|
assert john[0].age == 71
|
|
assert john[0].title == 'boss'
|
|
|
|
// delete
|
|
qb.where('name = ?', 'John')!.delete()!
|
|
no_john := qb.where('name = ?', 'John')!.query()!
|
|
assert no_john.len == 0
|
|
|
|
// complex select
|
|
selected_users := qb.where('created_at IS NULL && ((salary > ? && age < ?) || (role LIKE ?))',
|
|
2000, 30, '%employee%')!.query()!
|
|
assert selected_users[0].name == 'Silly'
|
|
assert selected_users.len == 1
|
|
|
|
// chain where
|
|
and_where := qb.where('salary > ?', 2000)!.where('age > ?', 40)!.query()!
|
|
assert and_where.len == 1
|
|
or_where := qb.where('salary > ?', 2000)!.or_where('age > ? OR score > ?', 40, 85)!.query()!
|
|
assert or_where.len == 9
|
|
|
|
// chain calls
|
|
final_users := qb
|
|
.drop()!
|
|
.create()!
|
|
.insert_many(users)!
|
|
.set('name = ?', 'haha')!.where('name = ?', 'Tom')!.update()!
|
|
.where('age >= ?', 30)!.delete()!
|
|
.order(.asc, 'age')!
|
|
.limit(100)!
|
|
.query()!
|
|
assert final_users.len == 5
|
|
assert final_users[0].age == 18
|
|
|
|
// access only part of the table
|
|
mut part := orm.new_query[UserPart](db)
|
|
part_user := part.query()!
|
|
// a `null` value in database, will map to default value of the require field in struct
|
|
assert part_user.filter(it.name == 'Silly')[0].created_at == time.Time{}
|
|
assert part_user.len == 5
|
|
}
|