This is an automated email from the ASF dual-hosted git repository.

lingmiao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new ed95352  support intersect and except syntax (#2882)
ed95352 is described below

commit ed95352ecdb8363063d9ba1cd6efe05ab99d90e8
Author: yangzhg <780531...@qq.com>
AuthorDate: Thu Feb 13 16:48:46 2020 +0800

    support intersect and except syntax (#2882)
---
 fe/src/main/cup/sql_parser.cup                     | 101 +++++-----
 .../java/org/apache/doris/analysis/Analyzer.java   |   2 +-
 .../{UnionStmt.java => SetOperationStmt.java}      | 204 +++++++++++----------
 .../org/apache/doris/analysis/StmtRewriter.java    |   8 +-
 .../apache/doris/planner/SingleNodePlanner.java    |  56 +++---
 fe/src/main/jflex/sql_scanner.flex                 |   3 +
 .../doris/analysis/SetOperationStmtTest.java       |  64 +++++++
 7 files changed, 269 insertions(+), 169 deletions(-)

diff --git a/fe/src/main/cup/sql_parser.cup b/fe/src/main/cup/sql_parser.cup
index cb050c2..bfc5bf3 100644
--- a/fe/src/main/cup/sql_parser.cup
+++ b/fe/src/main/cup/sql_parser.cup
@@ -23,8 +23,9 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.doris.analysis.UnionStmt.Qualifier;
-import org.apache.doris.analysis.UnionStmt.UnionOperand;
+import org.apache.doris.analysis.SetOperationStmt.Qualifier;
+import org.apache.doris.analysis.SetOperationStmt.Operation;
+import org.apache.doris.analysis.SetOperationStmt.SetOperand;
 import org.apache.doris.catalog.AccessPrivilege;
 import org.apache.doris.catalog.AggregateType;
 import org.apache.doris.catalog.Column;
@@ -200,17 +201,18 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, 
KW_ALL, KW_ALTER, KW_A
     KW_CONFIG, KW_CONNECTION, KW_CONNECTION_ID, KW_CONSISTENT, KW_COUNT, 
KW_CREATE, KW_CROSS, KW_CUBE, KW_CURRENT, KW_CURRENT_USER,
     KW_DATA, KW_DATABASE, KW_DATABASES, KW_DATE, KW_DATETIME, KW_DAY, 
KW_DECIMAL, KW_DECOMMISSION, KW_DEFAULT, KW_DESC, KW_DESCRIBE,
     KW_DELETE, KW_DISTINCT, KW_DISTINCTPC, KW_DISTINCTPCSA, KW_DISTRIBUTED, 
KW_DISTRIBUTION, KW_DYNAMIC, KW_BUCKETS, KW_DIV, KW_DOUBLE, KW_DROP, KW_DROPP, 
KW_DUPLICATE,
-    KW_ELSE, KW_END, KW_ENGINE, KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, 
KW_EXISTS, KW_EXPORT, KW_EXTERNAL, KW_EXTRACT,
+    KW_ELSE, KW_END, KW_ENGINE, KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, 
KW_EXCEPT, KW_EXISTS, KW_EXPORT,
+    KW_EXTERNAL, KW_EXTRACT,
     KW_FALSE, KW_FOLLOWER, KW_FOLLOWING, KW_FREE, KW_FROM, KW_FILE, KW_FIRST, 
KW_FLOAT, KW_FOR, KW_FORMAT, KW_FRONTEND, KW_FRONTENDS, KW_FULL, KW_FUNCTION, 
KW_FUNCTIONS,
     KW_GLOBAL, KW_GRANT, KW_GRANTS, KW_GROUP, KW_GROUPING,
     KW_HASH, KW_HAVING, KW_HELP,KW_HLL, KW_HLL_UNION, KW_HOUR, KW_HUB,
     KW_IDENTIFIED, KW_IF, KW_IN, KW_INDEX, KW_INDEXES, KW_INFILE,
-    KW_INNER, KW_INSERT, KW_INT, KW_INTERMEDIATE, KW_INTERVAL, KW_INTO, KW_IS, 
KW_ISNULL,  KW_ISOLATION,
+    KW_INNER, KW_INSERT, KW_INT, KW_INTERMEDIATE, KW_INTERSECT, KW_INTERVAL, 
KW_INTO, KW_IS, KW_ISNULL, KW_ISOLATION,
     KW_JOIN,
     KW_KEY, KW_KILL,
     KW_LABEL, KW_LARGEINT, KW_LAST, KW_LEFT, KW_LESS, KW_LEVEL, KW_LIKE, 
KW_LIMIT, KW_LINK, KW_LOAD,
     KW_LOCAL, KW_LOCATION,
-    KW_MAX, KW_MAX_VALUE, KW_MERGE, KW_MIN, KW_MINUTE, KW_MIGRATE, 
KW_MIGRATIONS, KW_MODIFY, KW_MONTH,
+    KW_MAX, KW_MAX_VALUE, KW_MERGE, KW_MIN, KW_MINUTE, KW_MINUS, KW_MIGRATE, 
KW_MIGRATIONS, KW_MODIFY, KW_MONTH,
     KW_NAME, KW_NAMES, KW_NEGATIVE, KW_NO, KW_NOT, KW_NULL, KW_NULLS,
     KW_OBSERVER, KW_OFFSET, KW_ON, KW_ONLY, KW_OPEN, KW_OR, KW_ORDER, 
KW_OUTER, KW_OVER,
     KW_PARTITION, KW_PARTITIONS, KW_PASSWORD, KW_PATH, KW_PAUSE, KW_PIPE, 
KW_PRECEDING,
@@ -276,14 +278,14 @@ nonterminal String quantity;
 // Description of user
 nonterminal UserDesc grant_user;
 
-// Select or union statement.
+// Select or set operation(union/intersect/except) statement.
 nonterminal QueryStmt query_stmt;
 // Single select_stmt or parenthesized query_stmt.
-nonterminal QueryStmt union_operand;
-// List of select or union blocks connected by UNION operators or a single 
select block.
-nonterminal List<UnionOperand> union_operand_list;
-// List of select blocks connected by UNION operators, with order by or limit.
-nonterminal QueryStmt union_with_order_by_or_limit;
+nonterminal QueryStmt set_operand;
+// List of select or set operation(union/intersect/except) blocks connected by 
 set operators or a single select block.
+nonterminal List<SetOperand> set_operand_list;
+// List of select blocks connected by set operators, with order by or limit.
+nonterminal QueryStmt set_operation_with_order_by_or_limit;
 nonterminal InsertStmt insert_stmt;
 nonterminal InsertTarget insert_target;
 nonterminal InsertSource insert_source;
@@ -342,7 +344,8 @@ nonterminal JoinOperator join_operator;
 nonterminal ArrayList<String> opt_plan_hints;
 nonterminal ArrayList<String> opt_sort_hints;
 nonterminal Expr sign_chain_expr;
-nonterminal Qualifier union_op;
+nonterminal Qualifier opt_set_qualifier;
+nonterminal Operation set_op;
 nonterminal ArrayList<String> opt_common_hints;
 
 nonterminal ArrayList<PartitionName> opt_partition_name_list, 
partition_name_list;
@@ -2406,21 +2409,21 @@ delete_stmt ::=
 // even if the union has order by and limit.
 // ORDER BY and LIMIT bind to the preceding select statement by default.
 query_stmt ::=
-    opt_with_clause:w union_operand_list:operands
+    opt_with_clause:w set_operand_list:operands
     {:
         QueryStmt queryStmt = null;
         if (operands.size() == 1) {
           queryStmt = operands.get(0).getQueryStmt();
         } else {
-          queryStmt = new UnionStmt(operands, null, LimitElement.NO_LIMIT);
+          queryStmt = new SetOperationStmt(operands, null, 
LimitElement.NO_LIMIT);
         }
         queryStmt.setWithClause(w);
         RESULT = queryStmt;
     :}
-    | opt_with_clause:w union_with_order_by_or_limit:union
+    | opt_with_clause:w set_operation_with_order_by_or_limit:set_operation
     {:
-        union.setWithClause(w);
-        RESULT = union;
+        set_operation.setWithClause(w);
+        RESULT = set_operation;
     :}
     ;
 
@@ -2465,80 +2468,80 @@ with_view_def_list ::=
 // making this issue unresolvable.
 // We rely on the left precedence of KW_ORDER, KW_BY, and KW_LIMIT,
 // to resolve the ambiguity with select_stmt in favor of select_stmt
-// (i.e., ORDER BY and LIMIT bind to the select_stmt by default, and not the 
union).
-// There must be at least two union operands for ORDER BY or LIMIT to bind to 
a union,
+// (i.e., ORDER BY and LIMIT bind to the select_stmt by default, and not the 
set operation).
+// There must be at least two set operands for ORDER BY or LIMIT to bind to a 
set operation,
 // and we manually throw a parse error if we reach this production
 // with only a single operand.
