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];
|
// => 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
|
// 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
|
// 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 struct QueryData {
|
||||||
pub:
|
pub:
|
||||||
fields []string
|
fields []string
|
||||||
data []Primitive
|
data []Primitive
|
||||||
types []int
|
types []int
|
||||||
kinds []OperationKind
|
parentheses [][]int
|
||||||
is_and []bool
|
kinds []OperationKind
|
||||||
|
is_and []bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InfixType {
|
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 {
|
if kind == .update || kind == .delete {
|
||||||
for i, field in where.fields {
|
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'
|
str += '$q$field$q ${where.kinds[i].to_str()} $qm'
|
||||||
if num {
|
if num {
|
||||||
str += '$c'
|
str += '$c'
|
||||||
c++
|
c++
|
||||||
}
|
}
|
||||||
|
if post_par {
|
||||||
|
str += ')'
|
||||||
|
}
|
||||||
if i < where.fields.len - 1 {
|
if i < where.fields.len - 1 {
|
||||||
str += ' AND '
|
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 {
|
if orm.has_where {
|
||||||
str += ' WHERE '
|
str += ' WHERE '
|
||||||
for i, field in where.fields {
|
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'
|
str += '$q$field$q ${where.kinds[i].to_str()} $qm'
|
||||||
if num {
|
if num {
|
||||||
str += '$c'
|
str += '$c'
|
||||||
c++
|
c++
|
||||||
}
|
}
|
||||||
|
if post_par {
|
||||||
|
str += ')'
|
||||||
|
}
|
||||||
if i < where.fields.len - 1 {
|
if i < where.fields.len - 1 {
|
||||||
if where.is_and[i] {
|
if where.is_and[i] {
|
||||||
str += ' AND '
|
str += ' AND '
|
||||||
|
@ -9,7 +9,7 @@ struct User {
|
|||||||
const db_path = os.join_path(os.temp_dir(), 'sql_statement_or_blocks.db')
|
const db_path = os.join_path(os.temp_dir(), 'sql_statement_or_blocks.db')
|
||||||
|
|
||||||
fn test_ensure_db_exists_and_user_table_is_ok() ? {
|
fn test_ensure_db_exists_and_user_table_is_ok() ? {
|
||||||
db := sqlite.connect(db_path)?
|
mut db := sqlite.connect(db_path)?
|
||||||
assert true
|
assert true
|
||||||
|
|
||||||
eprintln('> drop pre-existing User table...')
|
eprintln('> drop pre-existing User table...')
|
||||||
@ -20,10 +20,11 @@ fn test_ensure_db_exists_and_user_table_is_ok() ? {
|
|||||||
create table User
|
create table User
|
||||||
} or { panic(err) }
|
} or { panic(err) }
|
||||||
assert true
|
assert true
|
||||||
|
db.close()?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_sql_or_block_for_insert() ? {
|
fn test_sql_or_block_for_insert() ? {
|
||||||
db := sqlite.connect(db_path)?
|
mut db := sqlite.connect(db_path)?
|
||||||
user := User{1, 'bilbo'}
|
user := User{1, 'bilbo'}
|
||||||
|
|
||||||
eprintln('> inserting user 1 (first try)...')
|
eprintln('> inserting user 1 (first try)...')
|
||||||
@ -41,10 +42,12 @@ fn test_sql_or_block_for_insert() ? {
|
|||||||
assert true
|
assert true
|
||||||
println('user could not be inserted, err: $err')
|
println('user could not be inserted, err: $err')
|
||||||
}
|
}
|
||||||
|
eprintln('LINE: ${@LINE}')
|
||||||
|
db.close()?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_sql_or_block_for_select() ? {
|
fn test_sql_or_block_for_select() ? {
|
||||||
db := sqlite.connect(db_path)?
|
mut db := sqlite.connect(db_path)?
|
||||||
|
|
||||||
eprintln('> selecting user with id 1...')
|
eprintln('> selecting user with id 1...')
|
||||||
single := sql db {
|
single := sql db {
|
||||||
@ -53,6 +56,7 @@ fn test_sql_or_block_for_select() ? {
|
|||||||
eprintln('could not select user, err: $err')
|
eprintln('could not select user, err: $err')
|
||||||
User{0, ''}
|
User{0, ''}
|
||||||
}
|
}
|
||||||
|
eprintln('LINE: ${@LINE}')
|
||||||
|
|
||||||
assert single.id == 1
|
assert single.id == 1
|
||||||
|
|
||||||
@ -62,9 +66,11 @@ fn test_sql_or_block_for_select() ? {
|
|||||||
eprintln('could not select user, err: $err')
|
eprintln('could not select user, err: $err')
|
||||||
User{0, ''}
|
User{0, ''}
|
||||||
}
|
}
|
||||||
|
eprintln('LINE: ${@LINE}')
|
||||||
|
|
||||||
assert failed.id == 0
|
assert failed.id == 0
|
||||||
assert failed.name == ''
|
assert failed.name == ''
|
||||||
|
eprintln('LINE: ${@LINE}')
|
||||||
|
|
||||||
eprintln('> selecting users...')
|
eprintln('> selecting users...')
|
||||||
multiple := sql db {
|
multiple := sql db {
|
||||||
@ -73,8 +79,11 @@ fn test_sql_or_block_for_select() ? {
|
|||||||
eprintln('could not users, err: $err')
|
eprintln('could not users, err: $err')
|
||||||
[]User{}
|
[]User{}
|
||||||
}
|
}
|
||||||
|
eprintln('LINE: ${@LINE}')
|
||||||
|
|
||||||
assert multiple.len == 1
|
assert multiple.len == 1
|
||||||
|
eprintln('LINE: ${@LINE}')
|
||||||
|
db.close()?
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_finish() ? {
|
fn test_finish() ? {
|
||||||
|
@ -360,6 +360,12 @@ fn test_orm() {
|
|||||||
// has them zeroed, because the db field resolution is seconds.
|
// has them zeroed, because the db field resolution is seconds.
|
||||||
assert updated_time_mod.created.format_ss() == t.format_ss()
|
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 {
|
sql db {
|
||||||
drop table Module
|
drop table Module
|
||||||
drop table TestTime
|
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 {
|
pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ?[][]orm.Primitive {
|
||||||
// 1. Create query and bind necessary data
|
// 1. Create query and bind necessary data
|
||||||
query := orm.orm_select_gen(config, '`', true, '?', 1, where)
|
query := orm.orm_select_gen(config, '`', true, '?', 1, where)
|
||||||
|
$if trace_sqlite ? {
|
||||||
|
eprintln('> @select query: "$query"')
|
||||||
|
}
|
||||||
stmt := db.new_init_stmt(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 {
|
defer {
|
||||||
stmt.finalize()
|
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{}
|
mut ret := [][]orm.Primitive{}
|
||||||
|
|
||||||
@ -89,12 +91,17 @@ pub fn (db DB) drop(table string) ? {
|
|||||||
|
|
||||||
// Executes query and bind prepared statement data directly
|
// Executes query and bind prepared statement data directly
|
||||||
fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ? {
|
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)?
|
stmt := db.new_init_stmt(query)?
|
||||||
|
defer {
|
||||||
|
stmt.finalize()
|
||||||
|
}
|
||||||
mut c := 1
|
mut c := 1
|
||||||
sqlite_stmt_binder(stmt, data, query, mut c)?
|
sqlite_stmt_binder(stmt, data, query, mut c)?
|
||||||
sqlite_stmt_binder(stmt, where, query, mut c)?
|
sqlite_stmt_binder(stmt, where, query, mut c)?
|
||||||
stmt.orm_step(query)?
|
stmt.orm_step(query)?
|
||||||
stmt.finalize()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binds all values of d in the prepared statement
|
// 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('.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('.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('.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 {
|
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('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
|
||||||
g.write(' _MOV((string[$node.updated_columns.len]){')
|
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('),')
|
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 {
|
match expr {
|
||||||
ast.InfixExpr {
|
ast.InfixExpr {
|
||||||
g.sql_side = .left
|
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 {
|
mut kind := match expr.op {
|
||||||
.ne {
|
.ne {
|
||||||
'orm__OperationKind__neq'
|
'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'
|
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
|
kinds << kind
|
||||||
}
|
}
|
||||||
g.sql_side = .right
|
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 {
|
ast.Ident {
|
||||||
if g.sql_side == .left {
|
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){')
|
g.write('(orm__QueryData){')
|
||||||
mut fields := []string{}
|
mut fields := []string{}
|
||||||
mut kinds := []string{}
|
mut kinds := []string{}
|
||||||
|
mut parentheses := [][]int{}
|
||||||
mut data := []ast.Expr{}
|
mut data := []ast.Expr{}
|
||||||
mut is_and := []bool{}
|
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),')
|
g.write('.types = __new_array_with_default_noscan(0, 0, sizeof(int), 0),')
|
||||||
if fields.len > 0 {
|
if fields.len > 0 {
|
||||||
g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
|
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('),')
|
||||||
|
|
||||||
|
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 {
|
if kinds.len > 0 {
|
||||||
g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
|
g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
|
||||||
g.write(' _MOV((orm__OperationKind[$kinds.len]){')
|
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('.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('.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('.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 {
|
if exprs.len > 0 {
|
||||||
g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
|
g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
|
||||||
g.write(' _MOV((orm__Primitive[$exprs.len]){')
|
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('.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('.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('.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('.data = __new_array_with_default_noscan(0, 0, sizeof(orm__Primitive), 0)')
|
||||||
g.write('}')
|
g.write('}')
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user