From 98df45a9aa98466bcf4a49453cc5a597357fd2dc Mon Sep 17 00:00:00 2001 From: Yuhao Su Date: Mon, 22 Jul 2024 12:38:47 -0500 Subject: [PATCH 01/11] parse --- src/frontend/src/binder/query.rs | 16 ++++++++++ src/frontend/src/binder/set_expr.rs | 1 + src/sqlparser/src/ast/mod.rs | 6 ++-- src/sqlparser/src/ast/query.rs | 48 ++++++++++++++++++++++++++++- src/sqlparser/src/parser.rs | 16 ++++++++++ 5 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/binder/query.rs b/src/frontend/src/binder/query.rs index 459e1b7921e9..7c0ce2e69a94 100644 --- a/src/frontend/src/binder/query.rs +++ b/src/frontend/src/binder/query.rs @@ -295,6 +295,7 @@ impl Binder { SetExpr::SetOperation { op: SetOperator::Union, all, + corresponding, left, right, }, @@ -307,6 +308,12 @@ impl Binder { .into()); }; + // validated in `validate_rcte` + assert!( + !corresponding.is_corresponding(), + "`CORRESPONDING` is not supported in recursive CTE" + ); + let entry = self .context .cte_to_relation @@ -396,6 +403,7 @@ impl Binder { let SetExpr::SetOperation { op: SetOperator::Union, all, + corresponding, left, right, } = body @@ -412,10 +420,18 @@ impl Binder { .into()); } + if corresponding.is_corresponding() { + return Err(ErrorCode::BindError( + "`CORRESPONDING` is not supported in recursive CTE".to_string(), + ) + .into()); + } + Ok(( SetExpr::SetOperation { op: SetOperator::Union, all, + corresponding, left, right, }, diff --git a/src/frontend/src/binder/set_expr.rs b/src/frontend/src/binder/set_expr.rs index be4943d59def..3bf77ef2fd88 100644 --- a/src/frontend/src/binder/set_expr.rs +++ b/src/frontend/src/binder/set_expr.rs @@ -202,6 +202,7 @@ impl Binder { SetExpr::SetOperation { op, all, + corresponding, left, right, } => { diff --git a/src/sqlparser/src/ast/mod.rs b/src/sqlparser/src/ast/mod.rs index 448f448628e4..a623bbfb0e0c 100644 --- a/src/sqlparser/src/ast/mod.rs +++ b/src/sqlparser/src/ast/mod.rs @@ -46,9 +46,9 @@ pub use self::legacy_source::{ }; pub use self::operator::{BinaryOperator, QualifiedOperator, UnaryOperator}; pub use self::query::{ - Cte, CteInner, Distinct, Fetch, Join, JoinConstraint, JoinOperator, LateralView, OrderByExpr, - Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, - Values, With, + Corresponding, Cte, CteInner, Distinct, Fetch, Join, JoinConstraint, JoinOperator, LateralView, + OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, + TableWithJoins, Top, Values, With, }; pub use self::statement::*; pub use self::value::{ diff --git a/src/sqlparser/src/ast/query.rs b/src/sqlparser/src/ast/query.rs index 83e84907a109..75bbac56e0dd 100644 --- a/src/sqlparser/src/ast/query.rs +++ b/src/sqlparser/src/ast/query.rs @@ -97,6 +97,7 @@ pub enum SetExpr { SetOperation { op: SetOperator, all: bool, + corresponding: Corresponding, left: Box, right: Box, }, @@ -114,9 +115,10 @@ impl fmt::Display for SetExpr { right, op, all, + corresponding, } => { let all_str = if *all { " ALL" } else { "" }; - write!(f, "{} {}{} {}", left, op, all_str, right) + write!(f, "{} {}{} {}{}", left, op, all_str, right, corresponding) } } } @@ -140,6 +142,50 @@ impl fmt::Display for SetOperator { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// CORRESPONDING [ BY ] +pub struct Corresponding { + pub corresponding: bool, + pub column_list: Option>, +} + +impl Corresponding { + pub fn with_column_list(column_list: Option>) -> Self { + Self { + corresponding: true, + column_list, + } + } + + pub fn none() -> Self { + Self { + corresponding: false, + column_list: None, + } + } + + pub fn is_corresponding(&self) -> bool { + self.corresponding + } + + pub fn column_list(&self) -> Option<&[Ident]> { + self.column_list.as_deref() + } +} + +impl fmt::Display for Corresponding { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.corresponding { + write!(f, " CORRESPONDING")?; + if let Some(column_list) = &self.column_list { + write!(f, " BY ({})", display_comma_separated(column_list))?; + } + } + Ok(()) + } +} + /// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may /// appear either as the only body item of an `SQLQuery`, or as an operand /// to a set operation like `UNION`. diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index cd073b6bc261..c6cc778b7403 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -4082,6 +4082,7 @@ impl Parser<'_> { // Unexpected token or EOF => stop parsing the query body None => break, }; + let corresponding = self.parse_corresponding()?; if precedence >= next_precedence { break; } @@ -4089,6 +4090,7 @@ impl Parser<'_> { expr = SetExpr::SetOperation { left: Box::new(expr), op: op.unwrap(), + corresponding, all: self.parse_keyword(Keyword::ALL), right: Box::new(self.parse_query_body(next_precedence)?), }; @@ -4106,6 +4108,20 @@ impl Parser<'_> { } } + fn parse_corresponding(&mut self) -> PResult { + let corresponding = if self.parse_keyword(Keyword::CORRESPONDING) { + let column_list = if self.parse_keyword(Keyword::BY) { + Some(self.parse_parenthesized_column_list(IsOptional::Mandatory)?) + } else { + None + }; + Corresponding::with_column_list(column_list) + } else { + Corresponding::none() + }; + Ok(corresponding) + } + /// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`), /// assuming the initial `SELECT` was already consumed pub fn parse_select(&mut self) -> PResult