@@ -9947,13 +9947,41 @@ impl<'a> Parser<'a> {
99479947 Ok(IdentWithAlias { ident, alias })
99489948 }
99499949
9950+ /// Parse `identifier [AS] identifier` where the AS keyword is optional
99509951 pub fn parse_identifier_with_optional_alias(&mut self) -> Result<IdentWithAlias, ParserError> {
99519952 let ident = self.parse_identifier()?;
99529953 let _after_as = self.parse_keyword(Keyword::AS);
99539954 let alias = self.parse_identifier()?;
99549955 Ok(IdentWithAlias { ident, alias })
99559956 }
99569957
9958+ /// Parse comma-separated list of parenthesized queries for pipe operators
9959+ fn parse_pipe_operator_queries(&mut self) -> Result<Vec<Box<Query>>, ParserError> {
9960+ self.parse_comma_separated(|parser| {
9961+ parser.expect_token(&Token::LParen)?;
9962+ let query = parser.parse_query()?;
9963+ parser.expect_token(&Token::RParen)?;
9964+ Ok(query)
9965+ })
9966+ }
9967+
9968+ /// Parse optional alias (with or without AS keyword) for pipe operators
9969+ fn parse_optional_pipe_alias(&mut self) -> Result<Option<Ident>, ParserError> {
9970+ if self.parse_keyword(Keyword::AS) {
9971+ Some(self.parse_identifier()).transpose()
9972+ } else {
9973+ // Check if the next token is an identifier (implicit alias)
9974+ let checkpoint = self.index;
9975+ match self.parse_identifier() {
9976+ Ok(ident) => Ok(Some(ident)),
9977+ Err(_) => {
9978+ self.index = checkpoint; // Rewind on failure
9979+ Ok(None)
9980+ }
9981+ }
9982+ }
9983+ }
9984+
99579985 /// Optionally parses an alias for a select list item
99589986 fn maybe_parse_select_item_alias(&mut self) -> Result<Option<Ident>, ParserError> {
99599987 fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
@@ -11164,14 +11192,7 @@ impl<'a> Parser<'a> {
1116411192 Keyword::UNION => {
1116511193 // Reuse existing set quantifier parser for consistent BY NAME support
1116611194 let set_quantifier = self.parse_set_quantifier(&Some(SetOperator::Union));
11167- // BigQuery UNION pipe operator requires parentheses around queries
11168- // Parse comma-separated list of parenthesized queries
11169- let queries = self.parse_comma_separated(|parser| {
11170- parser.expect_token(&Token::LParen)?;
11171- let query = parser.parse_query()?;
11172- parser.expect_token(&Token::RParen)?;
11173- Ok(query)
11174- })?;
11195+ let queries = self.parse_pipe_operator_queries()?;
1117511196 pipe_operators.push(PipeOperator::Union {
1117611197 set_quantifier,
1117711198 queries,
@@ -11189,14 +11210,7 @@ impl<'a> Parser<'a> {
1118911210 "INTERSECT pipe operator requires DISTINCT modifier".to_string(),
1119011211 ));
1119111212 };
11192- // BigQuery INTERSECT pipe operator requires parentheses around queries
11193- // Parse comma-separated list of parenthesized queries
11194- let queries = self.parse_comma_separated(|parser| {
11195- parser.expect_token(&Token::LParen)?;
11196- let query = parser.parse_query()?;
11197- parser.expect_token(&Token::RParen)?;
11198- Ok(query)
11199- })?;
11213+ let queries = self.parse_pipe_operator_queries()?;
1120011214 pipe_operators.push(PipeOperator::Intersect {
1120111215 set_quantifier,
1120211216 queries,
@@ -11214,14 +11228,7 @@ impl<'a> Parser<'a> {
1121411228 "EXCEPT pipe operator requires DISTINCT modifier".to_string(),
1121511229 ));
1121611230 };
11217- // BigQuery EXCEPT pipe operator requires parentheses around queries
11218- // Parse comma-separated list of parenthesized queries
11219- let queries = self.parse_comma_separated(|parser| {
11220- parser.expect_token(&Token::LParen)?;
11221- let query = parser.parse_query()?;
11222- parser.expect_token(&Token::RParen)?;
11223- Ok(query)
11224- })?;
11231+ let queries = self.parse_pipe_operator_queries()?;
1122511232 pipe_operators.push(PipeOperator::Except {
1122611233 set_quantifier,
1122711234 queries,
@@ -11271,20 +11278,7 @@ impl<'a> Parser<'a> {
1127111278 self.expect_token(&Token::RParen)?;
1127211279 self.expect_token(&Token::RParen)?;
1127311280
11274- // Parse optional alias (with or without AS keyword)
11275- let alias = if self.parse_keyword(Keyword::AS) {
11276- Some(self.parse_identifier()?)
11277- } else {
11278- // Check if the next token is an identifier (implicit alias)
11279- let checkpoint = self.index;
11280- match self.parse_identifier() {
11281- Ok(ident) => Some(ident),
11282- Err(_) => {
11283- self.index = checkpoint; // Rewind on failure
11284- None
11285- }
11286- }
11287- };
11281+ let alias = self.parse_optional_pipe_alias()?;
1128811282
1128911283 pipe_operators.push(PipeOperator::Pivot {
1129011284 aggregate_functions,
@@ -11316,20 +11310,7 @@ impl<'a> Parser<'a> {
1131611310
1131711311 self.expect_token(&Token::RParen)?;
1131811312
11319- // Parse optional alias (with or without AS keyword)
11320- let alias = if self.parse_keyword(Keyword::AS) {
11321- Some(self.parse_identifier()?)
11322- } else {
11323- // Check if the next token is an identifier (implicit alias)
11324- let checkpoint = self.index;
11325- match self.parse_identifier() {
11326- Ok(ident) => Some(ident),
11327- Err(_) => {
11328- self.index = checkpoint; // Rewind on failure
11329- None
11330- }
11331- }
11332- };
11313+ let alias = self.parse_optional_pipe_alias()?;
1133311314
1133411315 pipe_operators.push(PipeOperator::Unpivot {
1133511316 value_column,
0 commit comments