orm: add IN and NOT IN (#24634)

This commit is contained in:
Louis Schmieder 2025-06-02 02:49:10 +02:00 committed by GitHub
parent 566d22ab66
commit 73ebf42015
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 165 additions and 7 deletions

View File

@ -248,6 +248,7 @@ const skip_on_ubuntu_musl = [
'vlib/orm/orm_serial_attribute_test.v',
'vlib/orm/orm_option_subselect_test.v',
'vlib/orm/orm_func_test.v',
'vlib/orm/orm_where_in_test.v',
'vlib/v/tests/orm_enum_test.v',
'vlib/v/tests/orm_sub_struct_test.v',
'vlib/v/tests/orm_sub_array_struct_test.v',

View File

@ -247,6 +247,11 @@ fn stmt_bind_primitive(mut stmt Stmt, data orm.Primitive) {
orm.Null {
stmt.bind_null()
}
[]orm.Primitive {
for element in data {
stmt_bind_primitive(mut stmt, element)
}
}
}
}

View File

@ -180,6 +180,11 @@ fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats
lens << int(0) // ignored
formats << 0 // ignored
}
[]orm.Primitive {
for element in data {
pg_stmt_match(mut types, mut vals, mut lens, mut formats, element)
}
}
}
}

View File

@ -114,7 +114,7 @@ fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryDa
// Binds all values of d in the prepared statement
fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ! {
for data in d.data {
err := bind(stmt, c, data)
err := bind(stmt, mut c, data)
if err != 0 {
return stmt.db.error_message(err, query)
@ -124,7 +124,7 @@ fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ! {
}
// Universal bind function
fn bind(stmt Stmt, c &int, data orm.Primitive) int {
fn bind(stmt Stmt, mut c &int, data orm.Primitive) int {
mut err := 0
match data {
i8, i16, int, u8, u16, u32, bool {
@ -143,11 +143,21 @@ fn bind(stmt Stmt, c &int, data orm.Primitive) int {
err = stmt.bind_int(c, int(data.unix()))
}
orm.InfixType {
err = bind(stmt, c, data.right)
err = bind(stmt, mut c, data.right)
}
orm.Null {
err = stmt.bind_null(c)
}
[]orm.Primitive {
for element in data {
tmp_err := bind(stmt, mut c, element)
c++
if tmp_err != 0 {
err = tmp_err
break
}
}
}
}
return err
}

View File

@ -52,6 +52,7 @@ pub type Primitive = InfixType
| u32
| u64
| u8
| []Primitive
pub struct Null {}
@ -66,6 +67,8 @@ pub enum OperationKind {
orm_ilike // ILIKE
is_null // IS NULL
is_not_null // IS NOT NULL
in // IN
not_in // NOT IN
}
pub enum MathOperationKind {
@ -105,6 +108,8 @@ fn (kind OperationKind) to_str() string {
.orm_ilike { 'ILIKE' }
.is_null { 'IS NULL' }
.is_not_null { 'IS NOT NULL' }
.in { 'IN' }
.not_in { 'NOT IN' }
}
return str
}
@ -402,12 +407,25 @@ fn gen_where_clause(where QueryData, q string, qm string, num bool, mut c &int)
}
str += '${q}${field}${q} ${where.kinds[i].to_str()}'
if !where.kinds[i].is_unary() {
if where.data.len > i && where.data[i] is []Primitive {
len := (where.data[i] as []Primitive).len
mut tmp := []string{len: len}
for j in 0 .. len {
tmp[j] = '${qm}'
if num {
tmp[j] += '${c}'
c++
}
}
str += ' (${tmp.join(', ')})'
} else {
str += ' ${qm}'
if num {
str += '${c}'
c++
}
}
}
if current_post_par > 0 {
str += ' ) '.repeat(current_post_par)
}
@ -629,6 +647,10 @@ fn option_bool_to_primitive(b ?bool) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_bool_to_primitive(b []bool) Primitive {
return Primitive(b.map(bool_to_primitive(it)))
}
fn f32_to_primitive(b f32) Primitive {
return Primitive(b)
}
@ -637,6 +659,10 @@ fn option_f32_to_primitive(b ?f32) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_f32_to_primitive(b []f32) Primitive {
return Primitive(b.map(f32_to_primitive(it)))
}
fn f64_to_primitive(b f64) Primitive {
return Primitive(b)
}
@ -645,6 +671,10 @@ fn option_f64_to_primitive(b ?f64) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_f64_to_primitive(b []f64) Primitive {
return Primitive(b.map(f64_to_primitive(it)))
}
fn i8_to_primitive(b i8) Primitive {
return Primitive(b)
}
@ -653,6 +683,10 @@ fn option_i8_to_primitive(b ?i8) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_i8_to_primitive(b []i8) Primitive {
return Primitive(b.map(i8_to_primitive(it)))
}
fn i16_to_primitive(b i16) Primitive {
return Primitive(b)
}
@ -661,6 +695,10 @@ fn option_i16_to_primitive(b ?i16) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_i16_to_primitive(b []i16) Primitive {
return Primitive(b.map(i16_to_primitive(it)))
}
fn int_to_primitive(b int) Primitive {
return Primitive(b)
}
@ -669,6 +707,10 @@ fn option_int_to_primitive(b ?int) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_int_to_primitive(b []int) Primitive {
return Primitive(b.map(int_to_primitive(it)))
}
// int_literal_to_primitive handles int literal value
fn int_literal_to_primitive(b int) Primitive {
return Primitive(b)
@ -678,6 +720,10 @@ fn option_int_literal_to_primitive(b ?int) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_int_literal_to_primitive(b []int) Primitive {
return Primitive(b.map(int_literal_to_primitive(it)))
}
// float_literal_to_primitive handles float literal value
fn float_literal_to_primitive(b f64) Primitive {
return Primitive(b)
@ -687,6 +733,10 @@ fn option_float_literal_to_primitive(b ?f64) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_float_literal_to_primitive(b []f64) Primitive {
return Primitive(b.map(float_literal_to_primitive(it)))
}
fn i64_to_primitive(b i64) Primitive {
return Primitive(b)
}
@ -695,6 +745,10 @@ fn option_i64_to_primitive(b ?i64) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_i64_to_primitive(b []i64) Primitive {
return Primitive(b.map(i64_to_primitive(it)))
}
fn u8_to_primitive(b u8) Primitive {
return Primitive(b)
}
@ -703,6 +757,10 @@ fn option_u8_to_primitive(b ?u8) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_u8_to_primitive(b []u8) Primitive {
return Primitive(b.map(u8_to_primitive(it)))
}
fn u16_to_primitive(b u16) Primitive {
return Primitive(b)
}
@ -711,6 +769,10 @@ fn option_u16_to_primitive(b ?u16) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_u16_to_primitive(b []u16) Primitive {
return Primitive(b.map(u16_to_primitive(it)))
}
fn u32_to_primitive(b u32) Primitive {
return Primitive(b)
}
@ -719,6 +781,10 @@ fn option_u32_to_primitive(b ?u32) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_u32_to_primitive(b []u32) Primitive {
return Primitive(b.map(u32_to_primitive(it)))
}
fn u64_to_primitive(b u64) Primitive {
return Primitive(b)
}
@ -727,6 +793,10 @@ fn option_u64_to_primitive(b ?u64) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_u64_to_primitive(b []u64) Primitive {
return Primitive(b.map(u64_to_primitive(it)))
}
fn string_to_primitive(b string) Primitive {
return Primitive(b)
}
@ -735,6 +805,10 @@ fn option_string_to_primitive(b ?string) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_string_to_primitive(b []string) Primitive {
return Primitive(b.map(string_to_primitive(it)))
}
fn time_to_primitive(b time.Time) Primitive {
return Primitive(b)
}
@ -743,6 +817,10 @@ fn option_time_to_primitive(b ?time.Time) Primitive {
return if b_ := b { Primitive(b_) } else { null_primitive }
}
fn array_time_to_primitive(b []time.Time) Primitive {
return Primitive(b.map(time_to_primitive(it)))
}
fn infix_to_primitive(b InfixType) Primitive {
return Primitive(b)
}

View File

@ -0,0 +1,51 @@
// vtest flaky: true
// vtest retry: 3
import db.sqlite
struct User {
id int @[primary; sql: serial]
name string
}
fn get_users_in(mut db sqlite.DB, names []string) ![]User {
return sql db {
select from User where name in names
}!
}
fn get_users_not_in(mut db sqlite.DB, names []string) ![]User {
return sql db {
select from User where name !in names
}!
}
fn test_orm_mut_db() {
mut db := sqlite.connect(':memory:') or { panic(err) }
sql db {
create table User
}!
first_user := User{
name: 'first'
}
second_user := User{
name: 'second'
}
sql db {
insert first_user into User
insert second_user into User
}!
in_users := get_users_in(mut db, ['first'])!
assert in_users.len == 1
not_in_users := get_users_not_in(mut db, ['second'])!
assert not_in_users.len == 1
all_users := get_users_in(mut db, ['first', 'second'])!
assert all_users.len == 2
}

View File

@ -625,6 +625,8 @@ fn (mut g Gen) write_orm_primitive(t ast.Type, expr ast.Expr) {
typ = 'option_${typ}'
} else if g.table.final_sym(t).kind == .enum {
typ = g.table.sym(g.table.final_type(t)).cname
} else if g.table.final_sym(t).kind == .array {
typ = g.table.sym(g.table.final_type(t)).cname.to_lower()
}
g.write('orm__${typ}_to_primitive(')
if expr is ast.CallExpr {
@ -778,6 +780,12 @@ fn (mut g Gen) write_orm_where_expr(expr ast.Expr, mut fields []string, mut pare
.not_is {
'orm__OperationKind__is_not_null'
}
.key_in {
'orm__OperationKind__in'
}
.not_in {
'orm__OperationKind__not_in'
}
else {
''
}