Skip to content

Commit 858c7fe

Browse files
committed
Revert "support for CONNECT BY"
This reverts commit 906ad24.
1 parent 769a6cf commit 858c7fe

6 files changed

Lines changed: 5 additions & 210 deletions

File tree

src/ast/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ pub use self::ddl::{
3939
};
4040
pub use self::operator::{BinaryOperator, UnaryOperator};
4141
pub use self::query::{
42-
ConnectBy, Cte, CteAsMaterialized, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch,
43-
ForClause, ForJson, ForXml, GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator,
42+
Cte, CteAsMaterialized, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause,
43+
ForJson, ForXml, GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator,
4444
JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
4545
NamedWindowDefinition, NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem,
4646
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator,
@@ -737,8 +737,6 @@ pub enum Expr {
737737
///
738738
/// See <https://docs.snowflake.com/en/sql-reference/constructs/where#joins-in-the-where-clause>.
739739
OuterJoin(Box<Expr>),
740-
/// A reference to the prior level in a CONNECT BY clause.
741-
Prior(Box<Expr>),
742740
}
743741

744742
impl fmt::Display for CastFormat {
@@ -1215,7 +1213,6 @@ impl fmt::Display for Expr {
12151213
Expr::OuterJoin(expr) => {
12161214
write!(f, "{expr} (+)")
12171215
}
1218-
Expr::Prior(expr) => write!(f, "PRIOR {expr}"),
12191216
}
12201217
}
12211218
}

src/ast/query.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ impl fmt::Display for Query {
9292
pub enum SetExpr {
9393
/// Restricted SELECT .. FROM .. HAVING (no ORDER BY or set operations)
9494
Select(Box<Select>),
95-
/// SELECT .. FROM .. STARTING WITH .. CONNECT BY
96-
ConnectBy(ConnectBy),
9795
/// Parenthesized SELECT subquery, which may include more set operations
9896
/// in its body and an optional ORDER BY / LIMIT.
9997
Query(Box<Query>),
@@ -114,7 +112,6 @@ impl fmt::Display for SetExpr {
114112
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115113
match self {
116114
SetExpr::Select(s) => write!(f, "{s}"),
117-
SetExpr::ConnectBy(c) => write!(f, "{c}"),
118115
SetExpr::Query(q) => write!(f, "({q})"),
119116
SetExpr::Values(v) => write!(f, "{v}"),
120117
SetExpr::Insert(v) => write!(f, "{v}"),
@@ -705,36 +702,6 @@ impl fmt::Display for TableWithJoins {
705702
}
706703
}
707704

708-
/// Joins a table to itself to process hierarchical data in the table.
709-
///
710-
/// See <https://docs.snowflake.com/en/sql-reference/constructs/connect-by>.
711-
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
712-
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
713-
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
714-
pub struct ConnectBy {
715-
/// SELECT
716-
pub projection: Vec<SelectItem>,
717-
/// FROM
718-
pub from: Vec<TableWithJoins>,
719-
/// START WITH
720-
pub condition: Expr,
721-
/// CONNECT BY
722-
pub relationships: Vec<Expr>,
723-
}
724-
725-
impl fmt::Display for ConnectBy {
726-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
727-
write!(
728-
f,
729-
"SELECT {projection} FROM {from} START WITH {condition} CONNECT BY {relationships}",
730-
projection = display_comma_separated(&self.projection),
731-
from = display_comma_separated(&self.from),
732-
condition = self.condition,
733-
relationships = display_comma_separated(&self.relationships)
734-
)
735-
}
736-
}
737-
738705
/// A table name or a parenthesized subquery with an optional alias
739706
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
740707
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/keywords.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -798,9 +798,6 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
798798
Keyword::FOR,
799799
// for MYSQL PARTITION SELECTION
800800
Keyword::PARTITION,
801-
// for Snowflake START WITH .. CONNECT BY
802-
Keyword::START,
803-
Keyword::CONNECT,
804801
];
805802

806803
/// Can't be used as a column alias, so that `SELECT <expr> alias`

src/parser/mod.rs

Lines changed: 3 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -256,22 +256,10 @@ impl ParserOptions {
256256
}
257257
}
258258

