Skip to content

Commit 9eb8d91

Browse files
committed
add support for a:['foo']
1 parent ae26f43 commit 9eb8d91

File tree

6 files changed

+37
-6
lines changed

6 files changed

+37
-6
lines changed

src/ast/mod.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,9 +517,13 @@ pub enum JsonPathElem {
517517
/// Accesses an object field or array element using bracket notation,
518518
/// e.g. `obj['foo']`.
519519
///
520+
/// Note that on Databricks this is *not* equivalent to dot notation; the
521+
/// former is case-insensitive but the latter is not.
522+
///
520523
/// See <https://docs.snowflake.com/en/user-guide/querying-semistructured#bracket-notation>.
521524
Bracket { key: Expr },
522-
/// Accesses all elements in the given (generally array) element. Used for constructs like `foo:bar[*].baz`.
525+
/// Accesses all elements in the given (generally array) element. Used for
526+
/// constructs like `foo:bar[*].baz`.
523527
///
524528
/// See <https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-json-path-expression#extract-values-from-arrays>
525529
AllElements,
@@ -533,17 +537,22 @@ pub enum JsonPathElem {
533537
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
534538
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
535539
pub struct JsonPath {
540+
/// True if the path should start with a colon. Some dialects (e.g. Snowflake) allow
541+
/// `a['b']`, whereas others (e.g. Databricks) require the colon even in this case
542+
/// (so `a:['b']`).
543+
pub has_colon: bool,
536544
pub path: Vec<JsonPathElem>,
537545
}
538546

539547
impl fmt::Display for JsonPath {
540548
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549+
if self.has_colon {
550+
write!(f, ":")?;
551+
}
541552
for (i, elem) in self.path.iter().enumerate() {
542553
match elem {
543554
JsonPathElem::Dot { key, quoted } => {
544-
if i == 0 {
545-
write!(f, ":")?;
546-
} else {
555+
if i != 0 {
547556
write!(f, ".")?;
548557
}
549558

src/ast/spans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1715,7 +1715,7 @@ impl Spanned for FunctionArgumentClause {
17151715
/// see Spanned impl for JsonPathElem for more information
17161716
impl Spanned for JsonPath {
17171717
fn span(&self) -> Span {
1718-
let JsonPath { path } = self;
1718+
let JsonPath { path, has_colon: _ } = self;
17191719

17201720
union_spans(path.iter().map(|i| i.span()))
17211721
}

src/parser/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3717,6 +3717,7 @@ impl<'a> Parser<'a> {
37173717

37183718
fn parse_json_path(&mut self) -> Result<JsonPath, ParserError> {
37193719
let mut path = Vec::new();
3720+
let has_colon = self.peek_token_ref().token == Token::Colon;
37203721
loop {
37213722
match self.next_token().token {
37223723
Token::Colon if path.is_empty() => {
@@ -3746,7 +3747,7 @@ impl<'a> Parser<'a> {
37463747
}
37473748

37483749
debug_assert!(!path.is_empty());
3749-
Ok(JsonPath { path })
3750+
Ok(JsonPath { has_colon, path })
37503751
}
37513752

37523753
/// Parses the parens following the `[ NOT ] IN` operator.

tests/sqlparser_databricks.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ fn parse_semi_structured_data_traversal() {
367367
SelectItem::UnnamedExpr(Expr::JsonAccess {
368368
value: Box::new(Expr::Identifier(Ident::new("a"))),
369369
path: JsonPath {
370+
has_colon: true,
370371
path: vec![
371372
JsonPathElem::Dot {
372373
key: "b".to_owned(),
@@ -389,6 +390,7 @@ fn parse_semi_structured_data_traversal() {
389390
SelectItem::UnnamedExpr(Expr::JsonAccess {
390391
value: Box::new(Expr::Identifier(Ident::new("a"))),
391392
path: JsonPath {
393+
has_colon: true,
392394
path: vec![
393395
JsonPathElem::Dot {
394396
key: "b".to_owned(),
@@ -413,6 +415,7 @@ fn parse_semi_structured_data_traversal() {
413415
SelectItem::UnnamedExpr(Expr::JsonAccess {
414416
value: Box::new(Expr::Identifier(Ident::new("a"))),
415417
path: JsonPath {
418+
has_colon: true,
416419
path: vec![
417420
JsonPathElem::Dot {
418421
key: "b".to_owned(),

tests/sqlparser_redshift.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ fn test_redshift_json_path() {
206206
Ident::new("c_orders")
207207
])),
208208
path: JsonPath {
209+
has_colon: false,
209210
path: vec![
210211
JsonPathElem::Bracket {
211212
key: Expr::value(number("0"))
@@ -229,6 +230,7 @@ fn test_redshift_json_path() {
229230
Ident::new("c_orders")
230231
])),
231232
path: JsonPath {
233+
has_colon: false,
232234
path: vec![
233235
JsonPathElem::Bracket {
234236
key: Expr::value(number("0"))
@@ -255,6 +257,7 @@ fn test_redshift_json_path() {
255257
Ident::new("col1")
256258
])),
257259
path: JsonPath {
260+
has_colon: false,
258261
path: vec![
259262
JsonPathElem::Bracket {
260263
key: Expr::value(number("0"))
@@ -281,6 +284,7 @@ fn test_redshift_json_path() {
281284
Ident::new("col1")
282285
])),
283286
path: JsonPath {
287+
has_colon: false,
284288
path: vec![
285289
JsonPathElem::Bracket {
286290
key: Expr::value(number("0"))
@@ -308,6 +312,7 @@ fn test_parse_json_path_from() {
308312
assert_eq!(
309313
json_path,
310314
&Some(JsonPath {
315+
has_colon: false,
311316
path: vec![
312317
JsonPathElem::Bracket {
313318
key: Expr::value(number("0"))
@@ -332,6 +337,7 @@ fn test_parse_json_path_from() {
332337
assert_eq!(
333338
json_path,
334339
&Some(JsonPath {
340+
has_colon: false,
335341
path: vec![
336342
JsonPathElem::Bracket {
337343
key: Expr::value(number("0"))

tests/sqlparser_snowflake.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ fn parse_semi_structured_data_traversal() {
10931093
SelectItem::UnnamedExpr(Expr::JsonAccess {
10941094
value: Box::new(Expr::Identifier(Ident::new("a"))),
10951095
path: JsonPath {
1096+
has_colon: true,
10961097
path: vec![JsonPathElem::Dot {
10971098
key: "b".to_owned(),
10981099
quoted: false
@@ -1109,6 +1110,7 @@ fn parse_semi_structured_data_traversal() {
11091110
SelectItem::UnnamedExpr(Expr::JsonAccess {
11101111
value: Box::new(Expr::Identifier(Ident::new("a"))),
11111112
path: JsonPath {
1113+
has_colon: true,
11121114
path: vec![JsonPathElem::Dot {
11131115
key: "my long object key name".to_owned(),
11141116
quoted: true
@@ -1125,6 +1127,7 @@ fn parse_semi_structured_data_traversal() {
11251127
SelectItem::UnnamedExpr(Expr::JsonAccess {
11261128
value: Box::new(Expr::Identifier(Ident::new("a"))),
11271129
path: JsonPath {
1130+
has_colon: false,
11281131
path: vec![JsonPathElem::Bracket {
11291132
key: Expr::BinaryOp {
11301133
left: Box::new(Expr::value(number("2"))),
@@ -1147,6 +1150,7 @@ fn parse_semi_structured_data_traversal() {
11471150
SelectItem::UnnamedExpr(Expr::JsonAccess {
11481151
value: Box::new(Expr::Identifier(Ident::new("a"))),
11491152
path: JsonPath {
1153+
has_colon: true,
11501154
path: vec![JsonPathElem::Dot {
11511155
key: "select".to_owned(),
11521156
quoted: false
@@ -1156,6 +1160,7 @@ fn parse_semi_structured_data_traversal() {
11561160
SelectItem::UnnamedExpr(Expr::JsonAccess {
11571161
value: Box::new(Expr::Identifier(Ident::new("a"))),
11581162
path: JsonPath {
1163+
has_colon: true,
11591164
path: vec![JsonPathElem::Dot {
11601165
key: "from".to_owned(),
11611166
quoted: false
@@ -1174,6 +1179,7 @@ fn parse_semi_structured_data_traversal() {
11741179
vec![SelectItem::UnnamedExpr(Expr::JsonAccess {
11751180
value: Box::new(Expr::Identifier(Ident::new("a"))),
11761181
path: JsonPath {
1182+
has_colon: true,
11771183
path: vec![
11781184
JsonPathElem::Dot {
11791185
key: "foo".to_owned(),
@@ -1201,6 +1207,7 @@ fn parse_semi_structured_data_traversal() {
12011207
vec![SelectItem::UnnamedExpr(Expr::JsonAccess {
12021208
value: Box::new(Expr::Identifier(Ident::new("a"))),
12031209
path: JsonPath {
1210+
has_colon: true,
12041211
path: vec![
12051212
JsonPathElem::Dot {
12061213
key: "foo".to_owned(),
@@ -1227,6 +1234,7 @@ fn parse_semi_structured_data_traversal() {
12271234
vec![SelectItem::UnnamedExpr(Expr::JsonAccess {
12281235
value: Box::new(Expr::Identifier(Ident::new("a"))),
12291236
path: JsonPath {
1237+
has_colon: false,
12301238
path: vec![
12311239
JsonPathElem::Bracket {
12321240
key: Expr::value(number("0")),
@@ -1251,10 +1259,12 @@ fn parse_semi_structured_data_traversal() {
12511259
Expr::JsonAccess {
12521260
value: Box::new(Expr::Identifier(Ident::new("a"))),
12531261
path: JsonPath {
1262+
has_colon: false,
12541263
path: vec![JsonPathElem::Bracket {
12551264
key: Expr::JsonAccess {
12561265
value: Box::new(Expr::Identifier(Ident::new("b"))),
12571266
path: JsonPath {
1267+
has_colon: true,
12581268
path: vec![JsonPathElem::Dot {
12591269
key: "c".to_owned(),
12601270
quoted: false
@@ -1286,6 +1296,7 @@ fn parse_semi_structured_data_traversal() {
12861296
expr: Box::new(Expr::JsonAccess {
12871297
value: Box::new(Expr::Identifier(Ident::new("a"))),
12881298
path: JsonPath {
1299+
has_colon: true,
12891300
path: vec![JsonPathElem::Dot {
12901301
key: "b".to_string(),
12911302
quoted: false
@@ -1294,6 +1305,7 @@ fn parse_semi_structured_data_traversal() {
12941305
})
12951306
}),
12961307
path: JsonPath {
1308+
has_colon: false,
12971309
path: vec![JsonPathElem::Bracket {
12981310
key: Expr::value(number("1"))
12991311
}]

0 commit comments

Comments
 (0)