Skip to content

Commit 5310f2d

Browse files
committed
Begin tokens of START WITH / CONNECT BY clauses
1 parent 5e0d2ed commit 5310f2d

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
@@ -4511,16 +4511,29 @@ impl<'a> Parser<'a> {
45114511
/// consumed and returns false
45124512
#[must_use]
45134513
pub fn parse_keywords(&mut self, keywords: &[Keyword]) -> bool {
4514-
let index = self.index;
4515-
for &keyword in keywords {
4516-
if !self.parse_keyword(keyword) {
4517-
// println!("parse_keywords aborting .. did not find {:?}", keyword);
4518-
// reset index and return immediately
4519-
self.index = index;
4520-
return false;
4514+
self.parse_keywords_(keywords).is_some()
4515+
}
4516+
4517+
/// Just like [Self::parse_keywords], but - upon success - returns the
4518+
/// token index of the first keyword.
4519+
#[must_use]
4520+
fn parse_keywords_(&mut self, keywords: &[Keyword]) -> Option<usize> {
4521+
let start_index = self.index;
4522+
let mut first_keyword_index = None;
4523+
match keywords {
4524+
[keyword, keywords @ ..] if self.parse_keyword(*keyword) => {
4525+
first_keyword_index = Some(self.index.saturating_sub(1));
4526+
for &keyword in keywords {
4527+
if !self.parse_keyword(keyword) {
4528+
self.index = start_index;
4529+
first_keyword_index = None;
4530+
break;
4531+
}
4532+
}
45214533
}
4534+
_ => {}
45224535
}
4523-
true
4536+
first_keyword_index
45244537
}
45254538

45264539
/// If the current token is one of the given `keywords`, returns the keyword
@@ -14178,16 +14191,18 @@ impl<'a> Parser<'a> {
1417814191
pub fn maybe_parse_connect_by(&mut self) -> Result<Vec<ConnectByKind>, ParserError> {
1417914192
let mut clauses = Vec::with_capacity(2);
1418014193
loop {
14181-
if self.parse_keywords(&[Keyword::START, Keyword::WITH]) {
14182-
clauses.push(ConnectByKind::StartWith(self.parse_expr()?.into()));
14183-
} else if self.parse_keywords(&[Keyword::CONNECT, Keyword::BY]) {
14184-
let nocycle = self.parse_keyword(Keyword::NOCYCLE);
14185-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
14186-
parser.parse_comma_separated(Parser::parse_expr)
14187-
})?;
14194+
if let Some(idx) = self.parse_keywords_(&[Keyword::START, Keyword::WITH]) {
14195+
clauses.push(ConnectByKind::StartWith {
14196+
start_token: self.token_at(idx).clone().into(),
14197+
condition: self.parse_expr()?.into(),
14198+
});
14199+
} else if let Some(idx) = self.parse_keywords_(&[Keyword::CONNECT, Keyword::BY]) {
1418814200
clauses.push(ConnectByKind::ConnectBy {
14189-
relationships,
14190-
nocycle,
14201+
connect_token: self.token_at(idx).clone().into(),
14202+
nocycle: self.parse_keyword(Keyword::NOCYCLE),
14203+
relationships: self.with_state(ParserState::ConnectBy, |parser| {
14204+
parser.parse_comma_separated(Parser::parse_expr)
14205+
})?,
1419114206
});
1419214207
} else {
1419314208
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)