mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Add support for MySQL MEMBER OF (#1917)
This commit is contained in:
parent
418b94227a
commit
be2d2f14e7
5 changed files with 65 additions and 0 deletions
|
@ -1124,6 +1124,8 @@ pub enum Expr {
|
||||||
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html)
|
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-lambda-functions.html)
|
||||||
/// [DuckDb](https://duckdb.org/docs/sql/functions/lambda.html)
|
/// [DuckDb](https://duckdb.org/docs/sql/functions/lambda.html)
|
||||||
Lambda(LambdaFunction),
|
Lambda(LambdaFunction),
|
||||||
|
/// Checks membership of a value in a JSON array
|
||||||
|
MemberOf(MemberOf),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
|
@ -1912,6 +1914,7 @@ impl fmt::Display for Expr {
|
||||||
}
|
}
|
||||||
Expr::Prior(expr) => write!(f, "PRIOR {expr}"),
|
Expr::Prior(expr) => write!(f, "PRIOR {expr}"),
|
||||||
Expr::Lambda(lambda) => write!(f, "{lambda}"),
|
Expr::Lambda(lambda) => write!(f, "{lambda}"),
|
||||||
|
Expr::MemberOf(member_of) => write!(f, "{member_of}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9831,6 +9834,27 @@ impl fmt::Display for NullInclusion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks membership of a value in a JSON array
|
||||||
|
///
|
||||||
|
/// Syntax:
|
||||||
|
/// ```sql
|
||||||
|
/// <value> MEMBER OF(<array>)
|
||||||
|
/// ```
|
||||||
|
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html#operator_member-of)
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct MemberOf {
|
||||||
|
pub value: Box<Expr>,
|
||||||
|
pub array: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MemberOf {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{} MEMBER OF({})", self.value, self.array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tokenizer::Location;
|
use crate::tokenizer::Location;
|
||||||
|
|
|
@ -1624,6 +1624,7 @@ impl Spanned for Expr {
|
||||||
Expr::OuterJoin(expr) => expr.span(),
|
Expr::OuterJoin(expr) => expr.span(),
|
||||||
Expr::Prior(expr) => expr.span(),
|
Expr::Prior(expr) => expr.span(),
|
||||||
Expr::Lambda(_) => Span::empty(),
|
Expr::Lambda(_) => Span::empty(),
|
||||||
|
Expr::MemberOf(member_of) => member_of.value.span().union(&member_of.array.span()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -649,6 +649,7 @@ pub trait Dialect: Debug + Any {
|
||||||
Token::Word(w) if w.keyword == Keyword::REGEXP => 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::MATCH => Ok(p!(Like)),
|
||||||
Token::Word(w) if w.keyword == Keyword::SIMILAR => 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)),
|
||||||
_ => Ok(self.prec_unknown()),
|
_ => Ok(self.prec_unknown()),
|
||||||
},
|
},
|
||||||
Token::Word(w) if w.keyword == Keyword::IS => Ok(p!(Is)),
|
Token::Word(w) if w.keyword == Keyword::IS => Ok(p!(Is)),
|
||||||
|
@ -661,6 +662,7 @@ pub trait Dialect: Debug + Any {
|
||||||
Token::Word(w) if w.keyword == Keyword::REGEXP => 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::MATCH => Ok(p!(Like)),
|
||||||
Token::Word(w) if w.keyword == Keyword::SIMILAR => 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)),
|
Token::Word(w) if w.keyword == Keyword::OPERATOR => Ok(p!(Between)),
|
||||||
Token::Word(w) if w.keyword == Keyword::DIV => Ok(p!(MulDivModOp)),
|
Token::Word(w) if w.keyword == Keyword::DIV => Ok(p!(MulDivModOp)),
|
||||||
Token::Period => Ok(p!(Period)),
|
Token::Period => Ok(p!(Period)),
|
||||||
|
|
|
@ -3609,6 +3609,19 @@ impl<'a> Parser<'a> {
|
||||||
self.expected("IN or BETWEEN after NOT", self.peek_token())
|
self.expected("IN or BETWEEN after NOT", self.peek_token())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Keyword::MEMBER => {
|
||||||
|
if self.parse_keyword(Keyword::OF) {
|
||||||
|
self.expect_token(&Token::LParen)?;
|
||||||
|
let array = self.parse_expr()?;
|
||||||
|
self.expect_token(&Token::RParen)?;
|
||||||
|
Ok(Expr::MemberOf(MemberOf {
|
||||||
|
value: Box::new(expr),
|
||||||
|
array: Box::new(array),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
self.expected("OF after MEMBER", self.peek_token())
|
||||||
|
}
|
||||||
|
}
|
||||||
// Can only happen if `get_next_precedence` got out of sync with this function
|
// Can only happen if `get_next_precedence` got out of sync with this function
|
||||||
_ => parser_err!(
|
_ => parser_err!(
|
||||||
format!("No infix parser for token {:?}", tok.token),
|
format!("No infix parser for token {:?}", tok.token),
|
||||||
|
|
|
@ -4109,3 +4109,28 @@ fn parse_alter_table_drop_index() {
|
||||||
AlterTableOperation::DropIndex { name } if name.value == "idx_index"
|
AlterTableOperation::DropIndex { name } if name.value == "idx_index"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_json_member_of() {
|
||||||
|
mysql().verified_stmt(r#"SELECT 17 MEMBER OF('[23, "abc", 17, "ab", 10]')"#);
|
||||||
|
let sql = r#"SELECT 'ab' MEMBER OF('[23, "abc", 17, "ab", 10]')"#;
|
||||||
|
let stmt = mysql().verified_stmt(sql);
|
||||||
|
match stmt {
|
||||||
|
Statement::Query(query) => {
|
||||||
|
let select = query.body.as_select().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
select.projection,
|
||||||
|
vec![SelectItem::UnnamedExpr(Expr::MemberOf(MemberOf {
|
||||||
|
value: Box::new(Expr::Value(
|
||||||
|
Value::SingleQuotedString("ab".to_string()).into()
|
||||||
|
)),
|
||||||
|
array: Box::new(Expr::Value(
|
||||||
|
Value::SingleQuotedString(r#"[23, "abc", 17, "ab", 10]"#.to_string())
|
||||||
|
.into()
|
||||||
|
)),
|
||||||
|
}))]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("Unexpected statement {stmt}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue