mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-03 12:47:21 +00:00
Snowflake: Support semi-structured data (#693)
* Support parse json in snowflake * MR Review * Lint * Try to fix right as value * Fix tests * Fix lint * Add generic dialect * Add support in key location
This commit is contained in:
parent
27c3ec87db
commit
93a050e5f0
4 changed files with 40 additions and 1 deletions
|
@ -187,6 +187,8 @@ pub enum JsonOperator {
|
||||||
HashArrow,
|
HashArrow,
|
||||||
/// #>> Extracts JSON sub-object at the specified path as text
|
/// #>> Extracts JSON sub-object at the specified path as text
|
||||||
HashLongArrow,
|
HashLongArrow,
|
||||||
|
/// : Colon is used by Snowflake (Which is similar to LongArrow)
|
||||||
|
Colon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for JsonOperator {
|
impl fmt::Display for JsonOperator {
|
||||||
|
@ -204,6 +206,9 @@ impl fmt::Display for JsonOperator {
|
||||||
JsonOperator::HashLongArrow => {
|
JsonOperator::HashLongArrow => {
|
||||||
write!(f, "#>>")
|
write!(f, "#>>")
|
||||||
}
|
}
|
||||||
|
JsonOperator::Colon => {
|
||||||
|
write!(f, ":")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -757,7 +762,11 @@ impl fmt::Display for Expr {
|
||||||
operator,
|
operator,
|
||||||
right,
|
right,
|
||||||
} => {
|
} => {
|
||||||
|
if operator == &JsonOperator::Colon {
|
||||||
write!(f, "{}{}{}", left, operator, right)
|
write!(f, "{}{}{}", left, operator, right)
|
||||||
|
} else {
|
||||||
|
write!(f, "{} {} {}", left, operator, right)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::CompositeAccess { expr, key } => {
|
Expr::CompositeAccess { expr, key } => {
|
||||||
write!(f, "{}.{}", expr, key)
|
write!(f, "{}.{}", expr, key)
|
||||||
|
|
|
@ -45,6 +45,8 @@ pub enum Value {
|
||||||
Null,
|
Null,
|
||||||
/// `?` or `$` Prepared statement arg placeholder
|
/// `?` or `$` Prepared statement arg placeholder
|
||||||
Placeholder(String),
|
Placeholder(String),
|
||||||
|
/// Add support of snowflake field:key - key should be a value
|
||||||
|
UnQuotedString(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Value {
|
impl fmt::Display for Value {
|
||||||
|
@ -59,6 +61,7 @@ impl fmt::Display for Value {
|
||||||
Value::Boolean(v) => write!(f, "{}", v),
|
Value::Boolean(v) => write!(f, "{}", v),
|
||||||
Value::Null => write!(f, "NULL"),
|
Value::Null => write!(f, "NULL"),
|
||||||
Value::Placeholder(v) => write!(f, "{}", v),
|
Value::Placeholder(v) => write!(f, "{}", v),
|
||||||
|
Value::UnQuotedString(v) => write!(f, "{}", v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1427,6 +1427,12 @@ impl<'a> Parser<'a> {
|
||||||
return self.parse_array_index(expr);
|
return self.parse_array_index(expr);
|
||||||
}
|
}
|
||||||
self.parse_map_access(expr)
|
self.parse_map_access(expr)
|
||||||
|
} else if Token::Colon == tok {
|
||||||
|
Ok(Expr::JsonAccess {
|
||||||
|
left: Box::new(expr),
|
||||||
|
operator: JsonOperator::Colon,
|
||||||
|
right: Box::new(Expr::Value(self.parse_value()?)),
|
||||||
|
})
|
||||||
} else if Token::Arrow == tok
|
} else if Token::Arrow == tok
|
||||||
|| Token::LongArrow == tok
|
|| Token::LongArrow == tok
|
||||||
|| Token::HashArrow == tok
|
|| Token::HashArrow == tok
|
||||||
|
@ -1628,6 +1634,7 @@ impl<'a> Parser<'a> {
|
||||||
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
|
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
|
||||||
Token::Mul | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
|
Token::Mul | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
|
||||||
Token::DoubleColon => Ok(50),
|
Token::DoubleColon => Ok(50),
|
||||||
|
Token::Colon => Ok(50),
|
||||||
Token::ExclamationMark => Ok(50),
|
Token::ExclamationMark => Ok(50),
|
||||||
Token::LBracket
|
Token::LBracket
|
||||||
| Token::LongArrow
|
| Token::LongArrow
|
||||||
|
@ -3446,6 +3453,10 @@ impl<'a> Parser<'a> {
|
||||||
Some('\'') => Ok(Value::SingleQuotedString(w.value)),
|
Some('\'') => Ok(Value::SingleQuotedString(w.value)),
|
||||||
_ => self.expected("A value?", Token::Word(w))?,
|
_ => self.expected("A value?", Token::Word(w))?,
|
||||||
},
|
},
|
||||||
|
// Case when Snowflake Semi-structured data like key:value
|
||||||
|
Keyword::NoKeyword | Keyword::LOCATION if dialect_of!(self is SnowflakeDialect | GenericDialect) => {
|
||||||
|
Ok(Value::UnQuotedString(w.value))
|
||||||
|
}
|
||||||
_ => self.expected("a concrete value", Token::Word(w)),
|
_ => self.expected("a concrete value", Token::Word(w)),
|
||||||
},
|
},
|
||||||
// The call to n.parse() returns a bigdecimal when the
|
// The call to n.parse() returns a bigdecimal when the
|
||||||
|
|
|
@ -143,6 +143,22 @@ fn test_single_table_in_parenthesis_with_alias() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_json_using_colon() {
|
||||||
|
let sql = "SELECT a:b FROM t";
|
||||||
|
let select = snowflake().verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
SelectItem::UnnamedExpr(Expr::JsonAccess {
|
||||||
|
left: Box::new(Expr::Identifier(Ident::new("a"))),
|
||||||
|
operator: JsonOperator::Colon,
|
||||||
|
right: Box::new(Expr::Value(Value::UnQuotedString("b".to_string()))),
|
||||||
|
}),
|
||||||
|
select.projection[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
snowflake().one_statement_parses_to("SELECT a:b::int FROM t", "SELECT CAST(a:b AS INT) FROM t");
|
||||||
|
}
|
||||||
|
|
||||||
fn snowflake() -> TestedDialects {
|
fn snowflake() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(SnowflakeDialect {})],
|
dialects: vec![Box::new(SnowflakeDialect {})],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue