Skip to content

Commit cf272e7

Browse files
committed
Preserve order of START WITH / CONNECT BY; simplify parsing
1 parent 77b9603 commit cf272e7

11 files changed

Lines changed: 178 additions & 161 deletions

src/ast/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub use self::dml::{
8585
};
8686
pub use self::operator::{BinaryOperator, UnaryOperator};
8787
pub use self::query::{
88-
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
88+
AfterMatchSkip, ConnectByKind, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
8989
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, ExprWithAliasAndOrderBy, Fetch, ForClause,
9090
ForJson, ForXml, FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias,
9191
IlikeSelectItem, InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint,

src/ast/query.rs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ pub struct Select {
375375
/// WHERE
376376
pub selection: Option<Expr>,
377377
/// [START WITH ..] CONNECT BY ..
378-
pub connect_by: Option<ConnectBy>,
378+
pub connect_by: Vec<ConnectByKind>,
379379
/// GROUP BY
380380
pub group_by: GroupByExpr,
381381
/// CLUSTER BY (Hive)
@@ -475,9 +475,9 @@ impl fmt::Display for Select {
475475
SpaceOrNewline.fmt(f)?;
476476
Indent(selection).fmt(f)?;
477477
}
478-
if let Some(ref connect_by) = self.connect_by {
478+
for clause in &self.connect_by {
479479
SpaceOrNewline.fmt(f)?;
480-
connect_by.fmt(f)?;
480+
clause.fmt(f)?;
481481
}
482482
match &self.group_by {
483483
GroupByExpr::All(_) => {
@@ -1098,38 +1098,38 @@ impl fmt::Display for TableWithJoins {
10981098
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10991099
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11001100
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1101-
pub struct ConnectBy {
1101+
pub enum ConnectByKind {
1102+
/// CONNECT BY
1103+
ConnectBy {
1104+
/// the join conditions denoting the hierarchical relationship
1105+
relationships: Vec<Expr>,
1106+
1107+
/// [CONNECT BY] NOCYCLE
1108+
///
1109+
/// 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)
1110+
nocycle: bool,
1111+
},
1112+
11021113
/// START WITH
11031114
///
11041115
/// Optional on [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Hierarchical-Queries.html#GUID-0118DF1D-B9A9-41EB-8556-C6E7D6A5A84E)
11051116
/// when comming _after_ the `CONNECT BY`.
1106-
pub condition: Option<Expr>,
1107-
1108-
/// CONNECT BY
1109-
pub relationships: Vec<Expr>,
1110-
1111-
/// [CONNECT BY] NOCYCLE
1112-
///
1113-
/// 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)
1114-
pub nocycle: bool,
1117+
StartWith(Box<Expr>),
11151118
}
11161119

1117-
impl fmt::Display for ConnectBy {
1120+
impl fmt::Display for ConnectByKind {
11181121
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1119-
let Self {
1120-
condition,
1121-
relationships,
1122-
nocycle,
1123-
} = self;
1124-
if let Some(condition) = condition {
1125-
write!(f, "START WITH {condition} ")?;
1122+
match self {
1123+
ConnectByKind::ConnectBy { relationships, nocycle } => {
1124+
write!(f, "CONNECT BY {nocycle}{relationships}",
1125+
nocycle = if *nocycle { "NOCYCLE " } else { "" },
1126+
relationships = display_comma_separated(relationships)
1127+
)
1128+
},
1129+
ConnectByKind::StartWith(condition) => {
1130+
write!(f, "START WITH {condition}")
1131+
},
11261132
}
1127-
write!(
1128-
f,
1129-
"CONNECT BY {nocycle}{relationships}",
1130-
nocycle = if *nocycle { "NOCYCLE " } else { "" },
1131-
relationships = display_comma_separated(relationships)
1132-
)
11331133
}
11341134
}
11351135

src/ast/spans.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use super::{
3232
AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget,
3333
AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef,
3434
ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements,
35-
ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
35+
ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
3636
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr,
3737
ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg, FunctionArgExpr,
3838
FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound,
@@ -2280,20 +2280,14 @@ impl Spanned for Select {
22802280
}
22812281
}
22822282

2283-
impl Spanned for ConnectBy {
2283+
impl Spanned for ConnectByKind {
22842284
fn span(&self) -> Span {
2285-
let ConnectBy {
2286-
condition,
2287-
relationships,
2288-
nocycle: _,
2289-
} = self;
2290-
2291-
union_spans(
2292-
condition
2293-
.iter()
2294-
.map(Spanned::span)
2295-
.chain(relationships.iter().map(|item| item.span())),
2296-
)
2285+
match self {
2286+
ConnectByKind::ConnectBy { relationships, nocycle: _ } => {
2287+
union_spans(relationships.iter().map(|item| item.span()))
2288+
}
2289+
ConnectByKind::StartWith(expr) => expr.span(),
2290+
}
22972291
}
22982292
}
22992293

src/parser/mod.rs

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13902,7 +13902,7 @@ impl<'a> Parser<'a> {
1390213902
window_before_qualify: false,
1390313903
qualify: None,
1390413904
value_table_mode: None,
13905-
connect_by: None,
13905+
connect_by: vec![],
1390613906
flavor: SelectFlavor::FromFirstNoSelect,
1390713907
});
1390813908
}
@@ -14000,15 +14000,7 @@ impl<'a> Parser<'a> {
1400014000
None
1400114001
};
1400214002

14003-
let connect_by = if self.dialect.supports_connect_by()
14004-
&& self
14005-
.peek_one_of_keywords(&[Keyword::START, Keyword::CONNECT])
14006-
.is_some()
14007-
{
14008-
Some(self.parse_connect_by()?)
14009-
} else {
14010-
None
14011-
};
14003+
let connect_by = self.maybe_parse_connect_by()?;
1401214004

1401314005
let group_by = self
1401414006
.parse_optional_group_by()?
@@ -14183,37 +14175,22 @@ impl<'a> Parser<'a> {
1418314175
}
1418414176

1418514177
/// Parse a `CONNECT BY` clause (Oracle-style hierarchical query support).
14186-
pub fn parse_connect_by(&mut self) -> Result<ConnectBy, ParserError> {
14187-
let (condition, relationships, nocycle) = if self
14188-
.parse_keywords(&[Keyword::CONNECT, Keyword::BY])
14189-
{
14190-
let (relationships, nocycle) = self.with_state(ParserState::ConnectBy, |parser| {
14191-
let nocycle = parser.parse_keyword(Keyword::NOCYCLE);
14192-
parser
14193-
.parse_comma_separated(Parser::parse_expr)
14194-
.map(|exprs| (exprs, nocycle))
14195-
})?;
14196-
let condition = if self.parse_keywords(&[Keyword::START, Keyword::WITH]) {
14197-
Some(self.parse_expr()?)
14178+
pub fn maybe_parse_connect_by(&mut self) -> Result<Vec<ConnectByKind>, ParserError> {
14179+
let mut clauses = Vec::with_capacity(2);
14180+
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+
})?;
14188+
clauses.push(ConnectByKind::ConnectBy { relationships, nocycle });
1419814189
} else {
14199-
None
14200-
};
14201-
(condition, relationships, nocycle)
14202-
} else {
14203-
self.expect_keywords(&[Keyword::START, Keyword::WITH])?;
14204-
let condition = self.parse_expr()?;
14205-
self.expect_keywords(&[Keyword::CONNECT, Keyword::BY])?;
14206-
let nocycle = self.parse_keyword(Keyword::NOCYCLE);
14207-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
14208-
parser.parse_comma_separated(Parser::parse_expr)
14209-
})?;
14210-
(Some(condition), relationships, nocycle)
14211-
};
14212-
Ok(ConnectBy {
14213-
condition,
14214-
relationships,
14215-
nocycle,
14216-
})
14190+
break;
14191+
}
14192+
}
14193+
Ok(clauses)
1421714194
}
1421814195

1421914196
/// Parse `CREATE TABLE x AS TABLE y`

tests/sqlparser_bigquery.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2710,7 +2710,7 @@ fn test_export_data() {
27102710
qualify: None,
27112711
window_before_qualify: false,
27122712
value_table_mode: None,
2713-
connect_by: None,
2713+
connect_by: vec![],
27142714
flavor: SelectFlavor::Standard,
27152715
}))),
27162716
order_by: Some(OrderBy {
@@ -2815,7 +2815,7 @@ fn test_export_data() {
28152815
qualify: None,
28162816
window_before_qualify: false,
28172817
value_table_mode: None,
2818-
connect_by: None,
2818+
connect_by: vec![],
28192819
flavor: SelectFlavor::Standard,
28202820
}))),
28212821
order_by: Some(OrderBy {

tests/sqlparser_clickhouse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fn parse_map_access_expr() {
102102
window_before_qualify: false,
103103
qualify: None,
104104
value_table_mode: None,
105-
connect_by: None,
105+
connect_by: vec![],
106106
flavor: SelectFlavor::Standard,
107107
},
108108
select

0 commit comments

Comments
 (0)