259-
#[derive(Copy, Clone)]
260-
enum ParserState {
261-
/// The default state of the parser.
262-
Normal,
263-
/// The state when parsing a CONNECT BY expression. This allows parsing
264-
/// PRIOR expressions while still allowing prior as an identifier name
265-
/// in other contexts.
266-
ConnectBy,
267-
}
268-
269259
pub struct Parser<'a> {
270260
tokens: Vec<TokenWithLocation>,
271261
/// The index of the first unprocessed token in `self.tokens`
272262
index: usize,
273-
/// The current state of the parser.
274-
state: ParserState,
275263
/// The current dialect to use
276264
dialect: &'a dyn Dialect,
277265
/// Additional options that allow you to mix & match behavior
@@ -302,7 +290,6 @@ impl<'a> Parser<'a> {
302290
Self {
303291
tokens: vec![],
304292
index: 0,
305-
state: ParserState::Normal,
306293
dialect,
307294
recursion_counter: RecursionCounter::new(DEFAULT_REMAINING_DEPTH),
308295
options: ParserOptions::default(),
@@ -979,10 +966,6 @@ impl<'a> Parser<'a> {
979966
self.prev_token();
980967
self.parse_bigquery_struct_literal()
981968
}
982-
Keyword::PRIOR if matches!(self.state, ParserState::ConnectBy) => {
983-
let expr = self.parse_subexpr(Self::PLUS_MINUS_PREC)?;
984-
Ok(Expr::Prior(Box::new(expr)))
985-
}
986969
// Here `w` is a word, check if it's a part of a multi-part
987970
// identifier, a function call, or a simple identifier:
988971
_ => match self.peek_token().token {
@@ -7179,7 +7162,7 @@ impl<'a> Parser<'a> {
71797162
// We parse the expression using a Pratt parser, as in `parse_expr()`.
71807163
// Start by parsing a restricted SELECT or a `(subquery)`:
71817164
let mut expr = if self.parse_keyword(Keyword::SELECT) {
7182-
self.parse_select()?
7165+
SetExpr::Select(Box::new(self.parse_select()?))
71837166
} else if self.consume_token(&Token::LParen) {
71847167
// CTEs are not allowed here, but the parser currently accepts them
71857168
let subquery = self.parse_query()?;
@@ -7267,7 +7250,7 @@ impl<'a> Parser<'a> {
72677250

72687251
/// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`),
72697252
/// assuming the initial `SELECT` was already consumed
7270-
pub fn parse_select(&mut self) -> Result<SetExpr, ParserError> {
7253+
pub fn parse_select(&mut self) -> Result<Select, ParserError> {
72717254
let value_table_mode =
72727255
if dialect_of!(self is BigQueryDialect) && self.parse_keyword(Keyword::AS) {
72737256
if self.parse_keyword(Keyword::VALUE) {
@@ -7319,18 +7302,6 @@ impl<'a> Parser<'a> {
73197302
vec![]
73207303
};
73217304

7322-
if distinct.is_none()
7323-
&& top.is_none()
7324-
&& into.is_none()
7325-
&& !from.is_empty()
7326-
&& self
7327-
.parse_one_of_keywords(&[Keyword::START, Keyword::CONNECT])
7328-
.is_some()
7329-
{
7330-
self.prev_token();
7331-
return Ok(SetExpr::ConnectBy(self.parse_connect_by(projection, from)?));
7332-
}
7333-
73347305
let mut lateral_views = vec![];
73357306
loop {
73367307
if self.parse_keywords(&[Keyword::LATERAL, Keyword::VIEW]) {
@@ -7414,7 +7385,7 @@ impl<'a> Parser<'a> {
74147385
None
74157386
};
74167387

7417-
Ok(SetExpr::Select(Box::new(Select {
7388+
Ok(Select {
74187389
distinct,
74197390
top,
74207391
projection,
@@ -7430,48 +7401,6 @@ impl<'a> Parser<'a> {
74307401
named_window: named_windows,
74317402
qualify,
74327403
value_table_mode,
7433-
})))
7434-
}
7435-
7436-
fn with_state<T, F>(&mut self, state: ParserState, mut f: F) -> Result<T, ParserError>
7437-
where
7438-
F: FnMut(&mut Parser) -> Result<T, ParserError>,
7439-
{
7440-
let current_state = self.state;
7441-
self.state = state;
7442-
let res = f(self);
7443-
self.state = current_state;
7444-
res
7445-
}
7446-
7447-
pub fn parse_connect_by(
7448-
&mut self,
7449-
projection: Vec<SelectItem>,
7450-
from: Vec<TableWithJoins>,
7451-
) -> Result<ConnectBy, ParserError> {
7452-
debug_assert!(!from.is_empty());
7453-
7454-
let (condition, relationships) = if self.parse_keywords(&[Keyword::CONNECT, Keyword::BY]) {
7455-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
7456-
parser.parse_comma_separated(Parser::parse_expr)
7457-
})?;
7458-
self.expect_keywords(&[Keyword::START, Keyword::WITH])?;
7459-
let condition = self.parse_expr()?;
7460-
(condition, relationships)
7461-
} else {
7462-
self.expect_keywords(&[Keyword::START, Keyword::WITH])?;
7463-
let condition = self.parse_expr()?;
7464-
self.expect_keywords(&[Keyword::CONNECT, Keyword::BY])?;
7465-
let relationships = self.with_state(ParserState::ConnectBy, |parser| {
7466-
parser.parse_comma_separated(Parser::parse_expr)
7467-
})?;
7468-
(condition, relationships)
7469-
};
7470-
Ok(ConnectBy {
7471-
projection,
7472-
from,
7473-
condition,
7474-
relationships,
74757404
})
74767405
}
74777406

src/test_utils.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,6 @@ impl TestedDialects {
157157
}
158158
}
159159

160-
/// Ensures that `sql` parses as a single [Query], and that
161-
/// re-serializing the parse result matches the given canonical
162-
/// sql string.
163-
pub fn verified_query_with_canonical(&self, query: &str, canonical: &str) -> Query {
164-
match self.one_statement_parses_to(query, canonical) {
165-
Statement::Query(query) => *query,
166-
_ => panic!("Expected Query"),
167-
}
168-
}
169-
170160
/// Ensures that `sql` parses as a single [Select], and that
171161
/// re-serializing the parse result produces the same `sql`
172162
/// string (is not modified after a serialization round-trip).

tests/sqlparser_snowflake.rs

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,88 +1508,3 @@ fn parse_comma_outer_join() {
15081508
"SELECT t1.c1, t2.c2 FROM t1, t2 WHERE t1.c1 = t2.c2 (+)",
15091509
);
15101510
}
1511-
1512-
#[test]
1513-
fn parse_connect_by() {
1514-
let expect_query = Query {
1515-
with: None,
1516-
body: Box::new(SetExpr::ConnectBy(ConnectBy {
1517-
projection: vec![
1518-
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("employee_id"))),
1519-
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("manager_id"))),
1520-
SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("title"))),
1521-
],
1522-
from: vec![TableWithJoins {
1523-
relation: TableFactor::Table {
1524-
name: ObjectName(vec![Ident::new("employees")]),
1525-
alias: None,
1526-
args: None,
1527-
with_hints: vec![],
1528-
version: None,
1529-
partitions: vec![],
1530-
},
1531-
joins: vec![],
1532-
}],
1533-
condition: Expr::BinaryOp {
1534-
left: Box::new(Expr::Identifier(Ident::new("title"))),
1535-
op: BinaryOperator::Eq,
1536-
right: Box::new(Expr::Value(Value::SingleQuotedString(
1537-
"president".to_owned(),
1538-
))),
1539-
},
1540-
relationships: vec![Expr::BinaryOp {
1541-
left: Box::new(Expr::Identifier(Ident::new("manager_id"))),
1542-
op: BinaryOperator::Eq,
1543-
right: Box::new(Expr::Prior(Box::new(Expr::Identifier(Ident::new(
1544-
"employee_id",
1545-
))))),
1546-
}],
1547-
})),
1548-
order_by: vec![OrderByExpr {
1549-
expr: Expr::Identifier(Ident::new("employee_id")),
1550-
asc: None,
1551-
nulls_first: None,
1552-
}],
1553-
limit: None,
1554-
limit_by: vec![],
1555-
offset: None,
1556-
fetch: None,
1557-
locks: vec![],
1558-
for_clause: None,
1559-
};
1560-
1561-
let connect_by_1 = concat!(
1562-
"SELECT employee_id, manager_id, title FROM employees ",
1563-
"START WITH title = 'president' ",
1564-
"CONNECT BY manager_id = PRIOR employee_id ",
1565-
"ORDER BY employee_id"
1566-
);
1567-
1568-
assert_eq!(
1569-
snowflake_and_generic().verified_query(connect_by_1),
1570-
expect_query
1571-
);
1572-
1573-
// CONNECT BY can come before START WITH
1574-
let connect_by_2 = concat!(
1575-
"SELECT employee_id, manager_id, title FROM employees ",
1576-
"CONNECT BY manager_id = PRIOR employee_id ",
1577-
"START WITH title = 'president' ",
1578-
"ORDER BY employee_id"
1579-
);
1580-
assert_eq!(
1581-
snowflake_and_generic().verified_query_with_canonical(connect_by_2, connect_by_1),
1582-
expect_query
1583-
);
1584-
1585-
// PRIOR expressions are only valid within a CONNECT BY, and the the token
1586-
// `prior` is valid as an identifier anywhere else.
1587-
assert_eq!(
1588-
snowflake_and_generic()
1589-
.verified_only_select("SELECT prior FROM some_table")
1590-
.projection,
1591-
vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident::new(
1592-
"prior"
1593-
)))]
1594-
);
1595-
}

0 commit comments

Comments
 (0)