diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index f63e4cbe13..e39e24e87a 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -20,9 +20,14 @@ use alloc::boxed::Box; use crate::ast::BinaryOperator; use crate::ast::{Expr, Statement}; -use crate::dialect::Dialect; +use crate::dialect::{Dialect, Precedence}; use crate::keywords::Keyword; use crate::parser::{Parser, ParserError}; +use crate::tokenizer::Token; + +/// Precedence of `<`, `<=`, `>` and `>=`, between `=` and the bitwise operators, see +/// +const COMPARISON_PREC: u8 = 21; /// A [`Dialect`] for [SQLite](https://www.sqlite.org) /// @@ -97,6 +102,33 @@ impl Dialect for SQLiteDialect { None } + fn get_next_precedence(&self, parser: &Parser) -> Option> { + match parser.peek_token_ref().token { + Token::Lt | Token::LtEq | Token::Gt | Token::GtEq => Some(Ok(COMPARISON_PREC)), + _ => None, + } + } + + fn prec_value(&self, prec: Precedence) -> u8 { + match prec { + Precedence::Period => 100, + Precedence::DoubleColon => 50, + Precedence::AtTz => 41, + Precedence::MulDivModOp => 40, + Precedence::PlusMinus => 30, + Precedence::Xor => 25, + Precedence::Ampersand => 24, + Precedence::Caret => 23, + Precedence::Pipe | Precedence::Colon | Precedence::PgOther => 22, + Precedence::Between | Precedence::Eq => 20, + Precedence::Like => 19, + Precedence::Is => 17, + Precedence::UnaryNot => 15, + Precedence::And => 10, + Precedence::Or => 5, + } + } + fn supports_in_empty_list(&self) -> bool { true } diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 2e60c75efa..13349e432d 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -1142,6 +1142,78 @@ fn parse_parenthesized_table_factor() { ); } +#[test] +fn parse_comparison_binds_tighter_than_equality() { + let id = |name: &str| Expr::Identifier(Ident::new(name)); + let binary = |left: Expr, op: BinaryOperator, right: Expr| Expr::BinaryOp { + left: Box::new(left), + op, + right: Box::new(right), + }; + + for (sql, eq_op, cmp_op) in [ + ("a = b < c", BinaryOperator::Eq, BinaryOperator::Lt), + ("a <> b <= c", BinaryOperator::NotEq, BinaryOperator::LtEq), + ("a = b > c", BinaryOperator::Eq, BinaryOperator::Gt), + ("a <> b >= c", BinaryOperator::NotEq, BinaryOperator::GtEq), + ] { + assert_eq!( + sqlite().verified_expr(sql), + binary(id("a"), eq_op, binary(id("b"), cmp_op, id("c"))), + "{sql}" + ); + } + + assert_eq!( + sqlite().verified_expr("a < b = c"), + binary( + binary(id("a"), BinaryOperator::Lt, id("b")), + BinaryOperator::Eq, + id("c") + ), + ); + + // `IN` and `BETWEEN` sit on the level of `=` + assert_eq!( + sqlite().verified_expr("a < b IN (1)"), + Expr::InList { + expr: Box::new(binary(id("a"), BinaryOperator::Lt, id("b"))), + list: vec![Expr::value(number("1"))], + negated: false, + global: false, + }, + ); + assert_eq!( + sqlite().verified_expr("a BETWEEN b AND c < d"), + Expr::Between { + expr: Box::new(id("a")), + negated: false, + low: Box::new(id("b")), + high: Box::new(binary(id("c"), BinaryOperator::Lt, id("d"))), + }, + ); + + // Other dialects keep one level for every comparison + assert_eq!( + all_dialects_except(|d| d.is::()).verified_expr("a = b < c"), + binary( + binary(id("a"), BinaryOperator::Eq, id("b")), + BinaryOperator::Lt, + id("c") + ), + ); +} + +#[test] +fn parse_rejects_star_qualified_wildcard_in_boolean_context() { + assert_eq!( + sqlite().parse_sql_statements("SELECT * FROM t WHERE a.*"), + Err(ParserError::ParserError( + "Expected: end of statement, found: .".to_string() + )), + ); +} + fn sqlite() -> TestedDialects { TestedDialects::new(vec![Box::new(SQLiteDialect {})]) }