Skip to content

Commit d6bfab7

Browse files
committed
Begin tokens of START WITH / CONNECT BY clauses
1 parent 0119f41 commit d6bfab7

4 files changed

Lines changed: 82 additions & 40 deletions

File tree

src/ast/query.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,28 +1101,38 @@ impl fmt::Display for TableWithJoins {
11011101
pub enum ConnectByKind {
11021102
/// CONNECT BY
11031103
ConnectBy {
1104-
/// the join conditions denoting the hierarchical relationship
1105-
relationships: Vec<Expr>,
1104+
/// the `CONNECT` token
1105+
connect_token: AttachedToken,
11061106

11071107
/// [CONNECT BY] NOCYCLE
11081108
///
11091109
/// 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)
11101110
nocycle: bool,
1111+
1112+
/// join conditions denoting the hierarchical relationship
1113+
relationships: Vec<Expr>,
11111114
},
11121115

11131116
/// START WITH
11141117
///
11151118
/// Optional on [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Hierarchical-Queries.html#GUID-0118DF1D-B9A9-41EB-8556-C6E7D6A5A84E)
11161119
/// when comming _after_ the `CONNECT BY`.
1117-
StartWith(Box<Expr>),
1120+
StartWith {
1121+
/// the `START` token
1122+
start_token: AttachedToken,
1123+
1124+
/// condition selecting the root rows of the hierarchy
1125+
condition: Box<Expr>,
1126+
},
11181127
}
11191128

11201129
impl fmt::Display for ConnectByKind {
11211130
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11221131
match self {
11231132
ConnectByKind::ConnectBy {
1124-
relationships,
1133+
connect_token: _,
11251134
nocycle,
1135+
relationships,
11261136
} => {
11271137
write!(
11281138
f,
@@ -1131,7 +1141,10 @@ impl fmt::Display for ConnectByKind {
11311141
relationships = display_comma_separated(relationships)
11321142
)
11331143
}
1134-
ConnectByKind::StartWith(condition) => {
1144+
ConnectByKind::StartWith {
1145+
start_token: _,
1146+
condition,
1147+
} => {
11351148
write!(f, "START WITH {condition}")
11361149
}
11371150
}

src/ast/spans.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2268,14 +2268,14 @@ impl Spanned for Select {
22682268
.chain(lateral_views.iter().map(|item| item.span()))
22692269
.chain(prewhere.iter().map(|item| item.span()))
22702270
.chain(selection.iter().map(|item| item.span()))
2271+
.chain(connect_by.iter().map(|item| item.span()))
22712272
.chain(core::iter::once(group_by.span()))
22722273
.chain(cluster_by.iter().map(|item| item.span()))
22732274
.chain(distribute_by.iter().map(|item| item.span()))
22742275
.chain(sort_by.iter().map(|item| item.span()))
22752276
.chain(having.iter().map(|item| item.span()))
22762277
.chain(named_window.iter().map(|item| item.span()))
2277-
.chain(qualify.iter().map(|item| item.span()))
2278-
.chain(connect_by.iter().map(|item| item.span())),
2278+
.chain(qualify.iter().map(|item| item.span())),
22792279
)
22802280
}
22812281
}
@@ -2284,10 +2284,17 @@ impl Spanned for ConnectByKind {
22842284
fn span(&self) -> Span {
22852285
match self {
22862286
ConnectByKind::ConnectBy {
2287-
relationships,
2287+
connect_token,
22882288
nocycle: _,
2289-
} => union_spans(relationships.iter().map(|item| item.span())),
2290-
ConnectByKind::StartWith(expr) => expr.span(),
2289+
relationships,
2290+
} => union_spans(
2291+
core::iter::once(connect_token.0.span())
2292+
.chain(relationships.last().iter().map(|item| item.span())),
2293+
),
2294+
ConnectByKind::StartWith {
2295+
start_token,
2296+
condition,
2297+
} => union_spans([start_token.0.span(), condition.span()].into_iter()),
22912298
}
22922299
}
22932300
}

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
@@ -14155,16 +14168,18 @@ impl<'a> Parser<'a> {
1415514168
pub fn maybe_parse_connect_by(&mut self) -> Result<Vec<ConnectByKind>, ParserError> {
1415614169
let mut clauses = Vec::with_capacity(2);
1415714170
loop {
14158-
if self.parse_keywords(&[Keyword::START, Keyword::WITH]) {
14159-
clauses.push(ConnectByKind::StartWith(self.parse_expr()?.into()));
14160-
} else if self.parse_keywords(&[Keyword::CONNECT, Keyword::BY]) {
14161-
let nocycle = self.parse_keyword(Keyword::NOCYCLE);
14162-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
14163-
parser.parse_comma_separated(Parser::parse_expr)
14164-
})?;
14171+
if let Some(idx) = self.parse_keywords_(&[Keyword::START, Keyword::WITH]) {
14172+
clauses.push(ConnectByKind::StartWith {
14173+
start_token: self.token_at(idx).clone().into(),
14174+
condition: self.parse_expr()?.into(),
14175+
});
14176+
} else if let Some(idx) = self.parse_keywords_(&[Keyword::CONNECT, Keyword::BY]) {
1416514177
clauses.push(ConnectByKind::ConnectBy {
14166-
relationships,
14167-
nocycle,
14178+
connect_token: self.token_at(idx).clone().into(),
14179+
nocycle: self.parse_keyword(Keyword::NOCYCLE),
14180+
relationships: self.with_state(ParserState::ConnectBy, |parser| {
14181+
parser.parse_comma_separated(Parser::parse_expr)
14182+
})?,
1416814183
});
1416914184
} else {
1417014185
break;

tests/sqlparser_common.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12669,25 +12669,27 @@ fn parse_connect_by() {
1266912669
window_before_qualify: false,
1267012670
value_table_mode: None,
1267112671
connect_by: vec![
12672-
ConnectByKind::StartWith(
12673-
Expr::BinaryOp {
12672+
ConnectByKind::StartWith {
12673+
start_token: AttachedToken::empty(),
12674+
condition: Expr::BinaryOp {
1267412675
left: Box::new(Expr::Identifier(Ident::new("title"))),
1267512676
op: BinaryOperator::Eq,
1267612677
right: Box::new(Expr::Value(
1267712678
Value::SingleQuotedString("president".to_owned()).with_empty_span(),
1267812679
)),
1267912680
}
1268012681
.into()
12681-
),
12682+
},
1268212683
ConnectByKind::ConnectBy {
12684+
connect_token: AttachedToken::empty(),
12685+
nocycle: false,
1268312686
relationships: vec![Expr::BinaryOp {
1268412687
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1268512688
op: BinaryOperator::Eq,
1268612689
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1268712690
"employee_id",
1268812691
))))),
1268912692
}],
12690-
nocycle: false,
1269112693
}
1269212694
],
1269312695
flavor: SelectFlavor::Standard,
@@ -12734,25 +12736,27 @@ fn parse_connect_by() {
1273412736
value_table_mode: None,
1273512737
connect_by: vec![
1273612738
ConnectByKind::ConnectBy {
12739+
connect_token: AttachedToken::empty(),
12740+
nocycle: false,
1273712741
relationships: vec![Expr::BinaryOp {
1273812742
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1273912743
op: BinaryOperator::Eq,
1274012744
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1274112745
"employee_id",
1274212746
))))),
1274312747
}],
12744-
nocycle: false,
1274512748
},
12746-
ConnectByKind::StartWith(
12747-
Expr::BinaryOp {
12749+
ConnectByKind::StartWith {
12750+
start_token: AttachedToken::empty(),
12751+
condition: Expr::BinaryOp {
1274812752
left: Box::new(Expr::Identifier(Ident::new("title"))),
1274912753
op: BinaryOperator::Eq,
1275012754
right: Box::new(Expr::Value(
1275112755
Value::SingleQuotedString("president".to_owned()).with_empty_span(),
1275212756
)),
1275312757
}
1275412758
.into()
12755-
)
12759+
},
1275612760
],
1275712761
flavor: SelectFlavor::Standard,
1275812762
}
@@ -12802,25 +12806,27 @@ fn parse_connect_by() {
1280212806
window_before_qualify: false,
1280312807
value_table_mode: None,
1280412808
connect_by: vec![
12805-
ConnectByKind::StartWith(
12806-
Expr::BinaryOp {
12809+
ConnectByKind::StartWith {
12810+
start_token: AttachedToken::empty(),
12811+
condition: Expr::BinaryOp {
1280712812
left: Box::new(Expr::Identifier(Ident::new("title"))),
1280812813
op: BinaryOperator::Eq,
1280912814
right: Box::new(Expr::Value(
1281012815
(Value::SingleQuotedString("president".to_owned(),)).with_empty_span()
1281112816
)),
1281212817
}
1281312818
.into()
12814-
),
12819+
},
1281512820
ConnectByKind::ConnectBy {
12821+
connect_token: AttachedToken::empty(),
12822+
nocycle: false,
1281612823
relationships: vec![Expr::BinaryOp {
1281712824
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1281812825
op: BinaryOperator::Eq,
1281912826
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1282012827
"employee_id",
1282112828
))))),
1282212829
}],
12823-
nocycle: false,
1282412830
}
1282512831
],
1282612832
flavor: SelectFlavor::Standard,
@@ -12882,12 +12888,13 @@ fn parse_connect_by() {
1288212888
window_before_qualify: false,
1288312889
value_table_mode: None,
1288412890
connect_by: vec![ConnectByKind::ConnectBy {
12891+
connect_token: AttachedToken::empty(),
12892+
nocycle: true,
1288512893
relationships: vec![Expr::BinaryOp {
1288612894
left: Expr::Identifier(Ident::new("parent")).into(),
1288712895
op: BinaryOperator::Eq,
1288812896
right: Expr::Prior(Expr::Identifier(Ident::new("child")).into()).into(),
1288912897
}],
12890-
nocycle: true,
1289112898
}],
1289212899
flavor: SelectFlavor::Standard,
1289312900
}

0 commit comments

Comments
 (0)