Skip to content

Commit 93a050e

Browse files
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
1 parent 27c3ec8 commit 93a050e

File tree

4 files changed

+40
-1
lines changed

4 files changed

+40
-1
lines changed

src/ast/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ pub enum JsonOperator {
187187
HashArrow,
188188
/// #>> Extracts JSON sub-object at the specified path as text
189189
HashLongArrow,
190+
/// : Colon is used by Snowflake (Which is similar to LongArrow)
191+
Colon,
190192
}
191193

192194
impl fmt::Display for JsonOperator {
@@ -204,6 +206,9 @@ impl fmt::Display for JsonOperator {
204206
JsonOperator::HashLongArrow => {
205207
write!(f, "#>>")
206208
}
209+
JsonOperator::Colon => {
210+
write!(f, ":")
211+
}
207212
}
208213
}
209214
}
@@ -757,7 +762,11 @@ impl fmt::Display for Expr {
757762
operator,
758763
right,
759764
} => {
760-
write!(f, "{} {} {}", left, operator, right)
765+
if operator == &JsonOperator::Colon {
766+
write!(f, "{}{}{}", left, operator, right)
767+
} else {
768+
write!(f, "{} {} {}", left, operator, right)
769+
}
761770
}
762771
Expr::CompositeAccess { expr, key } => {
763772
write!(f, "{}.{}", expr, key)

src/ast/value.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum Value {
4545
Null,
4646
/// `?` or `$` Prepared statement arg placeholder
4747
Placeholder(String),
48+
/// Add support of snowflake field:key - key should be a value
49+
UnQuotedString(String),
4850
}
4951

5052
impl fmt::Display for Value {
@@ -59,6 +61,7 @@ impl fmt::Display for Value {
5961
Value::Boolean(v) => write!(f, "{}", v),
6062
Value::Null => write!(f, "NULL"),
6163
Value::Placeholder(v) => write!(f, "{}", v),
64+
Value::UnQuotedString(v) => write!(f, "{}", v),
6265
}
6366
}
6467
}

src/parser.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,12 @@ impl<'a> Parser<'a> {
14271427
return self.parse_array_index(expr);
14281428
}
14291429
self.parse_map_access(expr)
1430+
} else if Token::Colon == tok {
1431+
Ok(Expr::JsonAccess {
1432+
left: Box::new(expr),
1433+
operator: JsonOperator::Colon,
1434+
right: Box::new(Expr::Value(self.parse_value()?)),
1435+
})
14301436
} else if Token::Arrow == tok
14311437
|| Token::LongArrow == tok
14321438
|| Token::HashArrow == tok
@@ -1628,6 +1634,7 @@ impl<'a> Parser<'a> {
16281634
Token::Plus | Token::Minus => Ok(Self::PLUS_MINUS_PREC),
16291635
Token::Mul | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
16301636
Token::DoubleColon => Ok(50),
1637+
Token::Colon => Ok(50),
16311638
Token::ExclamationMark => Ok(50),
16321639
Token::LBracket
16331640
| Token::LongArrow
@@ -3446,6 +3453,10 @@ impl<'a> Parser<'a> {
34463453
Some('\'') => Ok(Value::SingleQuotedString(w.value)),
34473454
_ => self.expected("A value?", Token::Word(w))?,
34483455
},
3456+
// Case when Snowflake Semi-structured data like key:value
3457+
Keyword::NoKeyword | Keyword::LOCATION if dialect_of!(self is SnowflakeDialect | GenericDialect) => {
3458+
Ok(Value::UnQuotedString(w.value))
3459+
}
34493460
_ => self.expected("a concrete value", Token::Word(w)),
34503461
},
34513462
// The call to n.parse() returns a bigdecimal when the

tests/sqlparser_snowflake.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,22 @@ fn test_single_table_in_parenthesis_with_alias() {
143143
);
144144
}
145145

146+
#[test]
147+
fn parse_json_using_colon() {
148+
let sql = "SELECT a:b FROM t";
149+
let select = snowflake().verified_only_select(sql);
150+
assert_eq!(
151+
SelectItem::UnnamedExpr(Expr::JsonAccess {
152+
left: Box::new(Expr::Identifier(Ident::new("a"))),
153+
operator: JsonOperator::Colon,
154+
right: Box::new(Expr::Value(Value::UnQuotedString("b".to_string()))),
155+
}),
156+
select.projection[0]
157+
);
158+
159+
snowflake().one_statement_parses_to("SELECT a:b::int FROM t", "SELECT CAST(a:b AS INT) FROM t");
160+
}
161+
146162
fn snowflake() -> TestedDialects {
147163
TestedDialects {
148164
dialects: vec![Box::new(SnowflakeDialect {})],

0 commit comments

Comments
 (0)