Skip to content

Commit f3359c6

Browse files
committed
Preserve order of START WITH / CONNECT BY; simplify parsing
1 parent f8df94a commit f3359c6

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
@@ -87,7 +87,7 @@ pub use self::dml::{
8787
};
8888
pub use self::operator::{BinaryOperator, UnaryOperator};
8989
pub use self::query::{
90-
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
90+
AfterMatchSkip, ConnectByKind, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
9191
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, ExprWithAliasAndOrderBy, Fetch, ForClause,
9292
ForJson, ForXml, FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias,
9393
IlikeSelectItem, InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint,

src/ast/query.rs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ pub struct Select {
481481
/// WHERE
482482
pub selection: Option<Expr>,
483483
/// [START WITH ..] CONNECT BY ..
484-
pub connect_by: Option<ConnectBy>,
484+
pub connect_by: Vec<ConnectByKind>,
485485
/// GROUP BY
486486
pub group_by: GroupByExpr,
487487
/// CLUSTER BY (Hive)
@@ -585,9 +585,9 @@ impl fmt::Display for Select {
585585
SpaceOrNewline.fmt(f)?;
586586
Indent(selection).fmt(f)?;
587587
}
588-
if let Some(ref connect_by) = self.connect_by {
588+
for clause in &self.connect_by {
589589
SpaceOrNewline.fmt(f)?;
590-
connect_by.fmt(f)?;
590+
clause.fmt(f)?;
591591
}
592592
match &self.group_by {
593593
GroupByExpr::All(_) => {
@@ -1208,38 +1208,38 @@ impl fmt::Display for TableWithJoins {
12081208
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
12091209
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12101210
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1211-
pub struct ConnectBy {
1211+
pub enum ConnectByKind {
1212+
/// CONNECT BY
1213+
ConnectBy {
1214+
/// the join conditions denoting the hierarchical relationship
1215+
relationships: Vec<Expr>,
1216+
1217+
/// [CONNECT BY] NOCYCLE
1218+
///
1219+
/// 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)
1220+
nocycle: bool,
1221+
},
1222+
12121223
/// START WITH
12131224
///
12141225
/// Optional on [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Hierarchical-Queries.html#GUID-0118DF1D-B9A9-41EB-8556-C6E7D6A5A84E)
12151226
/// when comming _after_ the `CONNECT BY`.
1216-
pub condition: Option<Expr>,
1217-
1218-
/// CONNECT BY
1219-
pub relationships: Vec<Expr>,
1220-
1221-
/// [CONNECT BY] NOCYCLE
1222-
///
1223-
/// 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)
1224-
pub nocycle: bool,
1227+
StartWith(Box<Expr>),
12251228
}
12261229

1227-
impl fmt::Display for ConnectBy {
1230+
impl fmt::Display for ConnectByKind {
12281231
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1229-
let Self {
1230-
condition,
1231-
relationships,
1232-
nocycle,
1233-
} = self;
1234-
if let Some(condition) = condition {
1235-
write!(f, "START WITH {condition} ")?;
1232+
match self {
1233+
ConnectByKind::ConnectBy { relationships, nocycle } => {
1234+
write!(f, "CONNECT BY {nocycle}{relationships}",
1235+
nocycle = if *nocycle { "NOCYCLE " } else { "" },
1236+
relationships = display_comma_separated(relationships)
1237+
)
1238+
},
1239+
ConnectByKind::StartWith(condition) => {
1240+
write!(f, "START WITH {condition}")
1241+
},
12361242
}
1237-
write!(
1238-
f,
1239-
"CONNECT BY {nocycle}{relationships}",
1240-
nocycle = if *nocycle { "NOCYCLE " } else { "" },
1241-
relationships = display_comma_separated(relationships)
1242-
)
12431243
}
12441244
}
12451245

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,
@@ -2281,20 +2281,14 @@ impl Spanned for Select {
22812281
}
22822282
}
22832283

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

src/parser/mod.rs

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13921,7 +13921,7 @@ impl<'a> Parser<'a> {
1392113921
window_before_qualify: false,
1392213922
qualify: None,
1392313923
value_table_mode: None,
13924-
connect_by: None,
13924+
connect_by: vec![],
1392513925
flavor: SelectFlavor::FromFirstNoSelect,
1392613926
});
1392713927
}
@@ -14032,15 +14032,7 @@ impl<'a> Parser<'a> {
1403214032
None
1403314033
};
1403414034

14035-
let connect_by = if self.dialect.supports_connect_by()
14036-
&& self
14037-
.peek_one_of_keywords(&[Keyword::START, Keyword::CONNECT])
14038-
.is_some()
14039-
{
14040-
Some(self.parse_connect_by()?)
14041-
} else {
14042-
None
14043-
};
14035+
let connect_by = self.maybe_parse_connect_by()?;
1404414036

1404514037
let group_by = self
1404614038
.parse_optional_group_by()?
@@ -14278,37 +14270,22 @@ impl<'a> Parser<'a> {
1427814270
}
1427914271

1428014272
/// Parse a `CONNECT BY` clause (Oracle-style hierarchical query support).
14281-
pub fn parse_connect_by(&mut self) -> Result<ConnectBy, ParserError> {
14282-
let (condition, relationships, nocycle) = if self
14283-
.parse_keywords(&[Keyword::CONNECT, Keyword::BY])
14284-
{
14285-
let (relationships, nocycle) = self.with_state(ParserState::ConnectBy, |parser| {
14286-
let nocycle = parser.parse_keyword(Keyword::NOCYCLE);
14287-
parser
14288-
.parse_comma_separated(Parser::parse_expr)
14289-
.map(|exprs| (exprs, nocycle))
14290-
})?;
14291-
let condition = if self.parse_keywords(&[Keyword::START, Keyword::WITH]) {
14292-
Some(self.parse_expr()?)
14273+
pub fn maybe_parse_connect_by(&mut self) -> Result<Vec<ConnectByKind>, ParserError> {
14274+
let mut clauses = Vec::with_capacity(2);
14275+
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+
})?;
14283+
clauses.push(ConnectByKind::ConnectBy { relationships, nocycle });
1429314284
} else {
14294-
None
14295-
};
14296-
(condition, relationships, nocycle)
14297-
} else {
14298-
self.expect_keywords(&[Keyword::START, Keyword::WITH])?;
14299-
let condition = self.parse_expr()?;
14300-
self.expect_keywords(&[Keyword::CONNECT, Keyword::BY])?;
14301-
let nocycle = self.parse_keyword(Keyword::NOCYCLE);
14302-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
14303-
parser.parse_comma_separated(Parser::parse_expr)
14304-
})?;
14305-
(Some(condition), relationships, nocycle)
14306-
};
14307-
Ok(ConnectBy {
14308-
condition,
14309-
relationships,
14310-
nocycle,
14311-
})
14285+
break;
14286+
}
14287+
}
14288+
Ok(clauses)
1431214289
}
1431314290

1431414291
/// 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
@@ -2711,7 +2711,7 @@ fn test_export_data() {
27112711
qualify: None,
27122712
window_before_qualify: false,
27132713
value_table_mode: None,
2714-
connect_by: None,
2714+
connect_by: vec![],
27152715
flavor: SelectFlavor::Standard,
27162716
}))),
27172717
order_by: Some(OrderBy {
@@ -2817,7 +2817,7 @@ fn test_export_data() {
28172817
qualify: None,
28182818
window_before_qualify: false,
28192819
value_table_mode: None,
2820-
connect_by: None,
2820+
connect_by: vec![],
28212821
flavor: SelectFlavor::Standard,
28222822
}))),
28232823
order_by: Some(OrderBy {

tests/sqlparser_clickhouse.rs

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

0 commit comments

Comments
 (0)