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:
yuval-illumex 2022-11-02 18:05:35 +02:00 committed by GitHub
parent 27c3ec87db
commit 93a050e5f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 1 deletions

View file

@ -187,6 +187,8 @@ pub enum JsonOperator {
HashArrow,
/// #>> Extracts JSON sub-object at the specified path as text
HashLongArrow,
/// : Colon is used by Snowflake (Which is similar to LongArrow)
Colon,
}
impl fmt::Display for JsonOperator {
@ -204,6 +206,9 @@ impl fmt::Display for JsonOperator {
JsonOperator::HashLongArrow => {
write!(f, "#>>")
}
JsonOperator::Colon => {
write!(f, ":")
}
}
}
}
@ -757,7 +762,11 @@ impl fmt::Display for Expr {
operator,
right,
} => {
if operator == &JsonOperator::Colon {
write!(f, "{}{}{}", left, operator, right)
} else {
write!(f, "{} {} {}", left, operator, right)
}
}
Expr::CompositeAccess { expr, key } => {
write!(f, "{}.{}", expr, key)

View file

@ -45,6 +45,8 @@ pub enum Value {
Null,
/// `?` or `$` Prepared statement arg placeholder
Placeholder(String),
/// Add support of snowflake field:key - key should be a value
UnQuotedString(String),
}
impl fmt::Display for Value {
@ -59,6 +61,7 @@ impl fmt::Display for Value {
Value::Boolean(v) => write!(f, "{}", v),
Value::Null => write!(f, "NULL"),
Value::Placeholder(v) => write!(f, "{}", v),
Value::UnQuotedString(v) => write!(f, "{}", v),
}
}
}

View file

@ -1427,6 +1427,12 @@ impl<'a> Parser<'a> {
return self.parse_array_index(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
|| Token::LongArrow == tok
|| Token::HashArrow == tok
@ -1628,6 +1634,7 @@ impl<'a> Parser<'a> {
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
Token::Mul | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
Token::DoubleColon => Ok(50),
Token::Colon => Ok(50),
Token::ExclamationMark => Ok(50),
Token::LBracket
| Token::LongArrow
@ -3446,6 +3453,10 @@ impl<'a> Parser<'a> {
Some('\'') => Ok(Value::SingleQuotedString(w.value)),
_ => 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)),
},
// The call to n.parse() returns a bigdecimal when the

View file

@ -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 {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {})],