From e980a1a8f978d5cd220e021112ef23b0c7424569 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Fri, 31 Jul 2026 16:15:49 +0200 Subject: [PATCH] Honour operator precedence in SQLite REGEXP, MATCH and GLOB --- src/dialect/sqlite.rs | 4 ++-- tests/sqlparser_sqlite.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index 37ede9fc1..d549c7507 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -79,7 +79,7 @@ impl Dialect for SQLiteDialect { &self, parser: &mut crate::parser::Parser, expr: &crate::ast::Expr, - _precedence: u8, + precedence: u8, ) -> Option> { // Parse MATCH, REGEXP and GLOB as operators // See @@ -90,7 +90,7 @@ impl Dialect for SQLiteDialect { ] { if parser.parse_keyword(keyword) { let left = Box::new(expr.clone()); - let right = Box::new(match parser.parse_expr() { + let right = Box::new(match parser.parse_subexpr(precedence) { Ok(expr) => expr, Err(e) => return Some(Err(e)), }); diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index b72d294f5..a017562df 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -926,6 +926,37 @@ fn test_drop_trigger() { } } +#[test] +fn parse_pattern_operators_bind_at_like_precedence() { + fn where_operator(sql: &str) -> BinaryOperator { + let Statement::Query(query) = sqlite().verified_stmt(sql) else { + panic!("expected a query"); + }; + let SetExpr::Select(select) = *query.body else { + panic!("expected a select"); + }; + let Some(Expr::BinaryOp { op, .. }) = select.selection else { + panic!("expected a WHERE binary operator"); + }; + op + } + + // Above AND, so the pattern does not swallow the rest of the expression. + for operator in ["REGEXP", "MATCH", "GLOB", "LIKE"] { + let sql = format!("SELECT 1 FROM t WHERE a {operator} 'p' AND b = 1"); + assert_eq!(where_operator(&sql), BinaryOperator::And, "{operator}"); + } + // Below string concatenation, so the pattern is not cut short either. + for (operator, expected) in [ + ("REGEXP", BinaryOperator::Regexp), + ("MATCH", BinaryOperator::Match), + ("GLOB", BinaryOperator::Glob), + ] { + let sql = format!("SELECT 1 FROM t WHERE a {operator} 'p' || 'q'"); + assert_eq!(where_operator(&sql), expected, "{operator}"); + } +} + fn sqlite() -> TestedDialects { TestedDialects::new(vec![Box::new(SQLiteDialect {})]) }