Skip to content

Commit 1cab851

Browse files
committed
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
1 parent 6de8aff commit 1cab851

File tree

2 files changed

+33
-5
lines changed

2 files changed

+33
-5
lines changed

src/sqlparser.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,13 @@ impl Parser {
165165
}
166166
"CASE" => self.parse_case_expression(),
167167
"CAST" => self.parse_cast_expression(),
168-
"NOT" => Ok(ASTNode::SQLUnary {
169-
operator: SQLOperator::Not,
170-
expr: Box::new(self.parse_subexpr(0)?), // TBD (2)
171-
}),
168+
"NOT" => {
169+
let p = self.get_precedence(&Token::make_keyword("NOT"))?;
170+
Ok(ASTNode::SQLUnary {
171+
operator: SQLOperator::Not,
172+
expr: Box::new(self.parse_subexpr(p)?),
173+
})
174+
}
172175
_ => match self.peek_token() {
173176
Some(Token::LParen) => self.parse_function(&w.value),
174177
Some(Token::Period) => {
@@ -371,7 +374,7 @@ impl Parser {
371374
&Token::SQLWord(ref k) if k.keyword == "OR" => Ok(5),
372375
&Token::SQLWord(ref k) if k.keyword == "AND" => Ok(10),
373376
&Token::SQLWord(ref k) if k.keyword == "NOT" => Ok(15),
374-
&Token::SQLWord(ref k) if k.keyword == "IS" => Ok(15),
377+
&Token::SQLWord(ref k) if k.keyword == "IS" => Ok(17),
375378
&Token::SQLWord(ref k) if k.keyword == "LIKE" => Ok(20),
376379
&Token::Eq | &Token::Lt | &Token::LtEq | &Token::Neq | &Token::Gt | &Token::GtEq => {
377380
Ok(20)

tests/sqlparser_generic.rs

+25
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,31 @@ fn parse_is_not_null() {
187187
);
188188
}
189189

190+
#[test]
191+
fn parse_not_precedence() {
192+
use self::ASTNode::*;
193+
// NOT has higher precedence than OR/AND, so the following must parse as (NOT true) OR true
194+
let sql = String::from("NOT true OR true");
195+
match verified_expr(&sql) {
196+
SQLBinaryExpr {
197+
op: SQLOperator::Or,
198+
..
199+
} => assert!(true),
200+
_ => assert!(false),
201+
};
202+
203+
// But NOT has lower precedence than comparison operators, so the following parses as NOT (a IS NULL)
204+
let sql = String::from("NOT a IS NULL");
205+
println!("{:#?}", verified_expr(&sql));
206+
match verified_expr(&sql) {
207+
SQLUnary {
208+
operator: SQLOperator::Not,
209+
..
210+
} => assert!(true),
211+
_ => assert!(false),
212+
};
213+
}
214+
190215
#[test]
191216
fn parse_like() {
192217
let sql = String::from("SELECT * FROM customers WHERE name LIKE '%a'");

0 commit comments

Comments
 (0)