mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
parser: recursively search undefined variables in the where
parts of SQL statements (#17136)
This commit is contained in:
parent
1d4fd53344
commit
e064743c73
@ -31,20 +31,15 @@ fn (mut p Parser) sql_expr() ast.Expr {
|
||||
if has_where {
|
||||
p.next()
|
||||
where_expr = p.expr(0)
|
||||
// `id == x` means that a single object is returned
|
||||
if !is_count && mut where_expr is ast.InfixExpr {
|
||||
if where_expr.op == .eq && mut where_expr.left is ast.Ident {
|
||||
if where_expr.left.name == 'id' {
|
||||
query_one = true
|
||||
}
|
||||
}
|
||||
if mut where_expr.right is ast.Ident {
|
||||
if !p.scope.known_var(where_expr.right.name) {
|
||||
p.check_undefined_variables([where_expr.left.str()], where_expr.right) or {
|
||||
return p.error_with_pos(err.msg(), where_expr.right.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where_check_result := p.check_sql_where_expr_has_no_undefined_variables(&where_expr,
|
||||
[])
|
||||
if where_check_result is ast.NodeError {
|
||||
return where_check_result
|
||||
}
|
||||
|
||||
if !is_count {
|
||||
query_one = p.has_sql_where_expr_with_comparison_with_id(&where_expr)
|
||||
}
|
||||
}
|
||||
mut has_limit := false
|
||||
@ -304,3 +299,59 @@ fn (mut p Parser) check_sql_keyword(name string) ?bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// has_sql_where_expr_with_comparison_with_id tries to search comparison with the `id` field.
|
||||
// If `id` is found it means that a database "should" return one row,
|
||||
// but it is not true and depends on a database scheme.
|
||||
// For example, it will be hard to use V ORM in an existing database scheme which wrote before using V.
|
||||
fn (p &Parser) has_sql_where_expr_with_comparison_with_id(expr &ast.Expr) bool {
|
||||
// This method will be removed in the future when we refuse it.
|
||||
// A more difficult expression with `id` means a user tries to find more than one row or he is wrong.
|
||||
// And there is no point to get one structure instead of an array.
|
||||
// `id == x` means that a single object is returned.
|
||||
if expr is ast.InfixExpr {
|
||||
if expr.left is ast.Ident && expr.op == .eq {
|
||||
return expr.left.name == 'id'
|
||||
}
|
||||
} else if expr is ast.ParExpr {
|
||||
return p.has_sql_where_expr_with_comparison_with_id(&expr.expr)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// check_sql_where_expr_has_no_undefined_variables recursively tries to find undefined variables in the right part of infix expressions.
|
||||
fn (mut p Parser) check_sql_where_expr_has_no_undefined_variables(expr &ast.Expr, unacceptable_variable_names []string) ast.Expr {
|
||||
if expr is ast.Ident {
|
||||
if !p.scope.known_var(expr.name) {
|
||||
p.check_undefined_variables(unacceptable_variable_names, expr) or {
|
||||
return p.error_with_pos(err.msg(), expr.pos)
|
||||
}
|
||||
}
|
||||
} else if expr is ast.InfixExpr {
|
||||
if expr.left is ast.Ident && expr.right is ast.Ident {
|
||||
return p.check_sql_where_expr_has_no_undefined_variables(&expr.right, [
|
||||
expr.left.str(),
|
||||
])
|
||||
}
|
||||
|
||||
left_check_result := p.check_sql_where_expr_has_no_undefined_variables(&expr.left,
|
||||
[])
|
||||
|
||||
if left_check_result is ast.NodeError {
|
||||
return left_check_result
|
||||
}
|
||||
|
||||
variable_names := if expr.left is ast.Ident { [expr.left.str()] } else { []string{} }
|
||||
right_check_result := p.check_sql_where_expr_has_no_undefined_variables(&expr.right,
|
||||
variable_names)
|
||||
|
||||
if right_check_result is ast.NodeError {
|
||||
return right_check_result
|
||||
}
|
||||
} else if expr is ast.ParExpr {
|
||||
return p.check_sql_where_expr_has_no_undefined_variables(&expr.expr, [])
|
||||
}
|
||||
|
||||
return ast.empty_expr
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
vlib/v/parser/tests/sql_undefined_variables_in_complex_exprs.vv:13:73: error: undefined variable: `username`
|
||||
11 | fn main() {
|
||||
12 | _ := sql _ {
|
||||
13 | select from User where (age > 18) && (city == 'London' || username == username)
|
||||
| ~~~~~~~~
|
||||
14 | }
|
||||
15 | }
|
@ -0,0 +1,15 @@
|
||||
module main
|
||||
|
||||
struct User {
|
||||
mut:
|
||||
id int [primary]
|
||||
username string
|
||||
age string
|
||||
city string
|
||||
}
|
||||
|
||||
fn main() {
|
||||
_ := sql _ {
|
||||
select from User where (age > 18) && (city == 'London' || username == username)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user