Skip to content

Commit 4df6dc8

Browse files
committed
Support optional stride in array access
1 parent bd573b2 commit 4df6dc8

File tree

3 files changed

+102
-22
lines changed

3 files changed

+102
-22
lines changed

src/ast/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,13 +820,22 @@ pub enum Subscript {
820820
/// {2,3,4,5}
821821
/// ```
822822
///
823+
/// Stride notation is also supported
824+
///
825+
/// ```plaintext
826+
/// => select (array[1,2,3,4,5,6])[1:6:2];
827+
/// -----------
828+
/// {1,3,5}
829+
/// ```
830+
///
823831
/// The lower and/or upper bound can be omitted to slice from the start or
824832
/// end of the array respectively.
825833
///
826834
/// See <https://www.postgresql.org/docs/current/arrays.html#ARRAYS-ACCESSING>.
827835
Slice {
828836
lower_bound: Option<Expr>,
829837
upper_bound: Option<Expr>,
838+
stride: Option<Expr>,
830839
},
831840
}
832841

@@ -837,6 +846,7 @@ impl fmt::Display for Subscript {
837846
Subscript::Slice {
838847
lower_bound,
839848
upper_bound,
849+
stride,
840850
} => {
841851
if let Some(lower) = lower_bound {
842852
write!(f, "{lower}")?;
@@ -845,6 +855,10 @@ impl fmt::Display for Subscript {
845855
if let Some(upper) = upper_bound {
846856
write!(f, "{upper}")?;
847857
}
858+
if let Some(stride) = stride {
859+
write!(f, ":")?;
860+
write!(f, "{stride}")?;
861+
}
848862
Ok(())
849863
}
850864
}

src/parser/mod.rs

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,31 +2610,84 @@ impl<'a> Parser<'a> {
26102610
}
26112611
}
26122612

2613-
pub fn parse_subscript(&mut self, expr: Expr) -> Result<Expr, ParserError> {
2614-
let parse_upper_bound = |p: &mut Parser<'a>| {
2615-
if let Token::RBracket = p.peek_token().token {
2616-
Ok(None)
2617-
} else {
2618-
p.parse_expr().map(Some)
2619-
}
2613+
/// Parses an array subscript like
2614+
/// * `[:]`
2615+
/// * `[l]`
2616+
/// * `[l:]`
2617+
/// * `[:u]`
2618+
/// * `[l:u]`
2619+
/// * `[l:u:s]`
2620+
///
2621+
/// Parser is right after `[`
2622+
fn parse_subscript_inner(&mut self) -> Result<Subscript, ParserError> {
2623+
// at either `<lower>:(rest)` or `:(rest)]`
2624+
let lower_bound = if self.consume_token(&Token::Colon) {
2625+
None
2626+
} else {
2627+
Some(self.parse_expr()?)
26202628
};
2621-
let subscript = if self.consume_token(&Token::Colon) {
2622-
Subscript::Slice {
2623-
lower_bound: None,
2624-
upper_bound: parse_upper_bound(self)?,
2625-
}
2629+
2630+
// check for end
2631+
if self.consume_token(&Token::RBracket) {
2632+
if let Some(lower_bound) = lower_bound {
2633+
return Ok(Subscript::Index { index: lower_bound });
2634+
};
2635+
return Ok(Subscript::Slice {
2636+
lower_bound,
2637+
upper_bound: None,
2638+
stride: None,
2639+
});
2640+
}
2641+
2642+
// consume the `:`
2643+
if lower_bound.is_some() {
2644+
self.expect_token(&Token::Colon)?;
2645+
}
2646+
2647+
// we are now at either `]`, `<upper>(rest)]`
2648+
let upper_bound = if self.consume_token(&Token::RBracket) {
2649+
return Ok(Subscript::Slice {
2650+
lower_bound,
2651+
upper_bound: None,
2652+
stride: None,
2653+
});
26262654
} else {
2627-
let expr = self.parse_expr()?;
2628-
if self.consume_token(&Token::Colon) {
2629-
Subscript::Slice {
2630-
lower_bound: Some(expr),
2631-
upper_bound: parse_upper_bound(self)?,
2632-
}
2633-
} else {
2634-
Subscript::Index { index: expr }
2635-
}
2655+
Some(self.parse_expr()?)
26362656
};
2637-
self.expect_token(&Token::RBracket)?;
2657+
2658+
// check for end
2659+
if self.consume_token(&Token::RBracket) {
2660+
return Ok(Subscript::Slice {
2661+
lower_bound,
2662+
upper_bound,
2663+
stride: None,
2664+
});
2665+
}
2666+
2667+
// we are now at `:]` or `:stride]`
2668+
self.expect_token(&Token::Colon)?;
2669+
let stride = if self.consume_token(&Token::RBracket) {
2670+
None
2671+
} else {
2672+
Some(self.parse_expr()?)
2673+
};
2674+
2675+
if stride.is_some() {
2676+
self.expect_token(&Token::RBracket)?;
2677+
}
2678+
2679+
Ok(Subscript::Slice {
2680+
lower_bound,
2681+
upper_bound,
2682+
stride,
2683+
})
2684+
}
2685+
2686+
/// Parses an array subscript like `[1:3]`
2687+
///
2688+
/// Parser is right after `[`
2689+
pub fn parse_subscript(&mut self, expr: Expr) -> Result<Expr, ParserError> {
2690+
let subscript = self.parse_subscript_inner()?;
26382691
Ok(Expr::Subscript {
26392692
expr: Box::new(expr),
26402693
subscript: Box::new(subscript),

tests/sqlparser_postgres.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,15 @@ fn parse_array_subscript() {
19921992
Subscript::Slice {
19931993
lower_bound: Some(Expr::Value(number("2"))),
19941994
upper_bound: Some(Expr::Value(number("5"))),
1995+
stride: None,
1996+
},
1997+
),
1998+
(
1999+
"(ARRAY[1, 2, 3, 4, 5, 6])[2:5:3]",
2000+
Subscript::Slice {
2001+
lower_bound: Some(Expr::Value(number("2"))),
2002+
upper_bound: Some(Expr::Value(number("5"))),
2003+
stride: Some(Expr::Value(number("3"))),
19952004
},
19962005
),
19972006
(
@@ -2007,27 +2016,31 @@ fn parse_array_subscript() {
20072016
op: BinaryOperator::Minus,
20082017
right: Box::new(Expr::Value(number("1"))),
20092018
}),
2019+
stride: None,
20102020
},
20112021
),
20122022
(
20132023
"(ARRAY[1, 2, 3, 4, 5, 6])[:5]",
20142024
Subscript::Slice {
20152025
lower_bound: None,
20162026
upper_bound: Some(Expr::Value(number("5"))),
2027+
stride: None,
20172028
},
20182029
),
20192030
(
20202031
"(ARRAY[1, 2, 3, 4, 5, 6])[2:]",
20212032
Subscript::Slice {
20222033
lower_bound: Some(Expr::Value(number("2"))),
20232034
upper_bound: None,
2035+
stride: None,
20242036
},
20252037
),
20262038
(
20272039
"(ARRAY[1, 2, 3, 4, 5, 6])[:]",
20282040
Subscript::Slice {
20292041
lower_bound: None,
20302042
upper_bound: None,
2043+
stride: None,
20312044
},
20322045
),
20332046
];

0 commit comments

Comments
 (0)