diff --git a/src/ast/operator.rs b/src/ast/operator.rs index 58c401f7d..669470cda 100644 --- a/src/ast/operator.rs +++ b/src/ast/operator.rs @@ -144,6 +144,9 @@ pub enum BinaryOperator { Match, /// REGEXP operator, e.g. `a REGEXP b` (SQLite-specific) Regexp, + /// GLOB operator, e.g. `a GLOB b` (SQLite-specific) + /// See + Glob, /// Support for custom operators (such as Postgres custom operators) Custom(String), /// Bitwise XOR, e.g. `a # b` (PostgreSQL-specific) @@ -357,6 +360,7 @@ impl fmt::Display for BinaryOperator { BinaryOperator::MyIntegerDivide => f.write_str("DIV"), BinaryOperator::Match => f.write_str("MATCH"), BinaryOperator::Regexp => f.write_str("REGEXP"), + BinaryOperator::Glob => f.write_str("GLOB"), 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 9b2ede40d..20692012f 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -837,6 +837,7 @@ pub trait Dialect: Debug + Any { 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::GLOB => Ok(p!(Like)), Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)), Token::Word(w) if w.keyword == Keyword::MEMBER => Ok(p!(Like)), Token::Word(w) @@ -859,6 +860,7 @@ pub trait Dialect: Debug + Any { 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::GLOB => Ok(p!(Like)), Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)), Token::Word(w) if w.keyword == Keyword::MEMBER => Ok(p!(Like)), Token::Word(w) if w.keyword == Keyword::OPERATOR => Ok(p!(Between)), diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index 39ee622d8..0a08fd44e 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -81,11 +81,12 @@ impl Dialect for SQLiteDialect { expr: &crate::ast::Expr, _precedence: u8, ) -> Option> { - // Parse MATCH and REGEXP as operators + // Parse MATCH, REGEXP and GLOB as operators // See for (keyword, op) in [ (Keyword::REGEXP, BinaryOperator::Regexp), (Keyword::MATCH, BinaryOperator::Match), + (Keyword::GLOB, BinaryOperator::Glob), ] { if parser.parse_keyword(keyword) { let left = Box::new(expr.clone()); diff --git a/src/keywords.rs b/src/keywords.rs index 45dbb7764..d0364d752 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -469,6 +469,7 @@ define_keywords!( GET, GIN, GIST, + GLOB, GLOBAL, GRANT, GRANTED, diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index f9536bc28..70c73bf76 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -610,6 +610,24 @@ fn test_regexp_operator() { assert!(sqlite().parse_sql_statements("SELECT 1 MATCH").is_err()); } +#[test] +fn test_glob_operator() { + assert_eq!( + sqlite().verified_expr("col GLOB 'pattern'"), + Expr::BinaryOp { + op: BinaryOperator::Glob, + 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 files WHERE name GLOB '*.txt'"#); + + // Should return an error, not panic + assert!(sqlite().parse_sql_statements("SELECT 1 GLOB").is_err()); +} + #[test] fn test_update_delete_limit() { match sqlite().verified_stmt("UPDATE foo SET bar = 1 LIMIT 99") {