mirror of
https://github.com/vlang/v.git
synced 2025-09-14 06:52:36 +03:00

* orm: added is none and !is none handling * orm: added NullType, support option fields and deprecate [nonull] Nullable DB fields are now determined by corresponding option struct field. The [nonull] attribute is deprecated and fields are all NOT NULL now, unless they are option fields. New orm primitive, NullType, added to support passing none values to db backends, which have been updated to support it. Also, empty string and 0 numberic values are no longer skipped during insert (since they may be valid values). * orm: fix [nonull] deprecation warning * orm: add null handling to update and select also, improved formatting for orm cgen, and removed optimised operand handling of orm `is` and `!is` operators * sqlite: read/report NULLs using new orm NullType * postgres: returning data primitives now returns new orm.NullType * orm: initialise NullType Primitives properly * orm: do not smart cast operands inside sql * orm: fix bad setting of option value * orm: improve orm_null_test.v, adding/fixing selects * orm: cleanup: rename NullType->Null, use serial const, cgen output * orm: handle automatically generated fields more explicitly During insert, fields which are * [sql: serial] * [default: whatever] and where the data is a default value (e.g., 0, ""), those fields are not sent to the db, so that the db can generate auto-increment or default values. (This was previously done only for [primary] fields, and not in all circumstances, but that is not correct -- primary and serial/auto-increment fields are differnet.) * orm: udpated README * orm: select cgen fixes: read from uninit res; fail to init res * orm: udpated tests * orm: fix option sub-struct fields * orm: fixed joins to option structs Changed orm.write_orm_select() so that you pass to it the name of a resut variable which it populates with the result (or not) and changed use of it in sql_select_expr() and calls in write_orm_select() to populate substructs. * orm: fix pg driver handling of NULL results * orm: move runtime checks to comptime checker; cache checked tables * orm: vfmt :( * orm: markdown formatting * orm: renamed orm.time_ and orm.enum_; updated db drivers * checker: updated orm tests * orm: fix issue setting up ast option values as orm primitives * checker: ORM use of none/options and operations (added tests) * orm: fixed tests * db: clean code * examples: remove orm nonull attributes * orm: skip test memory santisation for orm_null_test.v * orm: make the type-to-primitive converstion fns not public * orm: mv object var c-code from checker->cgen; fix memory corruption Code in checker/orm.v used the SqlStmtLine object field name to store c-specific referenecs to option and array fields (for arrays of children). I moved this logic to cgen. And fixed an issue introduced with option fields, where an array of children was unpacked into a non-array result which could corrupt memory. * orm: fixed vast error * orm: skip 2 tests on ubuntu-musl which require sqlite3.h * cgen: prevent casting a struct (string) * v fmt orm_fkey_attribute.vv, orm_multidim_array.vv, orm_table_attributes.vv; run `VAUTOFIX=1 ./v vlib/v/compiler_errors_test.v`
408 lines
7.1 KiB
V
408 lines
7.1 KiB
V
// import db.mysql
|
|
// import db.pg
|
|
import time
|
|
import db.sqlite
|
|
|
|
const (
|
|
offset_const = 2
|
|
)
|
|
|
|
struct Module {
|
|
id int [primary; sql: serial]
|
|
name string
|
|
nr_downloads int
|
|
test_id u64
|
|
user ?User
|
|
created time.Time
|
|
}
|
|
|
|
[table: 'userlist']
|
|
struct User {
|
|
id int [primary; sql: serial]
|
|
age int
|
|
name string [sql: 'username']
|
|
is_customer bool
|
|
skipped_string string [skip]
|
|
skipped_string2 string [sql: '-']
|
|
skipped_array []string [skip]
|
|
skipped_array2 []string [sql: '-']
|
|
}
|
|
|
|
struct Foo {
|
|
age int
|
|
}
|
|
|
|
struct TestTime {
|
|
id int [primary; sql: serial]
|
|
create time.Time
|
|
}
|
|
|
|
fn test_use_struct_field_as_limit() {
|
|
db := sqlite.connect(':memory:') or { panic(err) }
|
|
|
|
sql db {
|
|
create table User
|
|
}!
|
|
|
|
foo := Foo{
|
|
age: 10
|
|
}
|
|
|
|
sam := User{
|
|
age: 29
|
|
name: 'Sam'
|
|
skipped_string2: 'this should be ignored'
|
|
skipped_array: ['ignored', 'array']
|
|
skipped_array2: ['another', 'ignored', 'array']
|
|
}
|
|
|
|
sql db {
|
|
insert sam into User
|
|
}!
|
|
|
|
users := sql db {
|
|
select from User limit foo.age
|
|
}!
|
|
|
|
assert users.len == 1
|
|
assert users[0].name == 'Sam'
|
|
assert users[0].age == 29
|
|
assert users[0].skipped_string == ''
|
|
assert users[0].skipped_string2 == ''
|
|
assert users[0].skipped_array == [], 'skipped because of the [skip] tag, used for both sql and json'
|
|
assert users[0].skipped_array2 == [], "should be skipped, because of the sql specific [sql: '-'] tag"
|
|
}
|
|
|
|
fn test_orm() {
|
|
db := sqlite.connect(':memory:') or { panic(err) }
|
|
|
|
sql db {
|
|
create table Module
|
|
}!
|
|
sql db {
|
|
create table User
|
|
}!
|
|
|
|
name := 'Peter'
|
|
|
|
sam := User{
|
|
age: 29
|
|
name: 'Sam'
|
|
}
|
|
|
|
peter := User{
|
|
age: 31
|
|
name: 'Peter'
|
|
}
|
|
|
|
k := User{
|
|
age: 30
|
|
name: 'Kate'
|
|
is_customer: true
|
|
}
|
|
|
|
sql db {
|
|
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'
|
|
assert one_peter.id == 2
|
|
|
|
users = sql db {
|
|
select from User where id == 1
|
|
}!
|
|
|
|
user := users.first()
|
|
assert user.name == 'Sam'
|
|
assert user.id == 1
|
|
assert user.age == 29
|
|
|
|
users = sql db {
|
|
select from User where id > 0
|
|
}!
|
|
assert users.len == 3
|
|
assert users[0].name == 'Sam'
|
|
assert users[1].name == 'Peter'
|
|
assert users[1].age == 31
|
|
|
|
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
|
|
assert users3[1].age == 31
|
|
|
|
new_user := User{
|
|
name: 'New user'
|
|
age: 30
|
|
}
|
|
sql db {
|
|
insert new_user into User
|
|
}!
|
|
|
|
users = sql db {
|
|
select from User where id == 4
|
|
}!
|
|
|
|
x := users.first()
|
|
assert x.age == 30
|
|
assert x.id == 4
|
|
assert x.name == 'New user'
|
|
|
|
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
|
|
assert customer.name == 'Kate'
|
|
|
|
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'
|
|
|
|
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
|
|
assert kate3.name == 'Kate N'
|
|
|
|
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
|
|
|
|
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
|
|
|
|
// Remove this when pg is used
|
|
// 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()
|
|
|
|
time_test := TestTime{
|
|
create: tnow
|
|
}
|
|
|
|
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
|
|
|
|
mod := Module{}
|
|
|
|
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
|
|
// has them zeroed, because the db field resolution is seconds.
|
|
assert modules.first().created.format_ss() == t.format_ss()
|
|
|
|
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
|
|
}!
|
|
}
|