Skip to content

Commit 365f7f7

Browse files
committed
support union
1 parent ceb9496 commit 365f7f7

3 files changed

Lines changed: 60 additions & 0 deletions

File tree

src/ast/query.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,15 @@ pub enum PipeOperator {
26902690
///
26912691
/// See more at <https://cloud.google.com/bigquery/docs/reference/standard-sql/pipe-syntax#rename_pipe_operator>
26922692
Rename { mappings: Vec<IdentWithAlias> },
2693+
/// Combines the input table with one or more tables using UNION.
2694+
///
2695+
/// Syntax: `|> UNION [ALL|DISTINCT] (<query>), (<query>), ...`
2696+
///
2697+
/// See more at <https://cloud.google.com/bigquery/docs/reference/standard-sql/pipe-syntax#union_pipe_operator>
2698+
Union {
2699+
set_quantifier: SetQuantifier,
2700+
queries: Vec<Box<Query>>,
2701+
},
26932702
}
26942703

26952704
impl fmt::Display for PipeOperator {
@@ -2748,6 +2757,25 @@ impl fmt::Display for PipeOperator {
27482757
PipeOperator::Rename { mappings } => {
27492758
write!(f, "RENAME {}", display_comma_separated(mappings))
27502759
}
2760+
PipeOperator::Union { set_quantifier, queries } => {
2761+
write!(f, "UNION")?;
2762+
match set_quantifier {
2763+
SetQuantifier::All => write!(f, " ALL")?,
2764+
SetQuantifier::Distinct => write!(f, " DISTINCT")?,
2765+
SetQuantifier::None => {},
2766+
_ => {
2767+
write!(f, " {}", set_quantifier)?;
2768+
}
2769+
}
2770+
write!(f, " ")?;
2771+
for (i, query) in queries.iter().enumerate() {
2772+
if i > 0 {
2773+
write!(f, ", ")?;
2774+
}
2775+
write!(f, "({})", query)?;
2776+
}
2777+
Ok(())
2778+
}
27512779
}
27522780
}
27532781
}

src/parser/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11084,6 +11084,7 @@ impl<'a> Parser<'a> {
1108411084
Keyword::ORDER,
1108511085
Keyword::TABLESAMPLE,
1108611086
Keyword::RENAME,
11087+
Keyword::UNION,
1108711088
])?;
1108811089
match kw {
1108911090
Keyword::SELECT => {
@@ -11154,6 +11155,19 @@ impl<'a> Parser<'a> {
1115411155
let mappings = self.parse_comma_separated(Parser::parse_identifier_with_optional_alias)?;
1115511156
pipe_operators.push(PipeOperator::Rename { mappings });
1115611157
}
11158+
Keyword::UNION => {
11159+
// Reuse existing set quantifier parser for consistent BY NAME support
11160+
let set_quantifier = self.parse_set_quantifier(&Some(SetOperator::Union));
11161+
// BigQuery UNION pipe operator requires parentheses around queries
11162+
// Parse comma-separated list of parenthesized queries
11163+
let queries = self.parse_comma_separated(|parser| {
11164+
parser.expect_token(&Token::LParen)?;
11165+
let query = parser.parse_query()?;
11166+
parser.expect_token(&Token::RParen)?;
11167+
Ok(query)
11168+
})?;
11169+
pipe_operators.push(PipeOperator::Union { set_quantifier, queries });
11170+
}
1115711171
unhandled => {
1115811172
return Err(ParserError::ParserError(format!(
1115911173
"`expect_one_of_keywords` further up allowed unhandled keyword: {unhandled:?}"

tests/sqlparser_common.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15201,6 +15201,24 @@ fn parse_pipeline_operator() {
1520115201
"SELECT * FROM users |> RENAME id AS user_id",
1520215202
);
1520315203

15204+
// union pipe operator
15205+
dialects.verified_stmt("SELECT * FROM users |> UNION ALL (SELECT * FROM admins)");
15206+
dialects.verified_stmt("SELECT * FROM users |> UNION DISTINCT (SELECT * FROM admins)");
15207+
dialects.verified_stmt("SELECT * FROM users |> UNION (SELECT * FROM admins)");
15208+
15209+
// union pipe operator with multiple queries
15210+
dialects.verified_stmt("SELECT * FROM users |> UNION ALL (SELECT * FROM admins), (SELECT * FROM guests)");
15211+
dialects.verified_stmt("SELECT * FROM users |> UNION DISTINCT (SELECT * FROM admins), (SELECT * FROM guests), (SELECT * FROM employees)");
15212+
dialects.verified_stmt("SELECT * FROM users |> UNION (SELECT * FROM admins), (SELECT * FROM guests)");
15213+
15214+
// union pipe operator with BY NAME modifier
15215+
dialects.verified_stmt("SELECT * FROM users |> UNION BY NAME (SELECT * FROM admins)");
15216+
dialects.verified_stmt("SELECT * FROM users |> UNION ALL BY NAME (SELECT * FROM admins)");
15217+
dialects.verified_stmt("SELECT * FROM users |> UNION DISTINCT BY NAME (SELECT * FROM admins)");
15218+
15219+
// union pipe operator with BY NAME and multiple queries
15220+
dialects.verified_stmt("SELECT * FROM users |> UNION BY NAME (SELECT * FROM admins), (SELECT * FROM guests)");
15221+
1520415222
// many pipes
1520515223
dialects.verified_stmt(
1520615224
"SELECT * FROM CustomerOrders |> AGGREGATE SUM(cost) AS total_cost GROUP BY customer_id, state, item_type |> EXTEND COUNT(*) OVER (PARTITION BY customer_id) AS num_orders |> WHERE num_orders > 1 |> AGGREGATE AVG(total_cost) AS average GROUP BY state DESC, item_type ASC",

0 commit comments

Comments
 (0)