You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by if...@apache.org on 2025/05/08 23:48:27 UTC
(datafusion-sqlparser-rs) branch main updated: Add support for the MATCH and REGEXP binary operators (#1840)
This is an automated email from the ASF dual-hosted git repository.
iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 2182f7ea Add support for the MATCH and REGEXP binary operators (#1840)
2182f7ea is described below
commit 2182f7ea71242a7e9674932d559baef67dae522d
Author: Ophir LOJKINE <co...@ophir.dev>
AuthorDate: Fri May 9 01:48:23 2025 +0200
Add support for the MATCH and REGEXP binary operators (#1840)
---
src/ast/operator.rs | 7 +++++++
src/dialect/mod.rs | 2 ++
src/dialect/sqlite.rs | 27 ++++++++++++++++++++++++++-
tests/sqlparser_sqlite.rs | 30 ++++++++++++++++++++++++++++++
4 files changed, 65 insertions(+), 1 deletion(-)
diff --git a/src/ast/operator.rs b/src/ast/operator.rs
index 73fe9cf4..d0bb05e3 100644
--- a/src/ast/operator.rs
+++ b/src/ast/operator.rs
@@ -139,6 +139,11 @@ pub enum BinaryOperator {
DuckIntegerDivide,
/// MySQL [`DIV`](https://dev.mysql.com/doc/refman/8.0/en/arithmetic-functions.html) integer division
MyIntegerDivide,
+ /// MATCH operator, e.g. `a MATCH b` (SQLite-specific)
+ /// See <https://www.sqlite.org/lang_expr.html#the_like_glob_regexp_match_and_extract_operators>
+ Match,
+ /// REGEXP operator, e.g. `a REGEXP b` (SQLite-specific)
+ Regexp,
/// Support for custom operators (such as Postgres custom operators)
Custom(String),
/// Bitwise XOR, e.g. `a # b` (PostgreSQL-specific)
@@ -350,6 +355,8 @@ impl fmt::Display for BinaryOperator {
BinaryOperator::BitwiseXor => f.write_str("^"),
BinaryOperator::DuckIntegerDivide => f.write_str("//"),
BinaryOperator::MyIntegerDivide => f.write_str("DIV"),
+ BinaryOperator::Match => f.write_str("MATCH"),
+ BinaryOperator::Regexp => f.write_str("REGEXP"),
BinaryOperator::Custom(s) => f.write_str(s),
BinaryOperator::PGBitwiseXor => f.write_str("#"),
BinaryOperator::PGBitwiseShiftLeft => f.write_str("<<"),
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index b754a04f..6fbbc7a2 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -619,6 +619,7 @@ pub trait Dialect: Debug + Any {
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(p!(Like)),
+ Token::Word(w) if w.keyword == Keyword::MATCH => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)),
_ => Ok(self.prec_unknown()),
},
@@ -630,6 +631,7 @@ pub trait Dialect: Debug + Any {
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(p!(Like)),
+ Token::Word(w) if w.keyword == Keyword::MATCH => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)),
Token::Word(w) if w.keyword == Keyword::OPERATOR => Ok(p!(Between)),
Token::Word(w) if w.keyword == Keyword::DIV => Ok(p!(MulDivModOp)),
diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs
index 138c4692..847e0d13 100644
--- a/src/dialect/sqlite.rs
+++ b/src/dialect/sqlite.rs
@@ -15,7 +15,11 @@
// specific language governing permissions and limitations
// under the License.
-use crate::ast::Statement;
+#[cfg(not(feature = "std"))]
+use alloc::boxed::Box;
+
+use crate::ast::BinaryOperator;
+use crate::ast::{Expr, Statement};
use crate::dialect::Dialect;
use crate::keywords::Keyword;
use crate::parser::{Parser, ParserError};
@@ -70,6 +74,27 @@ impl Dialect for SQLiteDialect {
}
}
+ fn parse_infix(
+ &self,
+ parser: &mut crate::parser::Parser,
+ expr: &crate::ast::Expr,
+ _precedence: u8,
+ ) -> Option<Result<crate::ast::Expr, ParserError>> {
+ // Parse MATCH and REGEXP as operators
+ // See <https://www.sqlite.org/lang_expr.html#the_like_glob_regexp_match_and_extract_operators>
+ for (keyword, op) in [
+ (Keyword::REGEXP, BinaryOperator::Regexp),
+ (Keyword::MATCH, BinaryOperator::Match),
+ ] {
+ if parser.parse_keyword(keyword) {
+ let left = Box::new(expr.clone());
+ let right = Box::new(parser.parse_expr().unwrap());
+ return Some(Ok(Expr::BinaryOp { left, op, right }));
+ }
+ }
+ None
+ }
+
fn supports_in_empty_list(&self) -> bool {
true
}
diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs
index 361c9b05..b759065f 100644
--- a/tests/sqlparser_sqlite.rs
+++ b/tests/sqlparser_sqlite.rs
@@ -562,6 +562,36 @@ fn test_dollar_identifier_as_placeholder() {
}
}
+#[test]
+fn test_match_operator() {
+ assert_eq!(
+ sqlite().verified_expr("col MATCH 'pattern'"),
+ Expr::BinaryOp {
+ op: BinaryOperator::Match,
+ left: Box::new(Expr::Identifier(Ident::new("col"))),
+ right: Box::new(Expr::Value(
+ (Value::SingleQuotedString("pattern".to_string())).with_empty_span()
+ ))
+ }
+ );
+ sqlite().verified_only_select("SELECT * FROM email WHERE email MATCH 'fts5'");
+}
+
+#[test]
+fn test_regexp_operator() {
+ assert_eq!(
+ sqlite().verified_expr("col REGEXP 'pattern'"),
+ Expr::BinaryOp {
+ op: BinaryOperator::Regexp,
+ left: Box::new(Expr::Identifier(Ident::new("col"))),
+ right: Box::new(Expr::Value(
+ (Value::SingleQuotedString("pattern".to_string())).with_empty_span()
+ ))
+ }
+ );
+ sqlite().verified_only_select(r#"SELECT count(*) FROM messages WHERE msg_text REGEXP '\d+'"#);
+}
+
fn sqlite() -> TestedDialects {
TestedDialects::new(vec![Box::new(SQLiteDialect {})])
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org