mirror of
https://github.com/vlang/v.git
synced 2025-09-14 15:02:33 +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`
373 lines
5.9 KiB
V
373 lines
5.9 KiB
V
import db.sqlite
|
|
import rand
|
|
|
|
struct Parent {
|
|
id int [primary; sql: serial]
|
|
name string
|
|
children []Child [fkey: 'parent_id']
|
|
notes []Note [fkey: 'owner_id']
|
|
}
|
|
|
|
struct Child {
|
|
mut:
|
|
id int [primary; sql: serial]
|
|
parent_id int
|
|
name string
|
|
}
|
|
|
|
struct Note {
|
|
mut:
|
|
id int [primary; sql: serial]
|
|
owner_id int
|
|
text string
|
|
}
|
|
|
|
struct Account {
|
|
id int [primary; sql: serial]
|
|
}
|
|
|
|
struct Package {
|
|
id int [primary; sql: serial]
|
|
name string [unique]
|
|
author User [fkey: 'id'] // mandatory user
|
|
}
|
|
|
|
struct Delivery {
|
|
id int [primary; sql: serial]
|
|
name string [unique]
|
|
author ?User [fkey: 'id'] // optional user
|
|
}
|
|
|
|
struct User {
|
|
pub mut:
|
|
id int [primary; sql: serial]
|
|
username string [unique]
|
|
}
|
|
|
|
struct Entity {
|
|
uuid string [primary]
|
|
description string
|
|
}
|
|
|
|
struct EntityWithFloatPrimary {
|
|
id f64 [primary]
|
|
name string
|
|
}
|
|
|
|
pub fn insert_parent(db sqlite.DB, mut parent Parent) ! {
|
|
sql db {
|
|
insert parent into Parent
|
|
}!
|
|
}
|
|
|
|
fn test_set_primary_value() {
|
|
// The primary key is an constraint that ensures each record in a table is unique.
|
|
// Primary keys must contain unique values and cannot contain `NULL` values.
|
|
// However, this statement does not imply that a value cannot be inserted by the user.
|
|
// Therefore, let's allow this.
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table Child
|
|
}!
|
|
|
|
child := Child{
|
|
id: 10
|
|
parent_id: 20
|
|
}
|
|
|
|
sql db {
|
|
insert child into Child
|
|
}!
|
|
|
|
children := sql db {
|
|
select from Child
|
|
}!
|
|
|
|
assert children.first() == child
|
|
}
|
|
|
|
fn test_uuid_primary_key() {
|
|
db := sqlite.connect(':memory:')!
|
|
uuid := rand.uuid_v4()
|
|
|
|
sql db {
|
|
create table Entity
|
|
}!
|
|
|
|
entity := Entity{
|
|
uuid: uuid
|
|
description: 'Test'
|
|
}
|
|
|
|
sql db {
|
|
insert entity into Entity
|
|
}!
|
|
|
|
entities := sql db {
|
|
select from Entity where uuid == uuid
|
|
}!
|
|
|
|
mut is_duplicate_inserted := true
|
|
|
|
sql db {
|
|
insert entity into Entity
|
|
} or { is_duplicate_inserted = false }
|
|
|
|
assert entities.len == 1
|
|
assert entities.first() == entity
|
|
assert is_duplicate_inserted == false
|
|
}
|
|
|
|
fn test_float_primary_key() {
|
|
db := sqlite.connect(':memory:')!
|
|
id := 3.14
|
|
|
|
sql db {
|
|
create table EntityWithFloatPrimary
|
|
}!
|
|
|
|
entity := EntityWithFloatPrimary{
|
|
id: id
|
|
name: 'Test'
|
|
}
|
|
|
|
sql db {
|
|
insert entity into EntityWithFloatPrimary
|
|
}!
|
|
|
|
entities := sql db {
|
|
select from EntityWithFloatPrimary where id == id
|
|
}!
|
|
|
|
assert entities.len == 1
|
|
assert entities.first() == entity
|
|
}
|
|
|
|
fn test_does_not_insert_uninitialized_mandatory_field() {
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table User
|
|
create table Package
|
|
}!
|
|
|
|
package := Package{
|
|
name: 'xml'
|
|
// author
|
|
}
|
|
|
|
mut query_successful := true
|
|
|
|
sql db {
|
|
insert package into Package
|
|
} or { query_successful = false }
|
|
|
|
assert !query_successful
|
|
|
|
users := sql db {
|
|
select from User
|
|
}!
|
|
|
|
// users must be empty because the package doesn't have an initialized `User` structure.
|
|
assert users.len == 0
|
|
}
|
|
|
|
fn test_insert_empty_mandatory_field() {
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table User
|
|
create table Package
|
|
}!
|
|
|
|
package := Package{
|
|
name: 'xml'
|
|
author: User{}
|
|
}
|
|
|
|
sql db {
|
|
insert package into Package
|
|
}!
|
|
|
|
users := sql db {
|
|
select from User
|
|
}!
|
|
|
|
assert users.len == 1
|
|
}
|
|
|
|
fn test_does_insert_uninitialized_optional_field() {
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table User
|
|
create table Delivery
|
|
}!
|
|
|
|
package := Delivery{
|
|
name: 'wow'
|
|
// author
|
|
}
|
|
|
|
sql db {
|
|
insert package into Delivery
|
|
}!
|
|
|
|
users := sql db {
|
|
select from User
|
|
}!
|
|
|
|
assert users.len == 0 // no user added
|
|
}
|
|
|
|
fn test_insert_empty_optional_field() {
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table User
|
|
create table Delivery
|
|
}!
|
|
|
|
package := Delivery{
|
|
name: 'bob'
|
|
author: User{}
|
|
}
|
|
|
|
sql db {
|
|
insert package into Delivery
|
|
}!
|
|
|
|
users := sql db {
|
|
select from User
|
|
}!
|
|
|
|
assert users.len == 1 // user was added
|
|
}
|
|
|
|
fn test_insert_empty_object() {
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
account := Account{}
|
|
|
|
sql db {
|
|
create table Account
|
|
insert account into Account
|
|
}!
|
|
|
|
accounts := sql db {
|
|
select from Account
|
|
}!
|
|
|
|
assert accounts.len == 1
|
|
}
|
|
|
|
fn test_orm_insert_mut_object() {
|
|
db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table Parent
|
|
create table Child
|
|
create table Note
|
|
}!
|
|
|
|
mut parent := Parent{
|
|
name: 'test'
|
|
}
|
|
|
|
insert_parent(db, mut parent)!
|
|
|
|
parents := sql db {
|
|
select from Parent
|
|
}!
|
|
|
|
assert parents.len == 1
|
|
}
|
|
|
|
fn test_orm_insert_with_multiple_child_elements() {
|
|
mut db := sqlite.connect(':memory:')!
|
|
|
|
sql db {
|
|
create table Parent
|
|
create table Child
|
|
create table Note
|
|
}!
|
|
|
|
new_parent := Parent{
|
|
name: 'test'
|
|
children: [
|
|
Child{
|
|
name: 'Lisa'
|
|
},
|
|
Child{
|
|
name: 'Steve'
|
|
},
|
|
]
|
|
notes: [
|
|
Note{
|
|
text: 'First note'
|
|
},
|
|
Note{
|
|
text: 'Second note'
|
|
},
|
|
Note{
|
|
text: 'Third note'
|
|
},
|
|
]
|
|
}
|
|
|
|
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
|
|
assert parent.notes.len == new_parent.notes.len
|
|
|
|
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'
|
|
assert parent.children[1].name == 'Steve'
|
|
|
|
assert parent.notes[0].text == 'First note'
|
|
assert parent.notes[1].text == 'Second note'
|
|
assert parent.notes[2].text == 'Third note'
|
|
}
|
|
|
|
[table: 'customers']
|
|
struct Customer {
|
|
id i64 [primary; sql: serial]
|
|
name string
|
|
}
|
|
|
|
fn test_i64_primary_field_works_with_insertions_of_id_0() {
|
|
db := sqlite.connect(':memory:')!
|
|
sql db {
|
|
create table Customer
|
|
}!
|
|
for i in ['Bob', 'Charlie'] {
|
|
new_customer := Customer{
|
|
name: i
|
|
}
|
|
sql db {
|
|
insert new_customer into Customer
|
|
}!
|
|
}
|
|
users := sql db {
|
|
select from Customer
|
|
}!
|
|
assert users.len == 2
|
|
// println("${users}")
|
|
}
|