Skip to content

Commit f32369b

Browse files
committed
Begin tokens of START WITH / CONNECT BY clauses
1 parent 9a0278e commit f32369b

4 files changed

Lines changed: 100 additions & 51 deletions

File tree

src/ast/query.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,28 +1211,38 @@ impl fmt::Display for TableWithJoins {
12111211
pub enum ConnectByKind {
12121212
/// CONNECT BY
12131213
ConnectBy {
1214-
/// the join conditions denoting the hierarchical relationship
1215-
relationships: Vec<Expr>,
1214+
/// the `CONNECT` token
1215+
connect_token: AttachedToken,
12161216

12171217
/// [CONNECT BY] NOCYCLE
12181218
///
12191219
/// Optional on [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Hierarchical-Queries.html#GUID-0118DF1D-B9A9-41EB-8556-C6E7D6A5A84E__GUID-5377971A-F518-47E4-8781-F06FEB3EF993)
12201220
nocycle: bool,
1221+
1222+
/// join conditions denoting the hierarchical relationship
1223+
relationships: Vec<Expr>,
12211224
},
12221225

12231226
/// START WITH
12241227
///
12251228
/// Optional on [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Hierarchical-Queries.html#GUID-0118DF1D-B9A9-41EB-8556-C6E7D6A5A84E)
12261229
/// when comming _after_ the `CONNECT BY`.
1227-
StartWith(Box<Expr>),
1230+
StartWith {
1231+
/// the `START` token
1232+
start_token: AttachedToken,
1233+
1234+
/// condition selecting the root rows of the hierarchy
1235+
condition: Box<Expr>,
1236+
},
12281237
}
12291238

12301239
impl fmt::Display for ConnectByKind {
12311240
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12321241
match self {
12331242
ConnectByKind::ConnectBy {
1234-
relationships,
1243+
connect_token: _,
12351244
nocycle,
1245+
relationships,
12361246
} => {
12371247
write!(
12381248
f,
@@ -1241,7 +1251,10 @@ impl fmt::Display for ConnectByKind {
12411251
relationships = display_comma_separated(relationships)
12421252
)
12431253
}
1244-
ConnectByKind::StartWith(condition) => {
1254+
ConnectByKind::StartWith {
1255+
start_token: _,
1256+
condition,
1257+
} => {
12451258
write!(f, "START WITH {condition}")
12461259
}
12471260
}

src/ast/spans.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,14 +2269,14 @@ impl Spanned for Select {
22692269
.chain(lateral_views.iter().map(|item| item.span()))
22702270
.chain(prewhere.iter().map(|item| item.span()))
22712271
.chain(selection.iter().map(|item| item.span()))
2272+
.chain(connect_by.iter().map(|item| item.span()))
22722273
.chain(core::iter::once(group_by.span()))
22732274
.chain(cluster_by.iter().map(|item| item.span()))
22742275
.chain(distribute_by.iter().map(|item| item.span()))
22752276
.chain(sort_by.iter().map(|item| item.span()))
22762277
.chain(having.iter().map(|item| item.span()))
22772278
.chain(named_window.iter().map(|item| item.span()))
2278-
.chain(qualify.iter().map(|item| item.span()))
2279-
.chain(connect_by.iter().map(|item| item.span())),
2279+
.chain(qualify.iter().map(|item| item.span())),
22802280
)
22812281
}
22822282
}
@@ -2285,10 +2285,17 @@ impl Spanned for ConnectByKind {
22852285
fn span(&self) -> Span {
22862286
match self {
22872287
ConnectByKind::ConnectBy {
2288-
relationships,
2288+
connect_token,
22892289
nocycle: _,
2290-
} => union_spans(relationships.iter().map(|item| item.span())),
2291-
ConnectByKind::StartWith(expr) => expr.span(),
2290+
relationships,
2291+
} => union_spans(
2292+
core::iter::once(connect_token.0.span())
2293+
.chain(relationships.last().iter().map(|item| item.span())),
2294+
),
2295+
ConnectByKind::StartWith {
2296+
start_token,
2297+
condition,
2298+
} => union_spans([start_token.0.span(), condition.span()].into_iter()),
22922299
}
22932300
}
22942301
}

src/parser/mod.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4521,16 +4521,29 @@ impl<'a> Parser<'a> {
45214521
/// consumed and returns false
45224522
#[must_use]
45234523
pub fn parse_keywords(&mut self, keywords: &[Keyword]) -> bool {
4524-
let index = self.index;
4525-
for &keyword in keywords {
4526-
if !self.parse_keyword(keyword) {
4527-
// println!("parse_keywords aborting .. did not find {:?}", keyword);
4528-
// reset index and return immediately
4529-
self.index = index;
4530-
return false;
4524+
self.parse_keywords_(keywords).is_some()
4525+
}
4526+
4527+
/// Just like [Self::parse_keywords], but - upon success - returns the
4528+
/// token index of the first keyword.
4529+
#[must_use]
4530+
fn parse_keywords_(&mut self, keywords: &[Keyword]) -> Option<usize> {
4531+
let start_index = self.index;
4532+
let mut first_keyword_index = None;
4533+
match keywords {
4534+
[keyword, keywords @ ..] if self.parse_keyword(*keyword) => {
4535+
first_keyword_index = Some(self.index.saturating_sub(1));
4536+
for &keyword in keywords {
4537+
if !self.parse_keyword(keyword) {
4538+
self.index = start_index;
4539+
first_keyword_index = None;
4540+
break;
4541+
}
4542+
}
45314543
}
4544+
_ => {}
45324545
}
4533-
true
4546+
first_keyword_index
45344547
}
45354548

45364549
/// If the current token is one of the given `keywords`, returns the keyword
@@ -14273,16 +14286,18 @@ impl<'a> Parser<'a> {
1427314286
pub fn maybe_parse_connect_by(&mut self) -> Result<Vec<ConnectByKind>, ParserError> {
1427414287
let mut clauses = Vec::with_capacity(2);
1427514288
loop {
14276-
if self.parse_keywords(&[Keyword::START, Keyword::WITH]) {
14277-
clauses.push(ConnectByKind::StartWith(self.parse_expr()?.into()));
14278-
} else if self.parse_keywords(&[Keyword::CONNECT, Keyword::BY]) {
14279-
let nocycle = self.parse_keyword(Keyword::NOCYCLE);
14280-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
14281-
parser.parse_comma_separated(Parser::parse_expr)
14282-
})?;
14289+
if let Some(idx) = self.parse_keywords_(&[Keyword::START, Keyword::WITH]) {
14290+
clauses.push(ConnectByKind::StartWith {
14291+
start_token: self.token_at(idx).clone().into(),
14292+
condition: self.parse_expr()?.into(),
14293+
});
14294+
} else if let Some(idx) = self.parse_keywords_(&[Keyword::CONNECT, Keyword::BY]) {
1428314295
clauses.push(ConnectByKind::ConnectBy {
14284-
relationships,
14285-
nocycle,
14296+
connect_token: self.token_at(idx).clone().into(),
14297+
nocycle: self.parse_keyword(Keyword::NOCYCLE),
14298+
relationships: self.with_state(ParserState::ConnectBy, |parser| {
14299+
parser.parse_comma_separated(Parser::parse_expr)
14300+
})?,
1428614301
});
1428714302
} else {
1428814303
break;

tests/sqlparser_common.rs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12686,25 +12686,27 @@ fn parse_connect_by() {
1268612686
window_before_qualify: false,
1268712687
value_table_mode: None,
1268812688
connect_by: vec![
12689-
ConnectByKind::StartWith(
12690-
Expr::BinaryOp {
12689+
ConnectByKind::StartWith {
12690+
start_token: AttachedToken::empty(),
12691+
condition: Expr::BinaryOp {
1269112692
left: Box::new(Expr::Identifier(Ident::new("title"))),
1269212693
op: BinaryOperator::Eq,
1269312694
right: Box::new(Expr::Value(
1269412695
Value::SingleQuotedString("president".to_owned()).with_empty_span(),
1269512696
)),
1269612697
}
1269712698
.into()
12698-
),
12699+
},
1269912700
ConnectByKind::ConnectBy {
12701+
connect_token: AttachedToken::empty(),
12702+
nocycle: false,
1270012703
relationships: vec![Expr::BinaryOp {
1270112704
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1270212705
op: BinaryOperator::Eq,
1270312706
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1270412707
"employee_id",
1270512708
))))),
1270612709
}],
12707-
nocycle: false,
1270812710
}
1270912711
],
1271012712
flavor: SelectFlavor::Standard,
@@ -12752,22 +12754,28 @@ fn parse_connect_by() {
1275212754
value_table_mode: None,
1275312755
connect_by: vec![
1275412756
ConnectByKind::ConnectBy {
12757+
connect_token: AttachedToken::empty(),
12758+
nocycle: false,
1275512759
relationships: vec![Expr::BinaryOp {
1275612760
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1275712761
op: BinaryOperator::Eq,
1275812762
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1275912763
"employee_id",
1276012764
))))),
1276112765
}],
12762-
nocycle: false,
1276312766
},
12764-
ConnectByKind::StartWith(Expr::BinaryOp {
12765-
left: Box::new(Expr::Identifier(Ident::new("title"))),
12766-
op: BinaryOperator::Eq,
12767-
right: Box::new(Expr::Value(
12768-
Value::SingleQuotedString("president".to_owned()).with_empty_span(),
12769-
)),
12770-
}.into())],
12767+
ConnectByKind::StartWith {
12768+
start_token: AttachedToken::empty(),
12769+
condition: Expr::BinaryOp {
12770+
left: Box::new(Expr::Identifier(Ident::new("title"))),
12771+
op: BinaryOperator::Eq,
12772+
right: Box::new(Expr::Value(
12773+
Value::SingleQuotedString("president".to_owned()).with_empty_span(),
12774+
)),
12775+
}
12776+
.into()
12777+
},
12778+
],
1277112779
flavor: SelectFlavor::Standard,
1277212780
}
1277312781
);
@@ -12817,23 +12825,29 @@ fn parse_connect_by() {
1281712825
window_before_qualify: false,
1281812826
value_table_mode: None,
1281912827
connect_by: vec![
12820-
ConnectByKind::StartWith(Expr::BinaryOp {
12828+
ConnectByKind::StartWith {
12829+
start_token: AttachedToken::empty(),
12830+
condition: Expr::BinaryOp {
1282112831
left: Box::new(Expr::Identifier(Ident::new("title"))),
1282212832
op: BinaryOperator::Eq,
1282312833
right: Box::new(Expr::Value(
1282412834
(Value::SingleQuotedString("president".to_owned(),)).with_empty_span()
1282512835
)),
12826-
}.into()),
12836+
}
12837+
.into()
12838+
},
1282712839
ConnectByKind::ConnectBy {
12840+
connect_token: AttachedToken::empty(),
12841+
nocycle: false,
1282812842
relationships: vec![Expr::BinaryOp {
1282912843
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1283012844
op: BinaryOperator::Eq,
1283112845
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1283212846
"employee_id",
1283312847
))))),
1283412848
}],
12835-
nocycle: false,
12836-
}],
12849+
}
12850+
],
1283712851
flavor: SelectFlavor::Standard,
1283812852
}
1283912853
);
@@ -12893,15 +12907,15 @@ fn parse_connect_by() {
1289312907
qualify: None,
1289412908
window_before_qualify: false,
1289512909
value_table_mode: None,
12896-
connect_by: vec![
12897-
ConnectByKind::ConnectBy {
12898-
relationships: vec![Expr::BinaryOp {
12899-
left: Expr::Identifier(Ident::new("parent")).into(),
12900-
op: BinaryOperator::Eq,
12901-
right: Expr::Prior(Expr::Identifier(Ident::new("child")).into()).into(),
12902-
}],
12903-
nocycle: true,
12910+
connect_by: vec![ConnectByKind::ConnectBy {
12911+
connect_token: AttachedToken::empty(),
12912+
nocycle: true,
12913+
relationships: vec![Expr::BinaryOp {
12914+
left: Expr::Identifier(Ident::new("parent")).into(),
12915+
op: BinaryOperator::Eq,
12916+
right: Expr::Prior(Expr::Identifier(Ident::new("child")).into()).into(),
1290412917
}],
12918+
}],
1290512919
flavor: SelectFlavor::Standard,
1290612920
}
1290712921
);

0 commit comments

Comments
 (0)