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 {
|
if has_where {
|
||||||
p.next()
|
p.next()
|
||||||
where_expr = p.expr(0)
|
where_expr = p.expr(0)
|
||||||
// `id == x` means that a single object is returned
|
|
||||||
if !is_count && mut where_expr is ast.InfixExpr {
|
where_check_result := p.check_sql_where_expr_has_no_undefined_variables(&where_expr,
|
||||||
if where_expr.op == .eq && mut where_expr.left is ast.Ident {
|
[])
|
||||||
if where_expr.left.name == 'id' {
|
if where_check_result is ast.NodeError {
|
||||||
query_one = true
|
return where_check_result
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if mut where_expr.right is ast.Ident {
|
if !is_count {
|
||||||
if !p.scope.known_var(where_expr.right.name) {
|
query_one = p.has_sql_where_expr_with_comparison_with_id(&where_expr)
|
||||||
p.check_undefined_variables([where_expr.left.str()], where_expr.right) or {
|
|
||||||
return p.error_with_pos(err.msg(), where_expr.right.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mut has_limit := false
|
mut has_limit := false
|
||||||
@ -304,3 +299,59 @@ fn (mut p Parser) check_sql_keyword(name string) ?bool {
|
|||||||
}
|
}
|
||||||
return true
|
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