Skip to content

Commit f7817bc

Browse files
authored
Support DISTINCT for SetOperator (#689)
* Update SetOperation field all to op_option * Implement parse_set_operator_option cargo fmt Fix parse_set_operator_option after next_token * Add test for parsing union distinct * Rename to SetQualifier and fix fmt space * Add None to SetQualifier * Update parse method * Rename to SetQuantifier * Rename parse_set_operator_option parse_set_operator * Fix test to parse union, except, intersect * Add some comments to SetQuantifier * Fix comment
1 parent 0f7e144 commit f7817bc

File tree

5 files changed

+65
-9
lines changed

5 files changed

+65
-9
lines changed

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ pub use self::ddl::{
3232
pub use self::operator::{BinaryOperator, UnaryOperator};
3333
pub use self::query::{
3434
Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows,
35-
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, TableAlias,
36-
TableFactor, TableWithJoins, Top, Values, With,
35+
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier,
36+
TableAlias, TableFactor, TableWithJoins, Top, Values, With,
3737
};
3838
pub use self::value::{DateTimeField, TrimWhereField, Value};
3939

src/ast/query.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub enum SetExpr {
7878
/// UNION/EXCEPT/INTERSECT of two queries
7979
SetOperation {
8080
op: SetOperator,
81-
all: bool,
81+
set_quantifier: SetQuantifier,
8282
left: Box<SetExpr>,
8383
right: Box<SetExpr>,
8484
},
@@ -98,10 +98,17 @@ impl fmt::Display for SetExpr {
9898
left,
9999
right,
100100
op,
101-
all,
101+
set_quantifier,
102102
} => {
103-
let all_str = if *all { " ALL" } else { "" };
104-
write!(f, "{} {}{} {}", left, op, all_str, right)
103+
write!(f, "{} {}", left, op)?;
104+
match set_quantifier {
105+
SetQuantifier::All | SetQuantifier::Distinct => {
106+
write!(f, " {}", set_quantifier)?
107+
}
108+
SetQuantifier::None => write!(f, "{}", set_quantifier)?,
109+
}
110+
write!(f, " {}", right)?;
111+
Ok(())
105112
}
106113
}
107114
}
@@ -125,6 +132,26 @@ impl fmt::Display for SetOperator {
125132
}
126133
}
127134

135+
/// A quantifier for [SetOperator].
136+
// TODO: Restrict parsing specific SetQuantifier in some specific dialects.
137+
// For example, BigQuery does not support `DISTINCT` for `EXCEPT` and `INTERSECT`
138+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
139+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140+
pub enum SetQuantifier {
141+
All,
142+
Distinct,
143+
None,
144+
}
145+
146+
impl fmt::Display for SetQuantifier {
147+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148+
match self {
149+
SetQuantifier::All => write!(f, "ALL"),
150+
SetQuantifier::Distinct => write!(f, "DISTINCT"),
151+
SetQuantifier::None => write!(f, ""),
152+
}
153+
}
154+
}
128155
/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
129156
/// appear either as the only body item of a `Query`, or as an operand
130157
/// to a set operation like `UNION`.

src/parser.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4199,10 +4199,11 @@ impl<'a> Parser<'a> {
41994199
break;
42004200
}
42014201
self.next_token(); // skip past the set operator
4202+
let set_quantifier = self.parse_set_quantifier(&op);
42024203
expr = SetExpr::SetOperation {
42034204
left: Box::new(expr),
42044205
op: op.unwrap(),
4205-
all: self.parse_keyword(Keyword::ALL),
4206+
set_quantifier,
42064207
right: Box::new(self.parse_query_body(next_precedence)?),
42074208
};
42084209
}
@@ -4219,6 +4220,30 @@ impl<'a> Parser<'a> {
42194220
}
42204221
}
42214222

4223+
pub fn parse_set_quantifier(&mut self, op: &Option<SetOperator>) -> SetQuantifier {
4224+
match op {
4225+
Some(SetOperator::Union) => {
4226+
if self.parse_keyword(Keyword::ALL) {
4227+
SetQuantifier::All
4228+
} else if self.parse_keyword(Keyword::DISTINCT) {
4229+
SetQuantifier::Distinct
4230+
} else {
4231+
SetQuantifier::None
4232+
}
4233+
}
4234+
Some(SetOperator::Except) | Some(SetOperator::Intersect) => {
4235+
if self.parse_keyword(Keyword::ALL) {
4236+
SetQuantifier::All
4237+
} else if self.parse_keyword(Keyword::DISTINCT) {
4238+
SetQuantifier::Distinct
4239+
} else {
4240+
SetQuantifier::None
4241+
}
4242+
}
4243+
_ => SetQuantifier::None,
4244+
}
4245+
}
4246+
42224247
/// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`),
42234248
/// assuming the initial `SELECT` was already consumed
42244249
pub fn parse_select(&mut self) -> Result<Select, ParserError> {

tests/sqlparser_common.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4072,14 +4072,17 @@ fn parse_derived_tables() {
40724072
}
40734073

40744074
#[test]
4075-
fn parse_union() {
4075+
fn parse_union_except_intersect() {
40764076
// TODO: add assertions
40774077
verified_stmt("SELECT 1 UNION SELECT 2");
40784078
verified_stmt("SELECT 1 UNION ALL SELECT 2");
4079+
verified_stmt("SELECT 1 UNION DISTINCT SELECT 1");
40794080
verified_stmt("SELECT 1 EXCEPT SELECT 2");
40804081
verified_stmt("SELECT 1 EXCEPT ALL SELECT 2");
4082+
verified_stmt("SELECT 1 EXCEPT DISTINCT SELECT 1");
40814083
verified_stmt("SELECT 1 INTERSECT SELECT 2");
40824084
verified_stmt("SELECT 1 INTERSECT ALL SELECT 2");
4085+
verified_stmt("SELECT 1 INTERSECT DISTINCT SELECT 1");
40834086
verified_stmt("SELECT 1 UNION SELECT 2 UNION SELECT 3");
40844087
verified_stmt("SELECT 1 EXCEPT SELECT 2 UNION SELECT 3"); // Union[Except[1,2], 3]
40854088
verified_stmt("SELECT 1 INTERSECT (SELECT 2 EXCEPT SELECT 3)");
@@ -4088,6 +4091,7 @@ fn parse_union() {
40884091
verified_stmt("SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"); // Union[1, Intersect[2,3]]
40894092
verified_stmt("SELECT foo FROM tab UNION SELECT bar FROM TAB");
40904093
verified_stmt("(SELECT * FROM new EXCEPT SELECT * FROM old) UNION ALL (SELECT * FROM old EXCEPT SELECT * FROM new) ORDER BY 1");
4094+
verified_stmt("(SELECT * FROM new EXCEPT DISTINCT SELECT * FROM old) UNION DISTINCT (SELECT * FROM old EXCEPT DISTINCT SELECT * FROM new) ORDER BY 1");
40914095
}
40924096

40934097
#[test]

tests/sqlparser_postgres.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ fn parse_array_subquery_expr() {
13531353
with: None,
13541354
body: Box::new(SetExpr::SetOperation {
13551355
op: SetOperator::Union,
1356-
all: false,
1356+
set_quantifier: SetQuantifier::None,
13571357
left: Box::new(SetExpr::Select(Box::new(Select {
13581358
distinct: false,
13591359
top: None,

0 commit comments

Comments
 (0)