-union_with_order_by_or_limit ::=
-    union_operand_list:operands
+set_operation_with_order_by_or_limit ::=
+    set_operand_list:operands
     KW_LIMIT INTEGER_LITERAL:limit
   {:
     if (operands.size() == 1) {
       parser.parseError("limit", SqlParserSymbols.KW_LIMIT);
     }
-    RESULT = new UnionStmt(operands, null, new 
LimitElement(limit.longValue()));
+    RESULT = new SetOperationStmt(operands, null, new 
LimitElement(limit.longValue()));
   :}
   |
-    union_operand_list:operands
+    set_operand_list:operands
     KW_LIMIT INTEGER_LITERAL:offset COMMA INTEGER_LITERAL:limit
   {:
     if (operands.size() == 1) {
       parser.parseError("limit", SqlParserSymbols.KW_LIMIT);
     }
-    RESULT = new UnionStmt(operands, null, new 
LimitElement(offset.longValue(), limit.longValue()));
+    RESULT = new SetOperationStmt(operands, null, new 
LimitElement(offset.longValue(), limit.longValue()));
   :}
   |
-    union_operand_list:operands
+    set_operand_list:operands
     KW_LIMIT INTEGER_LITERAL:limit KW_OFFSET INTEGER_LITERAL:offset
   {:
     if (operands.size() == 1) {
       parser.parseError("limit", SqlParserSymbols.KW_LIMIT);
     }
-    RESULT = new UnionStmt(operands, null, new 
LimitElement(offset.longValue(), limit.longValue()));
+    RESULT = new SetOperationStmt(operands, null, new 
LimitElement(offset.longValue(), limit.longValue()));
   :}
   |
-    union_operand_list:operands
+    set_operand_list:operands
     KW_ORDER KW_BY order_by_elements:orderByClause
   {:
     if (operands.size() == 1) {
       parser.parseError("order", SqlParserSymbols.KW_ORDER);
     }
-    RESULT = new UnionStmt(operands, orderByClause, LimitElement.NO_LIMIT);
+    RESULT = new SetOperationStmt(operands, orderByClause, 
LimitElement.NO_LIMIT);
   :}
   |
-    union_operand_list:operands
+    set_operand_list:operands
     KW_ORDER KW_BY order_by_elements:orderByClause
     KW_LIMIT INTEGER_LITERAL:limit
   {:
     if (operands.size() == 1) {
       parser.parseError("order", SqlParserSymbols.KW_ORDER);
     }
-    RESULT = new UnionStmt(operands, orderByClause, new 
LimitElement(limit.longValue()));
+    RESULT = new SetOperationStmt(operands, orderByClause, new 
LimitElement(limit.longValue()));
   :}
   |
-    union_operand_list:operands
+    set_operand_list:operands
     KW_ORDER KW_BY order_by_elements:orderByClause
     KW_LIMIT INTEGER_LITERAL:offset COMMA INTEGER_LITERAL:limit
   {:
     if (operands.size() == 1) {
       parser.parseError("order", SqlParserSymbols.KW_ORDER);
     }
-    RESULT = new UnionStmt(operands, orderByClause, new 
LimitElement(offset.longValue(), limit.longValue()));
+    RESULT = new SetOperationStmt(operands, orderByClause, new 
LimitElement(offset.longValue(), limit.longValue()));
   :}
   |
-    union_operand_list:operands
+    set_operand_list:operands
     KW_ORDER KW_BY order_by_elements:orderByClause
     KW_LIMIT INTEGER_LITERAL:limit KW_OFFSET INTEGER_LITERAL:offset
   {:
     if (operands.size() == 1) {
       parser.parseError("order", SqlParserSymbols.KW_ORDER);
     }
-    RESULT = new UnionStmt(operands, orderByClause, new 
LimitElement(offset.longValue(), limit.longValue()));
+    RESULT = new SetOperationStmt(operands, orderByClause, new 
LimitElement(offset.longValue(), limit.longValue()));
   :}
   ;
 
 
-union_operand ::=
+set_operand ::=
   select_stmt:select
   {:
     RESULT = select;
@@ -2549,26 +2552,36 @@ union_operand ::=
   :}
   ;
 
-union_operand_list ::=
-  union_operand:operand
+set_operand_list ::=
+  set_operand:operand
   {:
-    List<UnionOperand> operands = new ArrayList<UnionOperand>();
-    operands.add(new UnionOperand(operand, null));
+    List<SetOperand> operands = new ArrayList<SetOperand>();
+    operands.add(new SetOperand(operand, null, null));
     RESULT = operands;
   :}
-  | union_operand_list:operands union_op:op union_operand:operand
+  | set_operand_list:operands set_op:op opt_set_qualifier:qualifier 
set_operand:operand
   {:
-    operands.add(new UnionOperand(operand, op));
+    operands.add(new SetOperand(operand, op, qualifier));
     RESULT = operands;
   :}
   ;
 
-union_op ::=
+set_op ::=
   KW_UNION
+  {: RESULT = Operation.UNION; :}
+  | KW_INTERSECT
+  {: RESULT = Operation.INTERSECT; :}
+  | KW_EXCEPT
+  {: RESULT = Operation.EXCEPT; :}
+  | KW_MINUS
+  {: RESULT = Operation.EXCEPT; :}
+  ;
+
+opt_set_qualifier ::=
   {: RESULT = Qualifier.DISTINCT; :}
-  | KW_UNION KW_DISTINCT
+  | KW_DISTINCT
   {: RESULT = Qualifier.DISTINCT; :}
-  | KW_UNION KW_ALL
+  | KW_ALL
   {: RESULT = Qualifier.ALL; :}
   ;
 
diff --git a/fe/src/main/java/org/apache/doris/analysis/Analyzer.java 
b/fe/src/main/java/org/apache/doris/analysis/Analyzer.java
index 728f939..04e0dd7 100644
--- a/fe/src/main/java/org/apache/doris/analysis/Analyzer.java
+++ b/fe/src/main/java/org/apache/doris/analysis/Analyzer.java
@@ -1425,7 +1425,7 @@ public class Analyzer {
      * the i-th expr among all expr lists is compatible.
      * Throw an AnalysisException if the types are incompatible.
      */
-    public void castToUnionCompatibleTypes(List<List<Expr>> exprLists)
+    public void castToSetOpsCompatibleTypes(List<List<Expr>> exprLists)
             throws AnalysisException {
         if (exprLists == null || exprLists.size() < 2) return;
 
diff --git a/fe/src/main/java/org/apache/doris/analysis/UnionStmt.java 
b/fe/src/main/java/org/apache/doris/analysis/SetOperationStmt.java
similarity index 78%
rename from fe/src/main/java/org/apache/doris/analysis/UnionStmt.java
rename to fe/src/main/java/org/apache/doris/analysis/SetOperationStmt.java
index f123db3..597d783 100644
--- a/fe/src/main/java/org/apache/doris/analysis/UnionStmt.java
+++ b/fe/src/main/java/org/apache/doris/analysis/SetOperationStmt.java
@@ -33,21 +33,27 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * Representation of a union with its list of operands, and optional order by 
and limit.
- * A union materializes its results, and its resultExprs are SlotRefs into a 
new
+ * Representation of a set ops with its list of operands, and optional order 
by and limit.
+ * A set ops materializes its results, and its resultExprs are SlotRefs into a 
new
  * materialized tuple.
  * During analysis, the operands are normalized (separated into a single 
sequence of
  * DISTINCT followed by a single sequence of ALL operands) and unnested to the 
extent
  * possible. This also creates the AggregationInfo for DISTINCT operands.
  *
  * Use of resultExprs vs. baseTblResultExprs:
- * We consistently use/cast the resultExprs of union operands because the 
final expr
+ * We consistently use/cast the resultExprs of set operands because the final 
expr
  * substitution happens during planning. The only place where 
baseTblResultExprs are
  * used is in materializeRequiredSlots() because that is called before plan 
generation
  * and we need to mark the slots of resolved exprs as materialized.
  */
-public class UnionStmt extends QueryStmt {
-    private final static Logger LOG = LogManager.getLogger(UnionStmt.class);
+public class SetOperationStmt extends QueryStmt {
+    private final static Logger LOG = 
LogManager.getLogger(SetOperationStmt.class);
+
+    public enum Operation {
+        UNION,
+        INTERSECT,
+        EXCEPT
+    }
 
     public enum Qualifier {
         ALL,
@@ -57,25 +63,25 @@ public class UnionStmt extends QueryStmt {
     /////////////////////////////////////////
     // BEGIN: Members that need to be reset()
 
-    // before analysis, this contains the list of union operands derived 
verbatim
+    // before analysis, this contains the list of set operands derived verbatim
     // from the query;
     // after analysis, this contains all of distinctOperands followed by 
allOperands
-    private final List<UnionOperand> operands;
+    private final List<SetOperand> operands;
 
     // filled during analyze(); contains all operands that need to go through
     // distinct aggregation
-    protected final List<UnionOperand> distinctOperands_ = 
Lists.newArrayList();
+    protected final List<SetOperand> distinctOperands_ = Lists.newArrayList();
 
     // filled during analyze(); contains all operands that can be aggregated 
with
     // a simple merge without duplicate elimination (also needs to merge the 
output
     // of the DISTINCT operands)
-    protected final List<UnionOperand> allOperands_ = Lists.newArrayList();
+    protected final List<SetOperand> allOperands_ = Lists.newArrayList();
 
     private AggregateInfo distinctAggInfo;  // only set if we have DISTINCT ops
 
     private boolean hasDistinct = false;
 
-    // Single tuple materialized by the union. Set in analyze().
+    // Single tuple materialized by the set operation. Set in analyze().
     private TupleId tupleId;
 
     // set prior to unnesting
@@ -84,15 +90,15 @@ public class UnionStmt extends QueryStmt {
     // true if any of the operands_ references an AnalyticExpr
     private boolean hasAnalyticExprs_ = false;
 
-    // List of output expressions produced by the union without the ORDER BY 
portion
+    // List of output expressions produced by the set operation without the 
ORDER BY portion
     // (if any). Same as resultExprs_ if there is no ORDER BY.
-    private List<Expr> unionResultExprs_ = Lists.newArrayList();
-    
+    private List<Expr> setOpsResultExprs_ = Lists.newArrayList();
+
     // END: Members that need to be reset()
     /////////////////////////////////////////
 
-    public UnionStmt(
-            List<UnionOperand> operands,
+    public SetOperationStmt(
+            List<SetOperand> operands,
             ArrayList<OrderByElement> orderByElements,
             LimitElement limitElement) {
         super(orderByElements, limitElement);
@@ -102,17 +108,17 @@ public class UnionStmt extends QueryStmt {
     /**
      * C'tor for cloning.
      */
-    protected UnionStmt(UnionStmt other) {
+    protected SetOperationStmt(SetOperationStmt other) {
         super(other.cloneOrderByElements(),
                 (other.limitElement == null) ? null : 
other.limitElement.clone());
         operands = Lists.newArrayList();
         if (analyzer != null) {
-            for (UnionOperand o: other.distinctOperands_) 
distinctOperands_.add(o.clone());
-            for (UnionOperand o: other.allOperands_) 
allOperands_.add(o.clone());
+            for (SetOperand o: other.distinctOperands_) 
distinctOperands_.add(o.clone());
+            for (SetOperand o: other.allOperands_) allOperands_.add(o.clone());
             operands.addAll(distinctOperands_);
             operands.addAll(allOperands_);
         } else {
-            for (UnionOperand operand: other.operands) 
operands.add(operand.clone());
+            for (SetOperand operand: other.operands) 
operands.add(operand.clone());
         }
         analyzer = other.analyzer;
         distinctAggInfo =
@@ -121,11 +127,11 @@ public class UnionStmt extends QueryStmt {
         toSqlString = (other.toSqlString != null) ? new 
String(other.toSqlString) : null;
         hasAnalyticExprs_ = other.hasAnalyticExprs_;
         withClause_ = (other.withClause_ != null) ? other.withClause_.clone() 
: null;
-        unionResultExprs_ = Expr.cloneList(other.unionResultExprs_);
+        setOpsResultExprs_ = Expr.cloneList(other.setOpsResultExprs_);
     }
 
     @Override
-    public UnionStmt clone() { return new UnionStmt(this); }
+    public SetOperationStmt clone() { return new SetOperationStmt(this); }
 
     /**
      * Undoes all changes made by analyze() except distinct propagation and 
unnesting.
@@ -137,20 +143,20 @@ public class UnionStmt extends QueryStmt {
     @Override
     public void reset() {
         super.reset();
-        for (UnionOperand op: operands) op.reset();
+        for (SetOperand op: operands) op.reset();
         distinctOperands_.clear();
         allOperands_.clear();
         distinctAggInfo = null;
         tupleId = null;
         toSqlString = null;
         hasAnalyticExprs_ = false;
-        unionResultExprs_.clear();
+        setOpsResultExprs_.clear();
     }
 
-    public List<UnionOperand> getOperands() { return operands; }
-    public List<UnionOperand> getDistinctOperands() { return 
distinctOperands_; }
+    public List<SetOperand> getOperands() { return operands; }
+    public List<SetOperand> getDistinctOperands() { return distinctOperands_; }
     public boolean hasDistinctOps() { return !distinctOperands_.isEmpty(); }
-    public List<UnionOperand> getAllOperands() { return allOperands_; }
+    public List<SetOperand> getAllOperands() { return allOperands_; }
     public boolean hasAllOps() { return !allOperands_.isEmpty(); }
     public AggregateInfo getDistinctAggInfo() { return distinctAggInfo; }
     public boolean hasAnalyticExprs() { return hasAnalyticExprs_; }
@@ -161,24 +167,19 @@ public class UnionStmt extends QueryStmt {
         allOperands_.clear();
     }
 
-
-    public List<UnionOperand> getUnionOperands() {
-        return operands;
-    }
-
-    public List<Expr> getUnionResultExprs() { return unionResultExprs_; }
+    public List<Expr> getSetOpsResultExprs() { return setOpsResultExprs_; }
 
     @Override
     public void getDbs(Analyzer analyzer, Map<String, Database> dbs) throws 
AnalysisException {
         getWithClauseDbs(analyzer, dbs);
-        for (UnionOperand op : operands) {
+        for (SetOperand op : operands) {
             op.getQueryStmt().getDbs(analyzer, dbs);
         }
     }
 
     /**
      * Propagates DISTINCT from left to right, and checks that all
-     * union operands are union compatible, adding implicit casts if necessary.
+     * set operands are set compatible, adding implicit casts if necessary.
      */
     @Override
     public void analyze(Analyzer analyzer) throws AnalysisException, 
UserException {
@@ -186,6 +187,11 @@ public class UnionStmt extends QueryStmt {
         super.analyze(analyzer);
         Preconditions.checkState(operands.size() > 0);
 
+        for (SetOperand op : operands) {
+            if (op.getOperation() != null && op.getOperation() != 
Operation.UNION) {
+                throw new AnalysisException("INTERSECT/EXCEPT is not 
implemented yet.");
+            }
+        }
         // Propagates DISTINCT from left to right,
         propagateDistinct();
 
@@ -204,7 +210,7 @@ public class UnionStmt extends QueryStmt {
 
         // Compute hasAnalyticExprs_
         hasAnalyticExprs_ = false;
-        for (UnionOperand op: operands) {
+        for (SetOperand op: operands) {
             if (op.hasAnalyticExprs()) {
                 hasAnalyticExprs_ = true;
                 break;
@@ -213,33 +219,33 @@ public class UnionStmt extends QueryStmt {
 
         // Collect all result expr lists and cast the exprs as necessary.
         List<List<Expr>> resultExprLists = Lists.newArrayList();
-        for (UnionOperand op: operands) {
+        for (SetOperand op: operands) {
             resultExprLists.add(op.getQueryStmt().getResultExprs());
         }
-        analyzer.castToUnionCompatibleTypes(resultExprLists);
+        analyzer.castToSetOpsCompatibleTypes(resultExprLists);
 
-        // Create tuple descriptor materialized by this UnionStmt, its 
resultExprs, and
+        // Create tuple descriptor materialized by this SetOperationStmt, its 
resultExprs, and
         // its sortInfo if necessary.
         createMetadata(analyzer);
         createSortInfo(analyzer);
 
         // Create unnested operands' smaps.
-        for (UnionOperand operand: operands) setOperandSmap(operand, analyzer);
+        for (SetOperand operand: operands) setOperandSmap(operand, analyzer);
 
         // Create distinctAggInfo, if necessary.
         if (!distinctOperands_.isEmpty()) {
-            // Aggregate produces exactly the same tuple as the original union 
stmt.
+            // Aggregate produces exactly the same tuple as the original setOp 
stmt.
             ArrayList<Expr> groupingExprs = Expr.cloneList(resultExprs);
             try {
                 distinctAggInfo = AggregateInfo.create(
                         groupingExprs, null, 
analyzer.getDescTbl().getTupleDesc(tupleId), analyzer);
             } catch (AnalysisException e) {
                 // Should never happen.
-                throw new IllegalStateException("Error creating agg info in 
UnionStmt.analyze()", e);
+                throw new IllegalStateException("Error creating agg info in 
SetOperationStmt.analyze()", e);
             }
         }
 
-        unionResultExprs_ = Expr.cloneList(resultExprs);
+        setOpsResultExprs_ = Expr.cloneList(resultExprs);
         if (evaluateOrderBy) createSortTupleInfo(analyzer);
         baseTblResultExprs = resultExprs;
     }
@@ -266,7 +272,7 @@ public class UnionStmt extends QueryStmt {
     }
 
     /**
-     * Fill distinct-/allOperands and performs possible unnesting of UnionStmt
+     * Fill distinct-/allOperands and performs possible unnesting of 
SetOperationStmt
      * operands in the process.
      */
     private void unnestOperands(Analyzer analyzer) throws AnalysisException {
@@ -279,7 +285,7 @@ public class UnionStmt extends QueryStmt {
         // find index of first ALL operand
         int firstUnionAllIdx = operands.size();
         for (int i = 1; i < operands.size(); ++i) {
-            UnionOperand operand = operands.get(i);
+            SetOperand operand = operands.get(i);
             if (operand.getQualifier() == Qualifier.ALL) {
                 firstUnionAllIdx = (i == 1 ? 0 : i);
                 break;
@@ -301,8 +307,8 @@ public class UnionStmt extends QueryStmt {
             unnestOperand(allOperands_, Qualifier.ALL, operands.get(i));
         }
 
-        for (UnionOperand op: distinctOperands_) 
op.setQualifier(Qualifier.DISTINCT);
-        for (UnionOperand op: allOperands_) op.setQualifier(Qualifier.ALL);
+        for (SetOperand op: distinctOperands_) 
op.setQualifier(Qualifier.DISTINCT);
+        for (SetOperand op: allOperands_) op.setQualifier(Qualifier.ALL);
 
         operands.clear();
         operands.addAll(distinctOperands_);
@@ -310,11 +316,11 @@ public class UnionStmt extends QueryStmt {
     }
 
     /**
-     * Add a single operand to the target list; if the operand itself is a 
UnionStmt, apply
+     * Add a single operand to the target list; if the operand itself is a 
SetOperationStmt, apply
      * unnesting to the extent possible (possibly modifying 'operand' in the 
process).
      */
     private void unnestOperand(
-            List<UnionOperand> target, Qualifier targetQualifier, UnionOperand 
operand) {
+            List<SetOperand> target, Qualifier targetQualifier, SetOperand 
operand) {
         Preconditions.checkState(operand.isAnalyzed());
         QueryStmt queryStmt = operand.getQueryStmt();
         if (queryStmt instanceof SelectStmt) {
@@ -322,30 +328,30 @@ public class UnionStmt extends QueryStmt {
             return;
         }
 
-        Preconditions.checkState(queryStmt instanceof UnionStmt);
-        UnionStmt unionStmt = (UnionStmt) queryStmt;
-        if (unionStmt.hasLimit() || unionStmt.hasOffset()) {
-            // we must preserve the nested Union
+        Preconditions.checkState(queryStmt instanceof SetOperationStmt);
+        SetOperationStmt setOperationStmt = (SetOperationStmt) queryStmt;
+        if (setOperationStmt.hasLimit() || setOperationStmt.hasOffset()) {
+            // we must preserve the nested SetOps
             target.add(operand);
-        } else if (targetQualifier == Qualifier.DISTINCT || 
!unionStmt.hasDistinctOps()) {
-            // there is no limit in the nested Union and we can absorb all of 
its
+        } else if (targetQualifier == Qualifier.DISTINCT || 
!setOperationStmt.hasDistinctOps()) {
+            // there is no limit in the nested SetOps and we can absorb all of 
its
             // operands as-is
-            target.addAll(unionStmt.getDistinctOperands());
-            target.addAll(unionStmt.getAllOperands());
+            target.addAll(setOperationStmt.getDistinctOperands());
+            target.addAll(setOperationStmt.getAllOperands());
         } else {
-            // the nested Union contains some Distinct ops and we're 
accumulating
+            // the nested SetOps contains some Distinct ops and we're 
accumulating
             // into our All ops; unnest only the All ops and leave the rest in 
place
-            target.addAll(unionStmt.getAllOperands());
-            unionStmt.removeAllOperands();
+            target.addAll(setOperationStmt.getAllOperands());
+            setOperationStmt.removeAllOperands();
             target.add(operand);
         }
     }
 
     /**
-     * Sets the smap for the given operand. It maps from the output slots this 
union's
+     * Sets the smap for the given operand. It maps from the output slots this 
SetOps's
      * tuple to the corresponding result exprs of the operand.
      */
-    private void setOperandSmap(UnionOperand operand, Analyzer analyzer) {
+    private void setOperandSmap(SetOperand operand, Analyzer analyzer) {
         TupleDescriptor tupleDesc = 
analyzer.getDescTbl().getTupleDesc(tupleId);
         // operands' smaps were already set in the operands' analyze()
         operand.getSmap().clear();
@@ -376,7 +382,7 @@ public class UnionStmt extends QueryStmt {
     private void propagateDistinct() {
         int firstDistinctPos = -1;
         for (int i = operands.size() - 1; i > 0; --i) {
-            UnionOperand operand = operands.get(i);
+            SetOperand operand = operands.get(i);
             if (firstDistinctPos != -1) {
                 // There is a DISTINCT somewhere to the right.
                 operand.setQualifier(Qualifier.DISTINCT);
@@ -387,18 +393,18 @@ public class UnionStmt extends QueryStmt {
     }
 
     /**
-     * Create a descriptor for the tuple materialized by the union.
+     * Create a descriptor for the tuple materialized by the setOps.
      * Set resultExprs to be slot refs into that tuple.
      * Also fills the substitution map, such that "order by" can properly 
resolve
-     * column references from the result of the union.
+     * column references from the result of the setOps.
      */
     private void createMetadata(Analyzer analyzer) throws AnalysisException {
-        // Create tuple descriptor for materialized tuple created by the union.
-        TupleDescriptor tupleDesc = 
analyzer.getDescTbl().createTupleDescriptor("union");
+        // Create tuple descriptor for materialized tuple created by the 
setOps.
+        TupleDescriptor tupleDesc = 
analyzer.getDescTbl().createTupleDescriptor("SetOps");
         tupleDesc.setIsMaterialized(true);
         tupleId = tupleDesc.getId();
         if (LOG.isTraceEnabled()) {
-            LOG.trace("UnionStmt.createMetadata: tupleId=" + 
tupleId.toString());
+            LOG.trace("SetOperationStmt.createMetadata: tupleId=" + 
tupleId.toString());
         }
 
         // One slot per expr in the select blocks. Use first select block as 
representative.
@@ -447,7 +453,7 @@ public class UnionStmt extends QueryStmt {
             // to operands' result exprs (if those happen to be slotrefs);
             // don't do that if the operand computes analytic exprs
             // (see Planner.createInlineViewPlan() for the reasoning)
-            for (UnionOperand op: operands) {
+            for (SetOperand op: operands) {
                 Expr resultExpr = op.getQueryStmt().getResultExprs().get(i);
                 slotDesc.addSourceExpr(resultExpr);
                 SlotRef slotRef = resultExpr.unwrapSlotRef(false);
@@ -458,7 +464,7 @@ public class UnionStmt extends QueryStmt {
                 if (slotRef == null) continue;
                 // analyzer.registerValueTransfer(outputSlotRef.getSlotId(), 
slotRef.getSlotId());
             }
-            // If all the child slots are not nullable, then the union output 
slot should not
+            // If all the child slots are not nullable, then the SetOps output 
slot should not
             // be nullable as well.
             slotDesc.setIsNullable(isNullable);
         }
@@ -486,7 +492,7 @@ public class UnionStmt extends QueryStmt {
         for (int i = 0; i < outputSlots.size(); ++i) {
             SlotDescriptor slotDesc = outputSlots.get(i);
             if (!slotDesc.isMaterialized()) continue;
-            for (UnionOperand op: operands) {
+            for (SetOperand op: operands) {
                 exprs.add(op.getQueryStmt().getBaseTblResultExprs().get(i));
             }
             if (distinctAggInfo != null) {
@@ -497,14 +503,14 @@ public class UnionStmt extends QueryStmt {
         }
         materializeSlots(analyzer, exprs);
 
-        for (UnionOperand op: operands) {
+        for (SetOperand op: operands) {
             op.getQueryStmt().materializeRequiredSlots(analyzer);
         }
     }
 
     @Override
     public void rewriteExprs(ExprRewriter rewriter) throws AnalysisException {
-        for (UnionOperand op: operands) 
op.getQueryStmt().rewriteExprs(rewriter);
+        for (SetOperand op: operands) op.getQueryStmt().rewriteExprs(rewriter);
         if (orderByElements != null) {
             for (OrderByElement orderByElem: orderByElements) {
                 orderByElem.setExpr(rewriter.rewrite(orderByElem.getExpr(), 
analyzer));
@@ -524,7 +530,7 @@ public class UnionStmt extends QueryStmt {
 
     @Override
     public void collectTableRefs(List<TableRef> tblRefs) {
-        for (UnionOperand op: operands) 
op.getQueryStmt().collectTableRefs(tblRefs);
+        for (SetOperand op: operands) 
op.getQueryStmt().collectTableRefs(tblRefs);
     }
 
     @Override
@@ -537,20 +543,22 @@ public class UnionStmt extends QueryStmt {
         strBuilder.append(operands.get(0).getQueryStmt().toSql());
         for (int i = 1; i < operands.size() - 1; ++i) {
             strBuilder.append(
-              " UNION " + ((operands.get(i).getQualifier() == Qualifier.ALL) ? 
"ALL " : ""));
-            if (operands.get(i).getQueryStmt() instanceof UnionStmt) {
+              " " + operands.get(i).getOperation().toString() + " "
+                      + ((operands.get(i).getQualifier() == Qualifier.ALL) ? 
"ALL " : ""));
+            if (operands.get(i).getQueryStmt() instanceof SetOperationStmt) {
                 strBuilder.append("(");
             }
             strBuilder.append(operands.get(i).getQueryStmt().toSql());
-            if (operands.get(i).getQueryStmt() instanceof UnionStmt) {
+            if (operands.get(i).getQueryStmt() instanceof SetOperationStmt) {
                 strBuilder.append(")");
             }
         }
-        // Determine whether we need parenthesis around the last union operand.
-        UnionOperand lastOperand = operands.get(operands.size() - 1);
+        // Determine whether we need parenthesis around the last Set operand.
+        SetOperand lastOperand = operands.get(operands.size() - 1);
         QueryStmt lastQueryStmt = lastOperand.getQueryStmt();
-        strBuilder.append(" UNION " + ((lastOperand.getQualifier() == 
Qualifier.ALL) ? "ALL " : ""));
-        if (lastQueryStmt instanceof UnionStmt || ((hasOrderByClause() || 
hasLimitClause()) &&
+        strBuilder.append(" " + lastOperand.getOperation().toString() + " "
+                + ((lastOperand.getQualifier() == Qualifier.ALL) ? "ALL " : 
""));
+        if (lastQueryStmt instanceof SetOperationStmt || ((hasOrderByClause() 
|| hasLimitClause()) &&
                 !lastQueryStmt.hasLimitClause() &&
                 !lastQueryStmt.hasOrderByClause())) {
             strBuilder.append("(");
@@ -584,7 +592,7 @@ public class UnionStmt extends QueryStmt {
     @Override
     public void setNeedToSql(boolean needToSql) {
         super.setNeedToSql(needToSql);
-        for (UnionOperand operand : operands) {
+        for (SetOperand operand : operands) {
             operand.getQueryStmt().setNeedToSql(needToSql);
         }
     }
@@ -601,14 +609,17 @@ public class UnionStmt extends QueryStmt {
     }
 
     /**
-     * Represents an operand to a union. It consists of a query statement and 
its left
+     * Represents an operand to a SetOperand. It consists of a query statement 
and its left
      * all/distinct qualifier (null for the first operand).
      */
-    public static class UnionOperand {
+    public static class SetOperand {
+        // Operand indicate this SetOperand is union/intersect/except
+        private Operation operation;
+
         // Effective qualifier. Should not be reset() to preserve changes made 
during
         // distinct propagation and unnesting that are needed after rewriting 
Subqueries.
         private Qualifier qualifier_;
-        
+
         // ///////////////////////////////////////
         // BEGIN: Members that need to be reset()
 
@@ -618,14 +629,15 @@ public class UnionStmt extends QueryStmt {
         // We must preserve the conjuncts registered in the analyzer for 
partition pruning.
         private Analyzer analyzer;
 
-        // Map from UnionStmt's result slots to our resultExprs. Used during 
plan generation.
+        // Map from SetOperationStmt's result slots to our resultExprs. Used 
during plan generation.
         private final ExprSubstitutionMap smap_;
 
         // END: Members that need to be reset()
         // ///////////////////////////////////////
-        
-        public UnionOperand(QueryStmt queryStmt, Qualifier qualifier) {
+
+        public SetOperand(QueryStmt queryStmt, Operation operation, Qualifier 
qualifier) {
             this.queryStmt = queryStmt;
+            this.operation = operation;
             qualifier_ = qualifier;
             smap_ = new ExprSubstitutionMap();
         }
@@ -639,8 +651,15 @@ public class UnionStmt extends QueryStmt {
         public boolean isAnalyzed() { return analyzer != null; }
         public QueryStmt getQueryStmt() { return queryStmt; }
         public Qualifier getQualifier() { return qualifier_; }
+        public Operation getOperation() {
+            return operation;
+        }
         // Used for propagating DISTINCT.
         public void setQualifier(Qualifier qualifier) { qualifier_ = 
qualifier; }
+
+        public void setOperation(Operation operation) {
+            this.operation =operation;
+        }
         public Analyzer getAnalyzer() { return analyzer; }
         public ExprSubstitutionMap getSmap() { return smap_; }
 
@@ -648,16 +667,17 @@ public class UnionStmt extends QueryStmt {
             if (queryStmt instanceof SelectStmt) {
                 return ((SelectStmt) queryStmt).hasAnalyticInfo();
             } else {
-                Preconditions.checkState(queryStmt instanceof UnionStmt);
-                return ((UnionStmt) queryStmt).hasAnalyticExprs();
+                Preconditions.checkState(queryStmt instanceof 
SetOperationStmt);
+                return ((SetOperationStmt) queryStmt).hasAnalyticExprs();
             }
         }
 
         /**
          * C'tor for cloning.
          */
-        private UnionOperand(UnionOperand other) {
+        private SetOperand(SetOperand other) {
             queryStmt = other.queryStmt.clone();
+            this.operation = other.operation;
             qualifier_ = other.qualifier_;
             analyzer = other.analyzer;
             smap_ = other.smap_.clone();
@@ -670,8 +690,8 @@ public class UnionStmt extends QueryStmt {
         }
 
         @Override
-        public UnionOperand clone() {
-            return new UnionOperand(this);
+        public SetOperand clone() {
+            return new SetOperand(this);
         }
     }
 }
diff --git a/fe/src/main/java/org/apache/doris/analysis/StmtRewriter.java 
b/fe/src/main/java/org/apache/doris/analysis/StmtRewriter.java
index b48fbbd..1a8c2c3 100644
--- a/fe/src/main/java/org/apache/doris/analysis/StmtRewriter.java
+++ b/fe/src/main/java/org/apache/doris/analysis/StmtRewriter.java
@@ -69,8 +69,8 @@ public class StmtRewriter {
         Preconditions.checkNotNull(stmt);
         if (stmt instanceof SelectStmt) {
             rewriteSelectStatement((SelectStmt) stmt, analyzer);
-        } else if (stmt instanceof UnionStmt) {
-            rewriteUnionStatement((UnionStmt) stmt, analyzer);
+        } else if (stmt instanceof SetOperationStmt) {
+            rewriteUnionStatement((SetOperationStmt) stmt, analyzer);
         } else {
             throw new AnalysisException("Subqueries not supported for "
                     + stmt.getClass().getSimpleName() + " statements");
@@ -105,9 +105,9 @@ public class StmtRewriter {
      * Rewrite all operands in a UNION. The conditions that apply to 
SelectStmt rewriting
      * also apply here.
      */
-    private static void rewriteUnionStatement(UnionStmt stmt, Analyzer 
analyzer)
+    private static void rewriteUnionStatement(SetOperationStmt stmt, Analyzer 
analyzer)
             throws AnalysisException {
-        for (UnionStmt.UnionOperand operand: stmt.getOperands()) {
+        for (SetOperationStmt.SetOperand operand: stmt.getOperands()) {
             Preconditions.checkState(operand.getQueryStmt() instanceof 
SelectStmt);
             StmtRewriter.rewriteSelectStatement(
                     (SelectStmt)operand.getQueryStmt(), operand.getAnalyzer());
diff --git a/fe/src/main/java/org/apache/doris/planner/SingleNodePlanner.java 
b/fe/src/main/java/org/apache/doris/planner/SingleNodePlanner.java
index 9b013e1..e90028c 100644
--- a/fe/src/main/java/org/apache/doris/planner/SingleNodePlanner.java
+++ b/fe/src/main/java/org/apache/doris/planner/SingleNodePlanner.java
@@ -39,6 +39,7 @@ import org.apache.doris.analysis.LiteralExpr;
 import org.apache.doris.analysis.NullLiteral;
 import org.apache.doris.analysis.QueryStmt;
 import org.apache.doris.analysis.SelectStmt;
+import org.apache.doris.analysis.SetOperationStmt;
 import org.apache.doris.analysis.SlotDescriptor;
 import org.apache.doris.analysis.SlotId;
 import org.apache.doris.analysis.SlotRef;
@@ -46,7 +47,6 @@ import org.apache.doris.analysis.TableRef;
 import org.apache.doris.analysis.TupleDescriptor;
 import org.apache.doris.analysis.TupleId;
 import org.apache.doris.analysis.TupleIsNullPredicate;
-import org.apache.doris.analysis.UnionStmt;
 import org.apache.doris.catalog.AggregateFunction;
 import org.apache.doris.catalog.AggregateType;
 import org.apache.doris.catalog.Column;
@@ -235,8 +235,8 @@ public class SingleNodePlanner {
                 }
             }
         } else {
-            Preconditions.checkState(stmt instanceof UnionStmt);
-            root = createUnionPlan((UnionStmt) stmt, analyzer, 
newDefaultOrderByLimit);
+            Preconditions.checkState(stmt instanceof SetOperationStmt);
+            root = createSetOperationPlan((SetOperationStmt) stmt, analyzer, 
newDefaultOrderByLimit);
         }
 
         // Avoid adding a sort node if the sort tuple has no materialized 
slots.
@@ -1175,8 +1175,8 @@ public class SingleNodePlanner {
                 viewAnalyzer.registerConjuncts(newConjuncts, 
select.getTableRefs().get(0).getDesc().getId().asList());
             }
         } else {
-            Preconditions.checkArgument(stmt instanceof UnionStmt);
-            final UnionStmt union = (UnionStmt) stmt;
+            Preconditions.checkArgument(stmt instanceof SetOperationStmt);
+            final SetOperationStmt union = (SetOperationStmt) stmt;
             viewAnalyzer.registerConjuncts(newConjuncts, 
union.getTupleId().asList());
         }
     }
@@ -1204,11 +1204,11 @@ public class SingleNodePlanner {
         // UnionNode will handle predicates and assigns predicates to it's 
children.
         final List<Expr> candicatePredicates =
                 Expr.substituteList(viewPredicates, inlineViewRef.getSmap(), 
analyzer, false);
-        if (inlineViewRef.getViewStmt() instanceof UnionStmt) {
-            final UnionStmt unionStmt = (UnionStmt) 
inlineViewRef.getViewStmt();
+        if (inlineViewRef.getViewStmt() instanceof SetOperationStmt) {
+            final SetOperationStmt setOperationStmt = (SetOperationStmt) 
inlineViewRef.getViewStmt();
             for (int i = 0; i < candicatePredicates.size(); i++) {
                 final Expr predicate = candicatePredicates.get(i);
-                if (predicate.isBound(unionStmt.getTupleId())) {
+                if (predicate.isBound(setOperationStmt.getTupleId())) {
                     pushDownPredicates.add(predicate);
                 } else {
                     pushDownFailedPredicates.add(viewPredicates.get(i));
@@ -1483,12 +1483,12 @@ public class SingleNodePlanner {
      * as a child of the returned UnionNode.
      */
     private UnionNode createUnionPlan(
-            Analyzer analyzer, UnionStmt unionStmt, 
List<UnionStmt.UnionOperand> unionOperands,
+            Analyzer analyzer, SetOperationStmt setOperationStmt, 
List<SetOperationStmt.SetOperand> setOperands,
             PlanNode unionDistinctPlan, long defaultOrderByLimit)
             throws UserException, AnalysisException {
-        UnionNode unionNode = new UnionNode(ctx_.getNextNodeId(), 
unionStmt.getTupleId(),
-                unionStmt.getUnionResultExprs(), false);
-        for (UnionStmt.UnionOperand op : unionOperands) {
+        UnionNode unionNode = new UnionNode(ctx_.getNextNodeId(), 
setOperationStmt.getTupleId(),
+                setOperationStmt.getSetOpsResultExprs(), false);
+        for (SetOperationStmt.SetOperand op : setOperands) {
             if (op.getAnalyzer().hasEmptyResultSet()) {
                 unmarkCollectionSlots(op.getQueryStmt());
                 continue;
@@ -1512,10 +1512,10 @@ public class SingleNodePlanner {
         }
 
         if (unionDistinctPlan != null) {
-            Preconditions.checkState(unionStmt.hasDistinctOps());
+            Preconditions.checkState(setOperationStmt.hasDistinctOps());
             Preconditions.checkState(unionDistinctPlan instanceof 
AggregationNode);
             unionNode.addChild(unionDistinctPlan,
-                    unionStmt.getDistinctAggInfo().getGroupingExprs());
+                    setOperationStmt.getDistinctAggInfo().getGroupingExprs());
         }
         unionNode.init(analyzer);
         return unionNode;
@@ -1537,23 +1537,23 @@ public class SingleNodePlanner {
      * TODO: Simplify the plan of unions with only a single non-empty operand 
to not
      * use a union node (this is tricky because a union materializes a new 
tuple).
      */
-    private PlanNode createUnionPlan(UnionStmt unionStmt, Analyzer analyzer, 
long defaultOrderByLimit)
+    private PlanNode createSetOperationPlan(SetOperationStmt setOperationStmt, 
Analyzer analyzer, long defaultOrderByLimit)
             throws UserException, AnalysisException {
         // TODO(zc): get unassigned conjuncts
         // List<Expr> conjuncts =
         //         
analyzer.getUnassignedConjuncts(unionStmt.getTupleId().asList(), false);
-        List<Expr> conjuncts = 
analyzer.getUnassignedConjuncts(unionStmt.getTupleId().asList());
+        List<Expr> conjuncts = 
analyzer.getUnassignedConjuncts(setOperationStmt.getTupleId().asList());
         // TODO chenhao
         // Because Conjuncts can't be assigned to UnionNode and Palo's fe 
can't evaluate conjuncts,
         // it needs to add SelectNode as UnionNode's parent, when UnionStmt's 
Ops contains constant 
         // Select.
         boolean hasConstantOp = false;
-        if (!unionStmt.hasAnalyticExprs()) {
+        if (!setOperationStmt.hasAnalyticExprs()) {
             // Turn unassigned predicates for unionStmt's tupleId_ into 
predicates for
             // the individual operands.
             // Do this prior to creating the operands' plan trees so they get 
a chance to
             // pick up propagated predicates.
-            for (UnionStmt.UnionOperand op : unionStmt.getOperands()) {
+            for (SetOperationStmt.SetOperand op : 
setOperationStmt.getOperands()) {
                 List<Expr> opConjuncts =
                         Expr.substituteList(conjuncts, op.getSmap(), analyzer, 
false);
                 boolean selectHasTableRef = true;
@@ -1570,8 +1570,8 @@ public class SingleNodePlanner {
                 if ((queryStmt instanceof SelectStmt) && selectHasTableRef) {
                     final SelectStmt select = (SelectStmt) queryStmt;
                     op.getAnalyzer().registerConjuncts(opConjuncts, 
select.getTableRefIds());
-                } else if (queryStmt instanceof UnionStmt) {
-                    final UnionStmt union = (UnionStmt) queryStmt;
+                } else if (queryStmt instanceof SetOperationStmt) {
+                    final SetOperationStmt union = (SetOperationStmt) 
queryStmt;
                     op.getAnalyzer().registerConjuncts(opConjuncts, 
union.getTupleId().asList());
                 } else {
                     if (selectHasTableRef) {
@@ -1587,24 +1587,24 @@ public class SingleNodePlanner {
             analyzer.materializeSlots(conjuncts);
         }
         // mark slots after predicate propagation but prior to plan tree 
generation
-        unionStmt.materializeRequiredSlots(analyzer);
+        setOperationStmt.materializeRequiredSlots(analyzer);
 
         PlanNode result = null;
         // create DISTINCT tree
-        if (unionStmt.hasDistinctOps()) {
+        if (setOperationStmt.hasDistinctOps()) {
             result = createUnionPlan(
-                    analyzer, unionStmt, unionStmt.getDistinctOperands(), 
null, defaultOrderByLimit);
-            result = new AggregationNode(ctx_.getNextNodeId(), result, 
unionStmt.getDistinctAggInfo());
+                    analyzer, setOperationStmt, 
setOperationStmt.getDistinctOperands(), null, defaultOrderByLimit);
+            result = new AggregationNode(ctx_.getNextNodeId(), result, 
setOperationStmt.getDistinctAggInfo());
             result.init(analyzer);
         }
         // create ALL tree
-        if (unionStmt.hasAllOps()) {
-            result = createUnionPlan(analyzer, unionStmt, 
unionStmt.getAllOperands(), result, defaultOrderByLimit);
+        if (setOperationStmt.hasAllOps()) {
+            result = createUnionPlan(analyzer, setOperationStmt, 
setOperationStmt.getAllOperands(), result, defaultOrderByLimit);
         }
 
-        if (unionStmt.hasAnalyticExprs() || hasConstantOp) {
+        if (setOperationStmt.hasAnalyticExprs() || hasConstantOp) {
             result = addUnassignedConjuncts(
-                    analyzer, unionStmt.getTupleId().asList(), result);
+                    analyzer, setOperationStmt.getTupleId().asList(), result);
         }
         return result;
     }
diff --git a/fe/src/main/jflex/sql_scanner.flex 
b/fe/src/main/jflex/sql_scanner.flex
index d5b4942..aba90b4 100644
--- a/fe/src/main/jflex/sql_scanner.flex
+++ b/fe/src/main/jflex/sql_scanner.flex
@@ -171,6 +171,7 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("enter", new Integer(SqlParserSymbols.KW_ENTER));
         keywordMap.put("errors", new Integer(SqlParserSymbols.KW_ERRORS));
         keywordMap.put("events", new Integer(SqlParserSymbols.KW_EVENTS));
+        keywordMap.put("except", new Integer(SqlParserSymbols.KW_EXCEPT));
         keywordMap.put("exists", new Integer(SqlParserSymbols.KW_EXISTS));
         keywordMap.put("explain", new Integer(SqlParserSymbols.KW_DESCRIBE));
         keywordMap.put("export", new Integer(SqlParserSymbols.KW_EXPORT));
@@ -214,6 +215,7 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("int", new Integer(SqlParserSymbols.KW_INT));
         keywordMap.put("integer", new Integer(SqlParserSymbols.KW_INT));
         keywordMap.put("intermediate", new 
Integer(SqlParserSymbols.KW_INTERMEDIATE));
+        keywordMap.put("intersect", new 
Integer(SqlParserSymbols.KW_INTERSECT));
         keywordMap.put("interval", new Integer(SqlParserSymbols.KW_INTERVAL));
         keywordMap.put("into", new Integer(SqlParserSymbols.KW_INTO));
         keywordMap.put("is", new Integer(SqlParserSymbols.KW_IS));
@@ -240,6 +242,7 @@ import org.apache.doris.qe.SqlModeHelper;
         keywordMap.put("migrate", new Integer(SqlParserSymbols.KW_MIGRATE));
         keywordMap.put("migrations", new 
Integer(SqlParserSymbols.KW_MIGRATIONS));
         keywordMap.put("min", new Integer(SqlParserSymbols.KW_MIN));
+        keywordMap.put("minus", new Integer(SqlParserSymbols.KW_MINUS));
         keywordMap.put("minute", new Integer(SqlParserSymbols.KW_MINUTE));
         keywordMap.put("modify", new Integer(SqlParserSymbols.KW_MODIFY));
         keywordMap.put("month", new Integer(SqlParserSymbols.KW_MONTH));
diff --git 
a/fe/src/test/java/org/apache/doris/analysis/SetOperationStmtTest.java 
b/fe/src/test/java/org/apache/doris/analysis/SetOperationStmtTest.java
new file mode 100644
index 0000000..ff5a2a0
--- /dev/null
+++ b/fe/src/test/java/org/apache/doris/analysis/SetOperationStmtTest.java
@@ -0,0 +1,64 @@
+package org.apache.doris.analysis;
+
+import java.io.StringReader;
+
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.mysql.privilege.MockedAuth;
+import org.apache.doris.mysql.privilege.PaloAuth;
+import org.apache.doris.qe.ConnectContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import mockit.Mocked;
+
+public class SetOperationStmtTest {
+    private Analyzer analyzer;
+
+    @Mocked
+    private PaloAuth auth;
+    @Mocked
+    private ConnectContext ctx;
+
+    @Before
+    public void setUp() {
+        analyzer = AccessTestUtil.fetchAdminAnalyzer(true);
+        MockedAuth.mockedAuth(auth);
+        MockedAuth.mockedConnectContext(ctx, "root", "192.168.1.1");
+    }
+    @Test
+    public void testNormal() throws Exception {
+        String sql = "select k1,k2 from t where k1='a' union select k1,k2 from 
t where k1='b';";
+        SqlScanner input = new SqlScanner(new StringReader(sql));
+        SqlParser parser = new SqlParser(input);
+        SetOperationStmt stmt = (SetOperationStmt) parser.parse().value;
+        Assert.assertEquals(SetOperationStmt.Operation.UNION, 
stmt.getOperands().get(1).getOperation());
+        sql = "select k1,k2 from t where k1='a' intersect select k1,k2 from t 
where k1='b';";
+        input = new SqlScanner(new StringReader(sql));
+        parser = new SqlParser(input);
+        stmt = (SetOperationStmt) parser.parse().value;
+        Assert.assertEquals(SetOperationStmt.Operation.INTERSECT, 
stmt.getOperands().get(1).getOperation());
+        sql = "select k1,k2 from t where k1='a' except select k1,k2 from t 
where k1='b';";
+        input = new SqlScanner(new StringReader(sql));
+        parser = new SqlParser(input);
+        stmt = (SetOperationStmt) parser.parse().value;
+        Assert.assertEquals(SetOperationStmt.Operation.EXCEPT, 
stmt.getOperands().get(1).getOperation());
+        sql = "select k1,k2 from t where k1='a' minus select k1,k2 from t 
where k1='b';";
+        input = new SqlScanner(new StringReader(sql));
+        parser = new SqlParser(input);
+        stmt = (SetOperationStmt) parser.parse().value;
+        Assert.assertEquals(SetOperationStmt.Operation.EXCEPT, 
stmt.getOperands().get(1).getOperation());
+        sql = "select k1,k2 from t where k1='a' union select k1,k2 from t 
where k1='b' intersect select k1,k2 from t "
+                + "where k1='c' except select k1,k2 from t where k1='d';";
+        input = new SqlScanner(new StringReader(sql));
+        parser = new SqlParser(input);
+        stmt = (SetOperationStmt) parser.parse().value;
+        Assert.assertEquals(SetOperationStmt.Operation.UNION, 
stmt.getOperands().get(1).getOperation());
+        Assert.assertEquals(SetOperationStmt.Operation.INTERSECT, 
stmt.getOperands().get(2).getOperation());
+        Assert.assertEquals(SetOperationStmt.Operation.EXCEPT, 
stmt.getOperands().get(3).getOperation());
+        Assert.assertEquals(4, stmt.getOperands().size());
+
+
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to