mirror of
https://github.com/vlang/v.git
synced 2025-09-15 15:32:27 +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`
227 lines
4.9 KiB
V
227 lines
4.9 KiB
V
import orm
|
|
import db.sqlite
|
|
import time
|
|
|
|
struct TestCustomSqlType {
|
|
id int [primary; sql: serial]
|
|
custom string [sql_type: 'INTEGER']
|
|
custom1 string [sql_type: 'TEXT']
|
|
custom2 string [sql_type: 'REAL']
|
|
custom3 string [sql_type: 'NUMERIC']
|
|
custom4 string
|
|
custom5 int
|
|
custom6 time.Time
|
|
}
|
|
|
|
struct TestDefaultAtribute {
|
|
id string [primary; sql: serial]
|
|
name string
|
|
created_at ?string [default: 'CURRENT_TIME']
|
|
created_at1 ?string [default: 'CURRENT_DATE']
|
|
created_at2 ?string [default: 'CURRENT_TIMESTAMP']
|
|
}
|
|
|
|
struct EntityToTest {
|
|
id int [notnull; sql_type: 'INTEGER']
|
|
smth string [notnull; sql_type: 'TEXT']
|
|
}
|
|
|
|
fn test_sqlite_orm() {
|
|
mut db := sqlite.connect(':memory:') or { panic(err) }
|
|
defer {
|
|
db.close() or { panic(err) }
|
|
}
|
|
db.create('Test', [
|
|
orm.TableField{
|
|
name: 'id'
|
|
typ: typeof[int]().idx
|
|
attrs: [
|
|
StructAttribute{
|
|
name: 'primary'
|
|
},
|
|
StructAttribute{
|
|
name: 'sql'
|
|
has_arg: true
|
|
kind: .plain
|
|
arg: 'serial'
|
|
},
|
|
]
|
|
},
|
|
orm.TableField{
|
|
name: 'name'
|
|
typ: typeof[string]().idx
|
|
attrs: []
|
|
},
|
|
orm.TableField{
|
|
name: 'age'
|
|
typ: typeof[i64]().idx
|
|
},
|
|
]) or { panic(err) }
|
|
|
|
db.insert('Test', orm.QueryData{
|
|
fields: ['name', 'age']
|
|
data: [orm.string_to_primitive('Louis'), orm.i64_to_primitive(100)]
|
|
}) or { panic(err) }
|
|
|
|
res := db.@select(orm.SelectConfig{
|
|
table: 'Test'
|
|
has_where: true
|
|
fields: ['id', 'name', 'age']
|
|
types: [typeof[int]().idx, typeof[string]().idx, typeof[i64]().idx]
|
|
}, orm.QueryData{}, orm.QueryData{
|
|
fields: ['name', 'age']
|
|
data: [orm.Primitive('Louis'), i64(100)]
|
|
types: [typeof[string]().idx, typeof[i64]().idx]
|
|
is_and: [true, true]
|
|
kinds: [.eq, .eq]
|
|
}) or { panic(err) }
|
|
|
|
id := res[0][0]
|
|
name := res[0][1]
|
|
age := res[0][2]
|
|
|
|
assert id is int
|
|
if id is int {
|
|
assert id == 1
|
|
}
|
|
|
|
assert name is string
|
|
if name is string {
|
|
assert name == 'Louis'
|
|
}
|
|
|
|
assert age is i64
|
|
if age is i64 {
|
|
assert age == 100
|
|
}
|
|
|
|
/** test orm sql type
|
|
* - verify if all type create by attribute sql_type has created
|
|
*/
|
|
|
|
sql db {
|
|
create table TestCustomSqlType
|
|
}!
|
|
|
|
mut result_custom_sql := db.exec('
|
|
pragma table_info(TestCustomSqlType);
|
|
')!
|
|
|
|
mut table_info_types_results := []string{}
|
|
information_schema_custom_sql := ['INTEGER', 'INTEGER', 'TEXT', 'REAL', 'NUMERIC', 'TEXT',
|
|
'INTEGER', 'INTEGER']
|
|
|
|
for data_type in result_custom_sql {
|
|
table_info_types_results << data_type.vals[2]
|
|
}
|
|
assert table_info_types_results == information_schema_custom_sql
|
|
|
|
sql db {
|
|
drop table TestCustomSqlType
|
|
}!
|
|
|
|
/** test default attribute
|
|
*/
|
|
|
|
sql db {
|
|
create table TestDefaultAtribute
|
|
}!
|
|
|
|
mut result_default_sql := db.exec('
|
|
pragma table_info(TestDefaultAtribute);
|
|
')!
|
|
|
|
mut information_schema_data_types_results := []string{}
|
|
information_schema_default_sql := ['', '', 'CURRENT_TIME', 'CURRENT_DATE', 'CURRENT_TIMESTAMP']
|
|
|
|
for data_type in result_default_sql {
|
|
information_schema_data_types_results << data_type.vals[4]
|
|
}
|
|
assert information_schema_data_types_results == information_schema_default_sql
|
|
|
|
test_default_atribute := TestDefaultAtribute{
|
|
name: 'Hitalo'
|
|
}
|
|
|
|
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'
|
|
assert test_default_atribute.created_at or { '' } == ''
|
|
assert test_default_atribute.created_at1 or { '' } == ''
|
|
assert test_default_atribute.created_at2 or { '' } == ''
|
|
assert result_test_default_atribute.created_at or { '' }.len == 8 // HH:MM:SS
|
|
assert result_test_default_atribute.created_at1 or { '' }.len == 10 // YYYY-MM-DD
|
|
assert result_test_default_atribute.created_at2 or { '' }.len == 19 // YYYY-MM-DD HH:MM:SS
|
|
|
|
sql db {
|
|
drop table TestDefaultAtribute
|
|
}!
|
|
}
|
|
|
|
fn test_get_affected_rows_count() {
|
|
mut db := sqlite.connect(':memory:') or { panic(err) }
|
|
defer {
|
|
db.close() or { panic(err) }
|
|
}
|
|
|
|
db.exec('create table EntityToTest(
|
|
id integer not null constraint tbl_pk primary key,
|
|
smth integer
|
|
);')!
|
|
|
|
fst := EntityToTest{
|
|
id: 1
|
|
smth: '1'
|
|
}
|
|
|
|
sql db {
|
|
insert fst into EntityToTest
|
|
} or { panic('first insert failed') }
|
|
|
|
assert db.get_affected_rows_count() == 1
|
|
|
|
snd := EntityToTest{
|
|
id: 1
|
|
smth: '2'
|
|
}
|
|
|
|
mut sndfailed := false
|
|
sql db {
|
|
insert snd into EntityToTest
|
|
} or { sndfailed = true }
|
|
|
|
assert db.get_affected_rows_count() == 0
|
|
assert sndfailed
|
|
|
|
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
|
|
}
|