mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-04 06:18:17 +00:00
Support json operators @>
<@
#-
@?
and @@
postgres supports a bunch more json operators. See https://www.postgresql.org/docs/15/functions-json.html Skipping operators starting with a question mark for now, since those are hard to distinguish from placeholders without more context.
This commit is contained in:
parent
fb02344131
commit
6d6eb4bc9b
4 changed files with 132 additions and 2 deletions
|
@ -190,6 +190,20 @@ pub enum JsonOperator {
|
||||||
HashLongArrow,
|
HashLongArrow,
|
||||||
/// : Colon is used by Snowflake (Which is similar to LongArrow)
|
/// : Colon is used by Snowflake (Which is similar to LongArrow)
|
||||||
Colon,
|
Colon,
|
||||||
|
/// jsonb @> jsonb -> boolean: Test whether left json contains the right json
|
||||||
|
AtArrow,
|
||||||
|
/// jsonb <@ jsonb -> boolean: Test whether right json contains the left json
|
||||||
|
ArrowAt,
|
||||||
|
/// jsonb #- text[] -> jsonb: Deletes the field or array element at the specified
|
||||||
|
/// path, where path elements can be either field keys or array indexes.
|
||||||
|
HashMinus,
|
||||||
|
/// jsonb @? jsonpath -> boolean: Does JSON path return any item for the specified
|
||||||
|
/// JSON value?
|
||||||
|
AtQuestion,
|
||||||
|
/// jsonb @@ jsonpath → boolean: Returns the result of a JSON path predicate check
|
||||||
|
/// for the specified JSON value. Only the first item of the result is taken into
|
||||||
|
/// account. If the result is not Boolean, then NULL is returned.
|
||||||
|
AtAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for JsonOperator {
|
impl fmt::Display for JsonOperator {
|
||||||
|
@ -210,6 +224,13 @@ impl fmt::Display for JsonOperator {
|
||||||
JsonOperator::Colon => {
|
JsonOperator::Colon => {
|
||||||
write!(f, ":")
|
write!(f, ":")
|
||||||
}
|
}
|
||||||
|
JsonOperator::AtArrow => {
|
||||||
|
write!(f, "@>")
|
||||||
|
}
|
||||||
|
JsonOperator::ArrowAt => write!(f, "<@"),
|
||||||
|
JsonOperator::HashMinus => write!(f, "#-"),
|
||||||
|
JsonOperator::AtQuestion => write!(f, "@?"),
|
||||||
|
JsonOperator::AtAt => write!(f, "@@"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1602,12 +1602,22 @@ impl<'a> Parser<'a> {
|
||||||
|| Token::LongArrow == tok
|
|| Token::LongArrow == tok
|
||||||
|| Token::HashArrow == tok
|
|| Token::HashArrow == tok
|
||||||
|| Token::HashLongArrow == tok
|
|| Token::HashLongArrow == tok
|
||||||
|
|| Token::AtArrow == tok
|
||||||
|
|| Token::ArrowAt == tok
|
||||||
|
|| Token::HashMinus == tok
|
||||||
|
|| Token::AtQuestion == tok
|
||||||
|
|| Token::AtAt == tok
|
||||||
{
|
{
|
||||||
let operator = match tok.token {
|
let operator = match tok.token {
|
||||||
Token::Arrow => JsonOperator::Arrow,
|
Token::Arrow => JsonOperator::Arrow,
|
||||||
Token::LongArrow => JsonOperator::LongArrow,
|
Token::LongArrow => JsonOperator::LongArrow,
|
||||||
Token::HashArrow => JsonOperator::HashArrow,
|
Token::HashArrow => JsonOperator::HashArrow,
|
||||||
Token::HashLongArrow => JsonOperator::HashLongArrow,
|
Token::HashLongArrow => JsonOperator::HashLongArrow,
|
||||||
|
Token::AtArrow => JsonOperator::AtArrow,
|
||||||
|
Token::ArrowAt => JsonOperator::ArrowAt,
|
||||||
|
Token::HashMinus => JsonOperator::HashMinus,
|
||||||
|
Token::AtQuestion => JsonOperator::AtQuestion,
|
||||||
|
Token::AtAt => JsonOperator::AtAt,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
Ok(Expr::JsonAccess {
|
Ok(Expr::JsonAccess {
|
||||||
|
@ -1805,7 +1815,12 @@ impl<'a> Parser<'a> {
|
||||||
| Token::LongArrow
|
| Token::LongArrow
|
||||||
| Token::Arrow
|
| Token::Arrow
|
||||||
| Token::HashArrow
|
| Token::HashArrow
|
||||||
| Token::HashLongArrow => Ok(50),
|
| Token::HashLongArrow
|
||||||
|
| Token::AtArrow
|
||||||
|
| Token::ArrowAt
|
||||||
|
| Token::HashMinus
|
||||||
|
| Token::AtQuestion
|
||||||
|
| Token::AtAt => Ok(50),
|
||||||
_ => Ok(0),
|
_ => Ok(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,20 @@ pub enum Token {
|
||||||
HashArrow,
|
HashArrow,
|
||||||
/// #>> Extracts JSON sub-object at the specified path as text
|
/// #>> Extracts JSON sub-object at the specified path as text
|
||||||
HashLongArrow,
|
HashLongArrow,
|
||||||
|
/// jsonb @> jsonb -> boolean: Test whether left json contains the right json
|
||||||
|
AtArrow,
|
||||||
|
/// jsonb <@ jsonb -> boolean: Test whether right json contains the left json
|
||||||
|
ArrowAt,
|
||||||
|
/// jsonb #- text[] -> jsonb: Deletes the field or array element at the specified
|
||||||
|
/// path, where path elements can be either field keys or array indexes.
|
||||||
|
HashMinus,
|
||||||
|
/// jsonb @? jsonpath -> boolean: Does JSON path return any item for the specified
|
||||||
|
/// JSON value?
|
||||||
|
AtQuestion,
|
||||||
|
/// jsonb @@ jsonpath → boolean: Returns the result of a JSON path predicate check
|
||||||
|
/// for the specified JSON value. Only the first item of the result is taken into
|
||||||
|
/// account. If the result is not Boolean, then NULL is returned.
|
||||||
|
AtAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Token {
|
impl fmt::Display for Token {
|
||||||
|
@ -217,7 +231,12 @@ impl fmt::Display for Token {
|
||||||
Token::LongArrow => write!(f, "->>"),
|
Token::LongArrow => write!(f, "->>"),
|
||||||
Token::HashArrow => write!(f, "#>"),
|
Token::HashArrow => write!(f, "#>"),
|
||||||
Token::HashLongArrow => write!(f, "#>>"),
|
Token::HashLongArrow => write!(f, "#>>"),
|
||||||
|
Token::AtArrow => write!(f, "@>"),
|
||||||
Token::DoubleDollarQuoting => write!(f, "$$"),
|
Token::DoubleDollarQuoting => write!(f, "$$"),
|
||||||
|
Token::ArrowAt => write!(f, "<@"),
|
||||||
|
Token::HashMinus => write!(f, "#-"),
|
||||||
|
Token::AtQuestion => write!(f, "@?"),
|
||||||
|
Token::AtAt => write!(f, "@@"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -708,6 +727,7 @@ impl<'a> Tokenizer<'a> {
|
||||||
}
|
}
|
||||||
Some('>') => self.consume_and_return(chars, Token::Neq),
|
Some('>') => self.consume_and_return(chars, Token::Neq),
|
||||||
Some('<') => self.consume_and_return(chars, Token::ShiftLeft),
|
Some('<') => self.consume_and_return(chars, Token::ShiftLeft),
|
||||||
|
Some('@') => self.consume_and_return(chars, Token::ArrowAt),
|
||||||
_ => Ok(Some(Token::Lt)),
|
_ => Ok(Some(Token::Lt)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -752,6 +772,7 @@ impl<'a> Tokenizer<'a> {
|
||||||
'#' => {
|
'#' => {
|
||||||
chars.next();
|
chars.next();
|
||||||
match chars.peek() {
|
match chars.peek() {
|
||||||
|
Some('-') => self.consume_and_return(chars, Token::HashMinus),
|
||||||
Some('>') => {
|
Some('>') => {
|
||||||
chars.next();
|
chars.next();
|
||||||
match chars.peek() {
|
match chars.peek() {
|
||||||
|
@ -765,7 +786,15 @@ impl<'a> Tokenizer<'a> {
|
||||||
_ => Ok(Some(Token::Sharp)),
|
_ => Ok(Some(Token::Sharp)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'@' => self.consume_and_return(chars, Token::AtSign),
|
'@' => {
|
||||||
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('>') => self.consume_and_return(chars, Token::AtArrow),
|
||||||
|
Some('?') => self.consume_and_return(chars, Token::AtQuestion),
|
||||||
|
Some('@') => self.consume_and_return(chars, Token::AtAt),
|
||||||
|
_ => Ok(Some(Token::AtSign)),
|
||||||
|
}
|
||||||
|
}
|
||||||
'?' => {
|
'?' => {
|
||||||
chars.next();
|
chars.next();
|
||||||
let s = peeking_take_while(chars, |ch| ch.is_numeric());
|
let s = peeking_take_while(chars, |ch| ch.is_numeric());
|
||||||
|
|
|
@ -1620,6 +1620,71 @@ fn test_json() {
|
||||||
}),
|
}),
|
||||||
select.projection[0]
|
select.projection[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT info FROM orders WHERE info @> '{\"a\": 1}'";
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::JsonAccess {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::new("info"))),
|
||||||
|
operator: JsonOperator::AtArrow,
|
||||||
|
right: Box::new(Expr::Value(Value::SingleQuotedString(
|
||||||
|
"{\"a\": 1}".to_string()
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
select.selection.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT info FROM orders WHERE '{\"a\": 1}' <@ info";
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::JsonAccess {
|
||||||
|
left: Box::new(Expr::Value(Value::SingleQuotedString(
|
||||||
|
"{\"a\": 1}".to_string()
|
||||||
|
))),
|
||||||
|
operator: JsonOperator::ArrowAt,
|
||||||
|
right: Box::new(Expr::Identifier(Ident::new("info"))),
|
||||||
|
},
|
||||||
|
select.selection.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT info #- ARRAY['a', 'b'] FROM orders";
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
SelectItem::UnnamedExpr(Expr::JsonAccess {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::from("info"))),
|
||||||
|
operator: JsonOperator::HashMinus,
|
||||||
|
right: Box::new(Expr::Array(Array {
|
||||||
|
elem: vec![
|
||||||
|
Expr::Value(Value::SingleQuotedString("a".to_string())),
|
||||||
|
Expr::Value(Value::SingleQuotedString("b".to_string())),
|
||||||
|
],
|
||||||
|
named: true,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
select.projection[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT info FROM orders WHERE info @? '$.a'";
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::JsonAccess {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::from("info"))),
|
||||||
|
operator: JsonOperator::AtQuestion,
|
||||||
|
right: Box::new(Expr::Value(Value::SingleQuotedString("$.a".to_string())),),
|
||||||
|
},
|
||||||
|
select.selection.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT info FROM orders WHERE info @@ '$.a'";
|
||||||
|
let select = pg().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
Expr::JsonAccess {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::from("info"))),
|
||||||
|
operator: JsonOperator::AtAt,
|
||||||
|
right: Box::new(Expr::Value(Value::SingleQuotedString("$.a".to_string())),),
|
||||||
|
},
|
||||||
|
select.selection.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue