mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
orm: support parenthesized expressions as in select from User where (name == 'Sam' && is_customer == true) || id == 1
(#15693)
This commit is contained in:
parent
61a4b469a3
commit
f249feb9da
@ -107,13 +107,15 @@ fn (kind OrderType) to_str() string {
|
||||
// => fields[abc, b]; data[3, 'test']; types[index of int, index of string]; kinds[.eq, .eq]; is_and[true];
|
||||
// Every field, data, type & kind of operation in the expr share the same index in the arrays
|
||||
// is_and defines how they're addicted to each other either and or or
|
||||
// parentheses defines which fields will be inside ()
|
||||
pub struct QueryData {
|
||||
pub:
|
||||
fields []string
|
||||
data []Primitive
|
||||
types []int
|
||||
kinds []OperationKind
|
||||
is_and []bool
|
||||
fields []string
|
||||
data []Primitive
|
||||
types []int
|
||||
parentheses [][]int
|
||||
kinds []OperationKind
|
||||
is_and []bool
|
||||
}
|
||||
|
||||
pub struct InfixType {
|
||||
@ -266,11 +268,25 @@ pub fn orm_stmt_gen(table string, q string, kind StmtKind, num bool, qm string,
|
||||
}
|
||||
if kind == .update || kind == .delete {
|
||||
for i, field in where.fields {
|
||||
mut pre_par := false
|
||||
mut post_par := false
|
||||
for par in where.parentheses {
|
||||
if i in par {
|
||||
pre_par = par[0] == i
|
||||
post_par = par[1] == i
|
||||
}
|
||||
}
|
||||
if pre_par {
|
||||
str += '('
|
||||
}
|
||||
str += '$q$field$q ${where.kinds[i].to_str()} $qm'
|
||||
if num {
|
||||
str += '$c'
|
||||
c++
|
||||
}
|
||||
if post_par {
|
||||
str += ')'
|
||||
}
|
||||
if i < where.fields.len - 1 {
|
||||
str += ' AND '
|
||||
}
|
||||
@ -311,11 +327,25 @@ pub fn orm_select_gen(orm SelectConfig, q string, num bool, qm string, start_pos
|
||||
if orm.has_where {
|
||||
str += ' WHERE '
|
||||
for i, field in where.fields {
|
||||
mut pre_par := false
|
||||
mut post_par := false
|
||||
for par in where.parentheses {
|
||||
if i in par {
|
||||
pre_par = par[0] == i
|
||||
post_par = par[1] == i
|
||||
}
|
||||
}
|
||||
if pre_par {
|
||||
str += '('
|
||||
}
|
||||
str += '$q$field$q ${where.kinds[i].to_str()} $qm'
|
||||
if num {
|
||||
str += '$c'
|
||||
c++
|
||||
}
|
||||
if post_par {
|
||||
str += ')'
|
||||
}
|
||||
if i < where.fields.len - 1 {
|
||||
if where.is_and[i] {
|
||||
str += ' AND '
|
||||
|
@ -9,7 +9,7 @@ struct User {
|
||||
const db_path = os.join_path(os.temp_dir(), 'sql_statement_or_blocks.db')
|
||||
|
||||
fn test_ensure_db_exists_and_user_table_is_ok() ? {
|
||||
db := sqlite.connect(db_path)?
|
||||
mut db := sqlite.connect(db_path)?
|
||||
assert true
|
||||
|
||||
eprintln('> drop pre-existing User table...')
|
||||
@ -20,10 +20,11 @@ fn test_ensure_db_exists_and_user_table_is_ok() ? {
|
||||
create table User
|
||||
} or { panic(err) }
|
||||
assert true
|
||||
db.close()?
|
||||
}
|
||||
|
||||
fn test_sql_or_block_for_insert() ? {
|
||||
db := sqlite.connect(db_path)?
|
||||
mut db := sqlite.connect(db_path)?
|
||||
user := User{1, 'bilbo'}
|
||||
|
||||
eprintln('> inserting user 1 (first try)...')
|
||||
@ -41,10 +42,12 @@ fn test_sql_or_block_for_insert() ? {
|
||||
assert true
|
||||
println('user could not be inserted, err: $err')
|
||||
}
|
||||
eprintln('LINE: ${@LINE}')
|
||||
db.close()?
|
||||
}
|
||||
|
||||
fn test_sql_or_block_for_select() ? {
|
||||
db := sqlite.connect(db_path)?
|
||||
mut db := sqlite.connect(db_path)?
|
||||
|
||||
eprintln('> selecting user with id 1...')
|
||||
single := sql db {
|
||||
@ -53,6 +56,7 @@ fn test_sql_or_block_for_select() ? {
|
||||
eprintln('could not select user, err: $err')
|
||||
User{0, ''}
|
||||
}
|
||||
eprintln('LINE: ${@LINE}')
|
||||
|
||||
assert single.id == 1
|
||||
|
||||
@ -62,9 +66,11 @@ fn test_sql_or_block_for_select() ? {
|
||||
eprintln('could not select user, err: $err')
|
||||
User{0, ''}
|
||||
}
|
||||
eprintln('LINE: ${@LINE}')
|
||||
|
||||
assert failed.id == 0
|
||||
assert failed.name == ''
|
||||
eprintln('LINE: ${@LINE}')
|
||||
|
||||
eprintln('> selecting users...')
|
||||
multiple := sql db {
|
||||
@ -73,8 +79,11 @@ fn test_sql_or_block_for_select() ? {
|
||||
eprintln('could not users, err: $err')
|
||||
[]User{}
|
||||
}
|
||||
eprintln('LINE: ${@LINE}')
|
||||
|
||||
assert multiple.len == 1
|
||||
eprintln('LINE: ${@LINE}')
|
||||
db.close()?
|
||||
}
|
||||
|
||||
fn test_finish() ? {
|
||||
|
@ -360,6 +360,12 @@ fn test_orm() {
|
||||
// has them zeroed, because the db field resolution is seconds.
|
||||
assert updated_time_mod.created.format_ss() == t.format_ss()
|
||||
|
||||
para_select := sql db {
|
||||
select from User where (name == 'Sam' && is_customer == true) || id == 1
|
||||
}
|
||||
|
||||
assert para_select[0] == first
|
||||
|
||||
sql db {
|
||||
drop table Module
|
||||
drop table TestTime
|
||||
|
@ -8,14 +8,16 @@ import time
|
||||
pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ?[][]orm.Primitive {
|
||||
// 1. Create query and bind necessary data
|
||||
query := orm.orm_select_gen(config, '`', true, '?', 1, where)
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> @select query: "$query"')
|
||||
}
|
||||
stmt := db.new_init_stmt(query)?
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, where, query, mut c)?
|
||||
sqlite_stmt_binder(stmt, data, query, mut c)?
|
||||
|
||||
defer {
|
||||
stmt.finalize()
|
||||
}
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, where, query, mut c)?
|
||||
sqlite_stmt_binder(stmt, data, query, mut c)?
|
||||
|
||||
mut ret := [][]orm.Primitive{}
|
||||
|
||||
@ -89,12 +91,17 @@ pub fn (db DB) drop(table string) ? {
|
||||
|
||||
// Executes query and bind prepared statement data directly
|
||||
fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ? {
|
||||
$if trace_sqlite ? {
|
||||
eprintln('> sqlite_stmt_worker query: "$query"')
|
||||
}
|
||||
stmt := db.new_init_stmt(query)?
|
||||
defer {
|
||||
stmt.finalize()
|
||||
}
|
||||
mut c := 1
|
||||
sqlite_stmt_binder(stmt, data, query, mut c)?
|
||||
sqlite_stmt_binder(stmt, where, query, mut c)?
|
||||
stmt.orm_step(query)?
|
||||
stmt.finalize()
|
||||
}
|
||||
|
||||
// Binds all values of d in the prepared statement
|
||||
|
@ -277,6 +277,7 @@ fn (mut g Gen) sql_update(node ast.SqlStmtLine, expr string, table_name string)
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
g.write('.parentheses = __new_array_with_default_noscan(0, 0, sizeof(Array_int), 0),')
|
||||
if node.updated_columns.len > 0 {
|
||||
g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
|
||||
g.write(' _MOV((string[$node.updated_columns.len]){')
|
||||
@ -380,11 +381,12 @@ fn (mut g Gen) sql_write_orm_primitive(t ast.Type, expr ast.Expr) {
|
||||
g.write('),')
|
||||
}
|
||||
|
||||
fn (mut g Gen) sql_where_data(expr ast.Expr, mut fields []string, mut kinds []string, mut data []ast.Expr, mut is_and []bool) {
|
||||
fn (mut g Gen) sql_where_data(expr ast.Expr, mut fields []string, mut parentheses [][]int, mut kinds []string, mut data []ast.Expr, mut is_and []bool) {
|
||||
match expr {
|
||||
ast.InfixExpr {
|
||||
g.sql_side = .left
|
||||
g.sql_where_data(expr.left, mut fields, mut kinds, mut data, mut is_and)
|
||||
g.sql_where_data(expr.left, mut fields, mut parentheses, mut kinds, mut data, mut
|
||||
is_and)
|
||||
mut kind := match expr.op {
|
||||
.ne {
|
||||
'orm__OperationKind__neq'
|
||||
@ -417,11 +419,19 @@ fn (mut g Gen) sql_where_data(expr ast.Expr, mut fields []string, mut kinds []st
|
||||
kind = 'orm__OperationKind__eq'
|
||||
}
|
||||
}
|
||||
if expr.left !is ast.InfixExpr && expr.right !is ast.InfixExpr {
|
||||
if expr.left !is ast.InfixExpr && expr.right !is ast.InfixExpr && kind != '' {
|
||||
kinds << kind
|
||||
}
|
||||
g.sql_side = .right
|
||||
g.sql_where_data(expr.right, mut fields, mut kinds, mut data, mut is_and)
|
||||
g.sql_where_data(expr.right, mut fields, mut parentheses, mut kinds, mut data, mut
|
||||
is_and)
|
||||
}
|
||||
ast.ParExpr {
|
||||
mut par := [fields.len]
|
||||
g.sql_where_data(expr.expr, mut fields, mut parentheses, mut kinds, mut data, mut
|
||||
is_and)
|
||||
par << fields.len - 1
|
||||
parentheses << par
|
||||
}
|
||||
ast.Ident {
|
||||
if g.sql_side == .left {
|
||||
@ -450,9 +460,11 @@ fn (mut g Gen) sql_gen_where_data(where_expr ast.Expr) {
|
||||
g.write('(orm__QueryData){')
|
||||
mut fields := []string{}
|
||||
mut kinds := []string{}
|
||||
mut parentheses := [][]int{}
|
||||
mut data := []ast.Expr{}
|
||||
mut is_and := []bool{}
|
||||
g.sql_where_data(where_expr, mut fields, mut kinds, mut data, mut is_and)
|
||||
g.sql_where_data(where_expr, mut fields, mut parentheses, mut kinds, mut data, mut
|
||||
is_and)
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
if fields.len > 0 {
|
||||
g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
|
||||
@ -476,6 +488,26 @@ fn (mut g Gen) sql_gen_where_data(where_expr ast.Expr) {
|
||||
}
|
||||
g.write('),')
|
||||
|
||||
g.write('.parentheses = ')
|
||||
if parentheses.len > 0 {
|
||||
g.write('new_array_from_c_array($parentheses.len, $parentheses.len, sizeof(Array_int), _MOV((Array_int[$parentheses.len]){')
|
||||
for par in parentheses {
|
||||
if par.len > 0 {
|
||||
g.write('new_array_from_c_array($par.len, $par.len, sizeof(int), _MOV((int[$par.len]){')
|
||||
for val in par {
|
||||
g.write('$val,')
|
||||
}
|
||||
g.write('})),')
|
||||
} else {
|
||||
g.write('__new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
}
|
||||
}
|
||||
g.write('}))')
|
||||
} else {
|
||||
g.write('__new_array_with_default_noscan(0, 0, sizeof(Array_int), 0)')
|
||||
}
|
||||
g.write(',')
|
||||
|
||||
if kinds.len > 0 {
|
||||
g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
|
||||
g.write(' _MOV((orm__OperationKind[$kinds.len]){')
|
||||
@ -618,6 +650,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
g.write('.parentheses = __new_array_with_default_noscan(0, 0, sizeof(Array_int), 0),')
|
||||
if exprs.len > 0 {
|
||||
g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
|
||||
g.write(' _MOV((orm__Primitive[$exprs.len]){')
|
||||
@ -637,6 +670,7 @@ fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string, or_expr as
|
||||
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||
g.write('.kinds = __new_array_with_default_noscan(0, 0, sizeof(orm__OperationKind), 0),')
|
||||
g.write('.is_and = __new_array_with_default_noscan(0, 0, sizeof(bool), 0),')
|
||||
g.write('.parentheses = __new_array_with_default_noscan(0, 0, sizeof(Array_int), 0),')
|
||||
g.write('.data = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0)')
|
||||
g.write('}')
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user