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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]