mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-03 22:08:16 +00:00
Fix the precedence of NOT LIKE
NOT LIKE has the same precedence as the LIKE operator. The parser was previously assigning it the precedence of the unary NOT operator. NOT BETWEEN and NOT IN are treated similarly, as they are equivalent, from a precedence perspective, to NOT LIKE. The fix for this requires associating precedences with sequences of tokens, rather than single tokens, so that "NOT LIKE" and "NOT <expr>" can have different preferences. Perhaps surprisingly, this change is not very invasive. An alternative I considered involved adjusting the tokenizer to lex NOT, NOT LIKE, NOT BETWEEN, and NOT IN as separate tokens. This broke symmetry in strange ways, though, as NotLike, NotBetween, and NotIn gained dedicated tokens, while LIKE, BETWEEN, and IN remained as stringly identifiers. Fixes #81.
This commit is contained in:
parent
f55e3d5305
commit
90bcf55a6a
2 changed files with 125 additions and 60 deletions
|
@ -413,38 +413,98 @@ fn parse_not_precedence() {
|
|||
operator: SQLOperator::Not,
|
||||
..
|
||||
});
|
||||
|
||||
// NOT has lower precedence than BETWEEN, so the following parses as NOT (1 NOT BETWEEN 1 AND 2)
|
||||
let sql = "NOT 1 NOT BETWEEN 1 AND 2";
|
||||
assert_eq!(
|
||||
verified_expr(sql),
|
||||
SQLUnary {
|
||||
operator: SQLOperator::Not,
|
||||
expr: Box::new(SQLBetween {
|
||||
expr: Box::new(SQLValue(Value::Long(1))),
|
||||
low: Box::new(SQLValue(Value::Long(1))),
|
||||
high: Box::new(SQLValue(Value::Long(2))),
|
||||
negated: true,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
// NOT has lower precedence than LIKE, so the following parses as NOT ('a' NOT LIKE 'b')
|
||||
let sql = "NOT 'a' NOT LIKE 'b'";
|
||||
assert_eq!(
|
||||
verified_expr(sql),
|
||||
SQLUnary {
|
||||
operator: SQLOperator::Not,
|
||||
expr: Box::new(SQLBinaryExpr {
|
||||
left: Box::new(SQLValue(Value::SingleQuotedString("a".into()))),
|
||||
op: SQLOperator::NotLike,
|
||||
right: Box::new(SQLValue(Value::SingleQuotedString("b".into()))),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
// NOT has lower precedence than IN, so the following parses as NOT (a NOT IN 'a')
|
||||
let sql = "NOT a NOT IN ('a')";
|
||||
assert_eq!(
|
||||
verified_expr(sql),
|
||||
SQLUnary {
|
||||
operator: SQLOperator::Not,
|
||||
expr: Box::new(SQLInList {
|
||||
expr: Box::new(SQLIdentifier("a".into())),
|
||||
list: vec![SQLValue(Value::SingleQuotedString("a".into()))],
|
||||
negated: true,
|
||||
}),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_like() {
|
||||
let sql = "SELECT * FROM customers WHERE name LIKE '%a'";
|
||||
let select = verified_only_select(sql);
|
||||
assert_eq!(
|
||||
ASTNode::SQLBinaryExpr {
|
||||
left: Box::new(ASTNode::SQLIdentifier("name".to_string())),
|
||||
op: SQLOperator::Like,
|
||||
right: Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
|
||||
"%a".to_string()
|
||||
))),
|
||||
},
|
||||
select.selection.unwrap()
|
||||
);
|
||||
}
|
||||
fn chk(negated: bool) {
|
||||
let sql = &format!(
|
||||
"SELECT * FROM customers WHERE name {}LIKE '%a'",
|
||||
if negated { "NOT " } else { "" }
|
||||
);
|
||||
let select = verified_only_select(sql);
|
||||
assert_eq!(
|
||||
ASTNode::SQLBinaryExpr {
|
||||
left: Box::new(ASTNode::SQLIdentifier("name".to_string())),
|
||||
op: if negated {
|
||||
SQLOperator::NotLike
|
||||
} else {
|
||||
SQLOperator::Like
|
||||
},
|
||||
right: Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
|
||||
"%a".to_string()
|
||||
))),
|
||||
},
|
||||
select.selection.unwrap()
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn parse_not_like() {
|
||||
let sql = "SELECT * FROM customers WHERE name NOT LIKE '%a'";
|
||||
let select = verified_only_select(sql);
|
||||
assert_eq!(
|
||||
ASTNode::SQLBinaryExpr {
|
||||
left: Box::new(ASTNode::SQLIdentifier("name".to_string())),
|
||||
op: SQLOperator::NotLike,
|
||||
right: Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
|
||||
"%a".to_string()
|
||||
))),
|
||||
},
|
||||
select.selection.unwrap()
|
||||
);
|
||||
// This statement tests that LIKE and NOT LIKE have the same precedence.
|
||||
// This was previously mishandled (#81).
|
||||
let sql = &format!(
|
||||
"SELECT * FROM customers WHERE name {}LIKE '%a' IS NULL",
|
||||
if negated { "NOT " } else { "" }
|
||||
);
|
||||
let select = verified_only_select(sql);
|
||||
assert_eq!(
|
||||
ASTNode::SQLIsNull(Box::new(ASTNode::SQLBinaryExpr {
|
||||
left: Box::new(ASTNode::SQLIdentifier("name".to_string())),
|
||||
op: if negated {
|
||||
SQLOperator::NotLike
|
||||
} else {
|
||||
SQLOperator::Like
|
||||
},
|
||||
right: Box::new(ASTNode::SQLValue(Value::SingleQuotedString(
|
||||
"%a".to_string()
|
||||
))),
|
||||
})),
|
||||
select.selection.unwrap()
|
||||
);
|
||||
}
|
||||
chk(false);
|
||||
chk(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue