orm: fix option type, convert from int to i8, add examples, etc (fix #24211) (#24213)

This commit is contained in:
kbkpbot 2025-04-15 01:19:31 +08:00 committed by GitHub
parent 277c9c5f05
commit b180a03253
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 674 additions and 139 deletions

67
examples/orm/orm_func.v Normal file
View file

@ -0,0 +1,67 @@
module main
import db.sqlite
import time
import orm
@[table: 'sys_users']
struct User {
pub:
id string @[immutable; primary; sql: 'id'; sql_type: 'VARCHAR(255)'; unique]
name ?string @[immutable; sql: 'nick_name'; sql_type: 'VARCHAR(255)'; unique]
created_at ?time.Time @[omitempty; sql_type: 'TIMESTAMP']
updated_at time.Time @[default: new; omitempty; sql_type: 'TIMESTAMP']
}
fn main() {
mut db := sqlite.connect(':memory:')!
defer { db.close() or {} }
user1 := User{
id: '001'
name: 'Jengro'
created_at: time.now()
updated_at: time.now()
}
user2 := User{
id: '002'
name: 'Dev'
created_at: time.now()
updated_at: time.now()
}
mut qb := orm.new_query[User](db)
// create table
qb.create()!
// insert into table
qb.insert(user1)!
qb.insert(user2)!
// query all fields
all_users := qb.query()!
dump(all_users)
// query all users' nick_name
all_user_names := qb.select('nick_name')!.query()!
dump(all_user_names)
// where
selected_users := qb.where('id = ?', '001')!.query()!
dump(selected_users)
// update
qb.set('nick_name = ?', 'Tom')!.where('id = ?', '001')!.update()!
updated_user := qb.query()!
dump(updated_user)
// delete
qb.where('id = ?', '001')!.delete()!
remain_users := qb.query()!
dump(remain_users)
// drop table
qb.drop()!
}

79
examples/orm/orm_sql.v Normal file
View file

@ -0,0 +1,79 @@
module main
import db.sqlite
import time
import orm
@[table: 'sys_users']
struct User {
pub:
id string @[immutable; primary; sql: 'id'; sql_type: 'VARCHAR(255)'; unique]
name ?string @[immutable; sql: 'name'; sql_type: 'VARCHAR(255)'; unique]
created_at ?time.Time @[omitempty; sql_type: 'TIMESTAMP']
updated_at time.Time @[default: new; omitempty; sql_type: 'TIMESTAMP']
}
fn main() {
mut db := sqlite.connect(':memory:')!
defer { db.close() or {} }
user1 := User{
id: '001'
name: 'Jengro'
created_at: time.now()
updated_at: time.now()
}
user2 := User{
id: '002'
name: 'Dev'
created_at: time.now()
updated_at: time.now()
}
// create table
sql db {
create table User
}!
// insert into table
sql db {
insert user1 into User
insert user2 into User
}!
// query all fields
all_users := sql db {
select from User
}!
dump(all_users)
// where
selected_users := sql db {
select from User where id == '001'
}!
dump(selected_users)
// update
sql db {
update User set name = 'Tom' where id == '001'
}!
updated_user := sql db {
select from User
}!
dump(updated_user)
// delete
sql db {
delete from User where id == '001'
}!
remain_users := sql db {
select from User
}!
dump(remain_users)
// drop table
sql db {
drop table User
}!
}

View file

@ -43,22 +43,6 @@ pub fn (qb_ &QueryBuilder[T]) reset() &QueryBuilder[T] {
return qb return qb
} }
// from vlib/v/gen/c/orm.v write_orm_select()
fn type_from(value string) int {
if ret_type := type_idx[value] {
return ret_type
} else {
if value.contains('time.Time') {
return time_
} else if value.contains('struct') {
return type_idx['int']
} else if value.contains('enum') {
return enum_
}
}
return 0
}
// where create a `where` clause // where create a `where` clause
// valid token in the `condition` include: `field's names`, `operator`, `(`, `)`, `?`, `AND`, `OR`, `||`, `&&`, // valid token in the `condition` include: `field's names`, `operator`, `(`, `)`, `?`, `AND`, `OR`, `||`, `&&`,
// valid `operator` incldue: `=`, `!=`, `<>`, `>=`, `<=`, `>`, `<`, `LIKE`, `ILIKE`, `IS NULL`, `IS NOT NULL` // valid `operator` incldue: `=`, `!=`, `<>`, `>=`, `<=`, `>`, `<`, `LIKE`, `ILIKE`, `IS NULL`, `IS NOT NULL`
@ -414,10 +398,19 @@ fn struct_meta[T]() []TableField {
} }
} }
mut field_type := field.typ
if typeof(field).name.contains('time.Time') {
field_type = time_
} else if field.is_struct {
field_type = type_idx['int']
} else if field.is_enum {
field_type = enum_
}
if !is_skip { if !is_skip {
meta << TableField{ meta << TableField{
name: field.name name: field.name
typ: type_from(typeof(field).name) typ: field_type
nullable: field.is_option nullable: field.is_option
attrs: attrs attrs: attrs
} }
@ -435,43 +428,164 @@ fn (qb &QueryBuilder[T]) map_row(row []Primitive) !T {
mm := qb.meta.filter(it.name == field.name) mm := qb.meta.filter(it.name == field.name)
if mm.len != 0 { if mm.len != 0 {
m = mm[0] m = mm[0]
} index := qb.config.fields.index(sql_field_name(m))
index := qb.config.fields.index(field.name) if index >= 0 {
if index >= 0 { value := row[index]
value := row[index]
if value == Primitive(Null{}) && m.nullable { if value == Primitive(Null{}) && m.nullable {
// set to none by default // set to none by default
} else { } else {
$if field.typ is i8 || field.typ is ?i8 { $if field.typ is i8 || field.typ is ?i8 {
instance.$(field.name) = value as i8 instance.$(field.name) = match value {
} $else $if field.typ is i16 || field.typ is ?i16 { i8 { i8(value) }
instance.$(field.name) = value as i16 i16 { i8(value) }
} $else $if field.typ is int || field.typ is ?int { int { i8(value) }
instance.$(field.name) = value as int i64 { i8(value) }
} $else $if field.typ is i64 || field.typ is ?i64 { u8 { i8(value) }
instance.$(field.name) = value as i64 u16 { i8(value) }
} $else $if field.typ is u8 || field.typ is ?u8 { u32 { i8(value) }
instance.$(field.name) = value as u8 u64 { i8(value) }
} $else $if field.typ is u16 || field.typ is ?u16 { bool { i8(value) }
instance.$(field.name) = value as u16 else { 0 }
} $else $if field.typ is u32 || field.typ is ?u32 { }
instance.$(field.name) = value as u32 } $else $if field.typ is i16 || field.typ is ?i16 {
} $else $if field.typ is u64 || field.typ is ?u64 { instance.$(field.name) = match value {
instance.$(field.name) = value as u64 i8 { i16(value) }
} $else $if field.typ is f32 || field.typ is ?f32 { i16 { i16(value) }
instance.$(field.name) = value as f32 int { i16(value) }
} $else $if field.typ is f64 || field.typ is ?f64 { i64 { i16(value) }
instance.$(field.name) = value as f64 u8 { i16(value) }
} $else $if field.typ is bool || field.typ is ?bool { u16 { i16(value) }
instance.$(field.name) = value as bool u32 { i16(value) }
} $else $if field.typ is string || field.typ is ?string { u64 { i16(value) }
instance.$(field.name) = value as string bool { i16(value) }
} $else $if field.typ is time.Time || field.typ is ?time.Time { else { 0 }
if m.typ == time_ { }
instance.$(field.name) = value as time.Time } $else $if field.typ is int || field.typ is ?int {
} else if m.typ == type_string { instance.$(field.name) = match value {
instance.$(field.name) = time.parse(value as string)! i8 { int(value) }
i16 { int(value) }
int { int(value) }
i64 { int(value) }
u8 { int(value) }
u16 { int(value) }
u32 { int(value) }
u64 { int(value) }
bool { int(value) }
else { 0 }
}
} $else $if field.typ is i64 || field.typ is ?i64 {
instance.$(field.name) = match value {
i8 { i64(value) }
i16 { i64(value) }
int { i64(value) }
i64 { i64(value) }
u8 { i64(value) }
u16 { i64(value) }
u32 { i64(value) }
u64 { i64(value) }
bool { i64(value) }
else { 0 }
}
} $else $if field.typ is u8 || field.typ is ?u8 {
instance.$(field.name) = match value {
i8 { u8(value) }
i16 { u8(value) }
int { u8(value) }
i64 { u8(value) }
u8 { u8(value) }
u16 { u8(value) }
u32 { u8(value) }
u64 { u8(value) }
bool { u8(value) }
else { 0 }
}
} $else $if field.typ is u16 || field.typ is ?u16 {
instance.$(field.name) = match value {
i8 { u16(value) }
i16 { u16(value) }
int { u16(value) }
i64 { u16(value) }
u8 { u16(value) }
u16 { u16(value) }
u32 { u16(value) }
u64 { u16(value) }
bool { u16(value) }
else { 0 }
}
} $else $if field.typ is u32 || field.typ is ?u32 {
instance.$(field.name) = match value {
i8 { u32(value) }
i16 { u32(value) }
int { u32(value) }
i64 { u32(value) }
u8 { u32(value) }
u16 { u32(value) }
u32 { u32(value) }
u64 { u32(value) }
bool { u32(value) }
else { 0 }
}
} $else $if field.typ is u64 || field.typ is ?u64 {
instance.$(field.name) = match value {
i8 { u64(value) }
i16 { u64(value) }
int { u64(value) }
i64 { u64(value) }
u8 { u64(value) }
u16 { u64(value) }
u32 { u64(value) }
u64 { u64(value) }
bool { u64(value) }
else { 0 }
}
} $else $if field.typ is f32 || field.typ is ?f32 {
instance.$(field.name) = match value {
i8 { f32(value) }
i16 { f32(value) }
int { f32(value) }
i64 { f32(value) }
u8 { f32(value) }
u16 { f32(value) }
u32 { f32(value) }
u64 { f32(value) }
bool { f32(value) }
else { 0 }
}
} $else $if field.typ is f64 || field.typ is ?f64 {
instance.$(field.name) = match value {
i8 { f64(value) }
i16 { f64(value) }
int { f64(value) }
i64 { f64(value) }
u8 { f64(value) }
u16 { f64(value) }
u32 { f64(value) }
u64 { f64(value) }
bool { f64(value) }
else { 0 }
}
} $else $if field.typ is bool || field.typ is ?bool {
instance.$(field.name) = match value {
i8 { value != 0 }
i16 { value != 0 }
int { value != 0 }
i64 { value != 0 }
u8 { value != 0 }
u16 { value != 0 }
u32 { value != 0 }
u64 { value != 0 }
bool { value }
else { false }
}
} $else $if field.typ is string || field.typ is ?string {
instance.$(field.name) = value as string
} $else $if field.typ is time.Time || field.typ is ?time.Time {
if m.typ == time_ {
instance.$(field.name) = value as time.Time
} else if m.typ == type_string {
instance.$(field.name) = time.parse(value as string)!
}
} }
} }
} }

View file

@ -4,15 +4,40 @@ import time
@[table: 'sys_users'] @[table: 'sys_users']
struct User { struct User {
id int @[primary; serial] id int @[primary; serial]
name string name string
age int age int
role string role string
status int status int
salary int salary int
title string title string
score int score int
created_at ?time.Time @[sql_type: 'TIMESTAMP'] 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
} }
fn test_orm_func_where() { fn test_orm_func_where() {
@ -56,101 +81,351 @@ fn test_orm_func_where() {
fn test_orm_func_stmts() { fn test_orm_func_stmts() {
users := [ users := [
User{ User{
name: 'Tom' name: 'Tom'
age: 30 age: 30
role: 'admin' role: 'admin'
status: 1 status: 1
salary: 5000 salary: 5000
title: 'manager' title: 'manager'
score: 90 score: 90
created_at: time.now() 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{ User{
name: 'Alice' name: 'Alice'
age: 20 age: 20
role: 'employee' role: 'employee'
status: 2 status: 2
salary: 2000 salary: 2000
title: 'doctor' title: 'doctor'
score: 95 score: 95
created_at: time.now() 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{ User{
name: 'Mars' name: 'Mars'
age: 40 age: 40
role: 'employer' role: 'employer'
status: 3 status: 3
salary: 1000 salary: 1000
title: 'doctor' title: 'doctor'
score: 85 score: 85
created_at: time.now() 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{ User{
name: 'Kitty' name: 'Kitty'
age: 18 age: 18
role: 'employer' role: 'employer'
status: 1 status: 1
salary: 1500 salary: 1500
title: 'doctor' title: 'doctor'
score: 87 score: 87
created_at: time.now() 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{ User{
name: 'Silly' name: 'Silly'
age: 27 age: 27
role: 'employer' role: 'employer'
status: 5 status: 5
salary: 2500 salary: 2500
title: 'doctor' title: 'doctor'
score: 81 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'
}, },
User{ User{
name: 'Smith' name: 'Smith'
age: 37 age: 37
role: 'employer' role: 'employer'
status: 1 status: 1
salary: 4500 salary: 4500
title: 'doctor' title: 'doctor'
score: 89 score: 89
created_at: time.now() 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{ User{
name: 'Bob' name: 'Bob'
age: 26 age: 26
role: 'employer' role: 'employer'
status: 2 status: 2
salary: 6500 salary: 6500
title: 'doctor' title: 'doctor'
score: 81 score: 81
created_at: time.now() 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{ User{
name: 'Peter' name: 'Peter'
age: 29 age: 29
role: 'employer' role: 'employer'
status: 1 status: 1
salary: 3500 salary: 3500
title: 'doctor' title: 'doctor'
score: 80 score: 80
created_at: time.now() 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{ User{
name: 'See' name: 'See'
age: 45 age: 45
role: 'employer' role: 'employer'
status: 2 status: 2
salary: 8500 salary: 8500
title: 'doctor' title: 'doctor'
score: 82 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{ User{
name: 'John' name: 'John'
age: 42 age: 42
role: 'employer' role: 'employer'
status: 1 status: 1
salary: 10000 salary: 10000
title: 'doctor' title: 'doctor'
score: 88 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:')! mut db := sqlite.connect(':memory:')!