1
0
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:
walking devel 2023-01-28 08:07:02 +00:00 committed by GitHub
parent 1d4fd53344
commit e064743c73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 14 deletions

View File

@ -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
}

View File

@ -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 | }

View File

@ -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)
}
}