Fix precedence for the NOT operator (2/4)

I checked the docs of a few of the most popular RDBMSes, and it seems
there's consensus that the precedence of `NOT` is higher than `AND`,
but lower than `IS NULL`.

Postgresql[1], Oracle[2] and MySQL[3] docs say that explicitly.

T-SQL docs[4] do mention it's higher than `AND`, and while they don't
explicitly mention IS NULL, this snippet:

    select * from (select 1 as a)x
    where (not x.a) is null

...is a parsing error, while the following works like IS NOT NULL:

    select * from (select 1 as a)x
    where not x.a is null

sqlite doesn't seem to mention `NOT` precedence, but I assume it works
similarly.

[1] https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-OPERATORS
[2] https://docs.oracle.com/cd/B19306_01/server.102/b14200/conditions001.htm#i1034834
[3] https://dev.mysql.com/doc/refman/8.0/en/operator-precedence.html
[4] https://docs.microsoft.com/en-us/sql/t-sql/language-elements/operator-precedence-transact-sql?view=sql-server-2017
This commit is contained in:
Nickolay Ponomarev 2019-01-31 00:10:26 +03:00
parent 29db619792
commit 82dc581639
2 changed files with 32 additions and 5 deletions

View file

@ -165,10 +165,13 @@ impl Parser {
}
"CASE" => self.parse_case_expression(),
"CAST" => self.parse_cast_expression(),
"NOT" => Ok(ASTNode::SQLUnary {
operator: SQLOperator::Not,
expr: Box::new(self.parse_subexpr(0)?), // TBD (2)
}),
"NOT" => {
let p = self.get_precedence(&Token::make_keyword("NOT"))?;
Ok(ASTNode::SQLUnary {
operator: SQLOperator::Not,
expr: Box::new(self.parse_subexpr(p)?),
})
}
_ => match self.peek_token() {
Some(Token::LParen) => self.parse_function(&w.value),
Some(Token::Period) => {
@ -371,7 +374,7 @@ impl Parser {
&Token::SQLWord(ref k) if k.keyword == "OR" => Ok(5),
&Token::SQLWord(ref k) if k.keyword == "AND" => Ok(10),
&Token::SQLWord(ref k) if k.keyword == "NOT" => Ok(15),
&Token::SQLWord(ref k) if k.keyword == "IS" => Ok(15),
&Token::SQLWord(ref k) if k.keyword == "IS" => Ok(17),
&Token::SQLWord(ref k) if k.keyword == "LIKE" => Ok(20),
&Token::Eq | &Token::Lt | &Token::LtEq | &Token::Neq | &Token::Gt | &Token::GtEq => {
Ok(20)

View file

@ -182,6 +182,30 @@ fn parse_is_not_null() {
);
}
#[test]
fn parse_not_precedence() {
use self::ASTNode::*;
// NOT has higher precedence than OR/AND, so the following must parse as (NOT true) OR true
let sql = "NOT true OR true";
match verified_expr(sql) {
SQLBinaryExpr {
op: SQLOperator::Or,
..
} => assert!(true),
_ => assert!(false),
};
// But NOT has lower precedence than comparison operators, so the following parses as NOT (a IS NULL)
let sql = "NOT a IS NULL";
match verified_expr(sql) {
SQLUnary {
operator: SQLOperator::Not,
..
} => assert!(true),
_ => assert!(false),
};
}
#[test]
fn parse_like() {
let sql = String::from("SELECT * FROM customers WHERE name LIKE '%a'");