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
}
// 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
// valid token in the `condition` include: `field's names`, `operator`, `(`, `)`, `?`, `AND`, `OR`, `||`, `&&`,
// 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 {
meta << TableField{
name: field.name
typ: type_from(typeof(field).name)
typ: field_type
nullable: field.is_option
attrs: attrs
}
@ -435,8 +428,7 @@ fn (qb &QueryBuilder[T]) map_row(row []Primitive) !T {
mm := qb.meta.filter(it.name == field.name)
if mm.len != 0 {
m = mm[0]
}
index := qb.config.fields.index(field.name)
index := qb.config.fields.index(sql_field_name(m))
if index >= 0 {
value := row[index]
@ -444,27 +436,148 @@ fn (qb &QueryBuilder[T]) map_row(row []Primitive) !T {
// set to none by default
} else {
$if field.typ is i8 || field.typ is ?i8 {
instance.$(field.name) = value as i8
instance.$(field.name) = match value {
i8 { i8(value) }
i16 { i8(value) }
int { i8(value) }
i64 { i8(value) }
u8 { i8(value) }
u16 { i8(value) }
u32 { i8(value) }
u64 { i8(value) }
bool { i8(value) }
else { 0 }
}
} $else $if field.typ is i16 || field.typ is ?i16 {
instance.$(field.name) = value as i16
instance.$(field.name) = match value {
i8 { i16(value) }
i16 { i16(value) }
int { i16(value) }
i64 { i16(value) }
u8 { i16(value) }
u16 { i16(value) }
u32 { i16(value) }
u64 { i16(value) }
bool { i16(value) }
else { 0 }
}
} $else $if field.typ is int || field.typ is ?int {
instance.$(field.name) = value as int
instance.$(field.name) = match value {
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) = value as 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) = value as 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) = value as 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) = value as 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) = value as 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) = value as 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) = value as 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) = value as 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 {
@ -477,6 +590,7 @@ fn (qb &QueryBuilder[T]) map_row(row []Primitive) !T {
}
}
}
}
return instance
}

View file

@ -13,6 +13,31 @@ struct User {
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
}
fn test_orm_func_where() {
@ -64,6 +89,31 @@ fn test_orm_func_stmts() {
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'
@ -74,6 +124,31 @@ fn test_orm_func_stmts() {
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'
@ -84,6 +159,31 @@ fn test_orm_func_stmts() {
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'
@ -94,6 +194,31 @@ fn test_orm_func_stmts() {
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'
@ -103,6 +228,31 @@ fn test_orm_func_stmts() {
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'
},
User{
name: 'Smith'
@ -113,6 +263,31 @@ fn test_orm_func_stmts() {
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'
@ -123,6 +298,31 @@ fn test_orm_func_stmts() {
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'
@ -133,6 +333,31 @@ fn test_orm_func_stmts() {
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_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'
@ -142,6 +367,31 @@ fn test_orm_func_stmts() {
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'
@ -151,6 +401,31 @@ fn test_orm_func_stmts() {
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:')!