You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by pa...@apache.org on 2022/10/09 12:33:06 UTC

[shardingsphere] branch master updated: Support select intersect, except statement in sql federation and refactor combine operator parse (#21425)

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

panjuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 9b9b6e0802d Support select intersect, except statement in sql federation and refactor combine operator parse (#21425)
9b9b6e0802d is described below

commit 9b9b6e0802d1d8f8c7fa95f1e0c4bf978bbcfae7
Author: Zhengqiang Duan <du...@apache.org>
AuthorDate: Sun Oct 9 20:32:56 2022 +0800

    Support select intersect, except statement in sql federation and refactor combine operator parse (#21425)
---
 .../decider/ShardingSQLFederationDecider.java      |  2 +-
 .../statement/dml/SelectStatementContext.java      |  6 +--
 .../converter/SQLNodeConverterEngine.java          | 25 ++++++++----
 .../converter/type/CombineOperatorConverter.java   |  6 ++-
 .../optimizer/util/SQLFederationPlannerUtil.java   | 13 +-----
 .../type/CombineOperatorConverterTest.java         |  7 +---
 .../statement/impl/MySQLStatementSQLVisitor.java   |  9 ++++-
 .../impl/OpenGaussStatementSQLVisitor.java         |  9 ++++-
 .../impl/PostgreSQLStatementSQLVisitor.java        |  9 ++++-
 .../sql/common/extractor/TableExtractor.java       |  4 +-
 .../sql/common/statement/dml/SelectStatement.java  | 13 ++++--
 .../sql/common/util/SubqueryExtractUtil.java       |  9 +----
 .../sql/common/util/SubqueryExtractUtilTest.java   |  2 +-
 .../SQLNodeConverterEngineParameterizedTest.java   |  8 +++-
 .../statement/dml/impl/SelectStatementAssert.java  | 29 ++++++--------
 .../statement/dml/SelectStatementTestCase.java     |  4 +-
 .../src/main/resources/case/ddl/create-view.xml    | 46 +++++++++++-----------
 .../src/main/resources/case/dml/select-union.xml   | 44 ++++++++++-----------
 18 files changed, 127 insertions(+), 118 deletions(-)

diff --git a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/decider/ShardingSQLFederationDecider.java b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/decider/ShardingSQLFederationDecider.java
index 4cb6662e000..4716aa9eb2f 100644
--- a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/decider/ShardingSQLFederationDecider.java
+++ b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/decider/ShardingSQLFederationDecider.java
@@ -50,7 +50,7 @@ public final class ShardingSQLFederationDecider implements SQLFederationDecider<
         }
         addTableDataNodes(deciderContext, rule, tableNames);
         ShardingConditions shardingConditions = createShardingConditions(queryContext, database, rule);
-        // TODO remove this judge logic when we support issue#21392 
+        // TODO remove this judge logic when we support issue#21392
         if (select.getPaginationContext().isHasPagination() && !(select.getDatabaseType() instanceof PostgreSQLDatabaseType) && !(select.getDatabaseType() instanceof OpenGaussDatabaseType)) {
             return;
         }
diff --git a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementContext.java b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementContext.java
index 6e373e5f6bb..ecc0d6de65e 100644
--- a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementContext.java
+++ b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/statement/dml/SelectStatementContext.java
@@ -20,6 +20,8 @@ package org.apache.shardingsphere.infra.binder.statement.dml;
 import com.google.common.base.Preconditions;
 import lombok.Getter;
 import lombok.Setter;
+import org.apache.shardingsphere.dialect.exception.syntax.database.NoDatabaseSelectedException;
+import org.apache.shardingsphere.dialect.exception.syntax.database.UnknownDatabaseException;
 import org.apache.shardingsphere.infra.binder.aware.ParameterAware;
 import org.apache.shardingsphere.infra.binder.segment.select.groupby.GroupByContext;
 import org.apache.shardingsphere.infra.binder.segment.select.groupby.engine.GroupByContextEngine;
@@ -39,8 +41,6 @@ import org.apache.shardingsphere.infra.binder.segment.table.TablesContext;
 import org.apache.shardingsphere.infra.binder.statement.CommonSQLStatementContext;
 import org.apache.shardingsphere.infra.binder.type.TableAvailable;
 import org.apache.shardingsphere.infra.binder.type.WhereAvailable;
-import org.apache.shardingsphere.dialect.exception.syntax.database.NoDatabaseSelectedException;
-import org.apache.shardingsphere.dialect.exception.syntax.database.UnknownDatabaseException;
 import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.metadata.database.schema.decorator.model.ShardingSphereSchema;
 import org.apache.shardingsphere.infra.util.exception.ShardingSpherePreconditions;
@@ -172,7 +172,7 @@ public final class SelectStatementContext extends CommonSQLStatementContext<Sele
      * @return whether contains combine or not
      */
     public boolean isContainsCombine() {
-        return !getSqlStatement().getCombines().isEmpty();
+        return getSqlStatement().getCombine().isPresent();
     }
     
     /**
diff --git a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/SQLNodeConverterEngine.java b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/SQLNodeConverterEngine.java
index b7363ef8d45..341269d7763 100644
--- a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/SQLNodeConverterEngine.java
+++ b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/SQLNodeConverterEngine.java
@@ -22,12 +22,12 @@ import lombok.NoArgsConstructor;
 import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.shardingsphere.sqlfederation.optimizer.converter.exception.OptimizationSQLNodeConvertException;
-import org.apache.shardingsphere.sqlfederation.optimizer.converter.statement.select.SelectStatementConverter;
-import org.apache.shardingsphere.sqlfederation.optimizer.converter.type.CombineOperatorConverter;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.combine.CombineSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
 import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
+import org.apache.shardingsphere.sqlfederation.optimizer.converter.exception.OptimizationSQLNodeConvertException;
+import org.apache.shardingsphere.sqlfederation.optimizer.converter.statement.select.SelectStatementConverter;
+import org.apache.shardingsphere.sqlfederation.optimizer.converter.type.CombineOperatorConverter;
 
 import java.util.Arrays;
 
@@ -45,13 +45,22 @@ public final class SQLNodeConverterEngine {
      */
     public static SqlNode convert(final SQLStatement statement) {
         if (statement instanceof SelectStatement) {
-            SqlNode sqlNode = new SelectStatementConverter().convert((SelectStatement) statement);
-            for (CombineSegment each : ((SelectStatement) statement).getCombines()) {
-                SqlNode combineSqlNode = convert(each.getSelectStatement());
-                return new SqlBasicCall(CombineOperatorConverter.convert(each.getCombineType()), Arrays.asList(sqlNode, combineSqlNode), SqlParserPos.ZERO);
+            SqlNode result = new SelectStatementConverter().convert((SelectStatement) statement);
+            if (((SelectStatement) statement).getCombine().isPresent()) {
+                return convert(result, (SelectStatement) statement);
             }
-            return sqlNode;
+            return result;
         }
         throw new OptimizationSQLNodeConvertException(statement);
     }
+    
+    private static SqlNode convert(final SqlNode sqlNode, final SelectStatement selectStatement) {
+        if (selectStatement.getCombine().isPresent()) {
+            CombineSegment combineSegment = selectStatement.getCombine().get();
+            SqlNode combineSqlNode = new SqlBasicCall(CombineOperatorConverter.convert(combineSegment.getCombineType()),
+                    Arrays.asList(sqlNode, new SelectStatementConverter().convert(combineSegment.getSelectStatement())), SqlParserPos.ZERO);
+            return convert(combineSqlNode, combineSegment.getSelectStatement());
+        }
+        return sqlNode;
+    }
 }
diff --git a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverter.java b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverter.java
index 3e38bc28507..0d8151fe2c8 100644
--- a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverter.java
+++ b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverter.java
@@ -39,10 +39,12 @@ public final class CombineOperatorConverter {
     private static void registerCombine() {
         REGISTRY.put(CombineType.UNION, SqlStdOperatorTable.UNION);
         REGISTRY.put(CombineType.UNION_ALL, SqlStdOperatorTable.UNION_ALL);
-        REGISTRY.put(CombineType.INTERSECT_ALL, SqlStdOperatorTable.INTERSECT_ALL);
         REGISTRY.put(CombineType.INTERSECT, SqlStdOperatorTable.INTERSECT);
-        REGISTRY.put(CombineType.EXCEPT_ALL, SqlStdOperatorTable.EXCEPT_ALL);
+        REGISTRY.put(CombineType.INTERSECT_ALL, SqlStdOperatorTable.INTERSECT_ALL);
         REGISTRY.put(CombineType.EXCEPT, SqlStdOperatorTable.EXCEPT);
+        REGISTRY.put(CombineType.EXCEPT_ALL, SqlStdOperatorTable.EXCEPT_ALL);
+        REGISTRY.put(CombineType.MINUS, SqlStdOperatorTable.EXCEPT);
+        REGISTRY.put(CombineType.MINUS_ALL, SqlStdOperatorTable.EXCEPT_ALL);
     }
     
     /**
diff --git a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/util/SQLFederationPlannerUtil.java b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/util/SQLFederationPlannerUtil.java
index 110ede11d61..72f3c31ce1e 100644
--- a/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/util/SQLFederationPlannerUtil.java
+++ b/kernel/sql-federation/optimizer/src/main/java/org/apache/shardingsphere/sqlfederation/optimizer/util/SQLFederationPlannerUtil.java
@@ -109,18 +109,7 @@ public final class SQLFederationPlannerUtil {
     private static void setUpRules(final RelOptPlanner planner) {
         planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
         planner.addRelTraitDef(RelCollationTraitDef.INSTANCE);
-        planner.addRule(EnumerableRules.ENUMERABLE_CALC_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_SORT_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_LIMIT_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_JOIN_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_AGGREGATE_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_FILTER_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_PROJECT_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_CORRELATE_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_UNION_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_FILTER_TO_CALC_RULE);
-        planner.addRule(EnumerableRules.ENUMERABLE_PROJECT_TO_CALC_RULE);
+        EnumerableRules.rules().forEach(planner::addRule);
     }
     
     private static Collection<RelOptRule> getSubQueryRules() {
diff --git a/kernel/sql-federation/optimizer/src/test/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverterTest.java b/kernel/sql-federation/optimizer/src/test/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverterTest.java
index 81667d2cb9f..a8dc1d77afd 100644
--- a/kernel/sql-federation/optimizer/src/test/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverterTest.java
+++ b/kernel/sql-federation/optimizer/src/test/java/org/apache/shardingsphere/sqlfederation/optimizer/converter/type/CombineOperatorConverterTest.java
@@ -34,10 +34,7 @@ public final class CombineOperatorConverterTest {
         assertThat(CombineOperatorConverter.convert(CombineType.INTERSECT), is(SqlStdOperatorTable.INTERSECT));
         assertThat(CombineOperatorConverter.convert(CombineType.EXCEPT_ALL), is(SqlStdOperatorTable.EXCEPT_ALL));
         assertThat(CombineOperatorConverter.convert(CombineType.EXCEPT), is(SqlStdOperatorTable.EXCEPT));
-    }
-    
-    @Test(expected = IllegalStateException.class)
-    public void assertConvertFailure() {
-        CombineOperatorConverter.convert(CombineType.MINUS);
+        assertThat(CombineOperatorConverter.convert(CombineType.MINUS_ALL), is(SqlStdOperatorTable.EXCEPT_ALL));
+        assertThat(CombineOperatorConverter.convert(CombineType.MINUS), is(SqlStdOperatorTable.EXCEPT));
     }
 }
diff --git a/sql-parser/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/impl/MySQLStatementSQLVisitor.java b/sql-parser/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/impl/MySQLStatementSQLVisitor.java
index c1b4075fbe1..48080bda734 100644
--- a/sql-parser/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/impl/MySQLStatementSQLVisitor.java
+++ b/sql-parser/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/impl/MySQLStatementSQLVisitor.java
@@ -686,11 +686,16 @@ public abstract class MySQLStatementSQLVisitor extends MySQLStatementBaseVisitor
         }
         if (null != ctx.queryExpressionBody()) {
             MySQLSelectStatement result = (MySQLSelectStatement) visit(ctx.queryExpressionBody());
-            result.getCombines().add((CombineSegment) visitCombineClause(ctx.combineClause()));
+            CombineSegment combineSegment = (CombineSegment) visitCombineClause(ctx.combineClause());
+            if (result.getCombine().isPresent()) {
+                result.getCombine().get().getSelectStatement().setCombine(combineSegment);
+            } else {
+                result.setCombine(combineSegment);
+            }
             return result;
         }
         MySQLSelectStatement result = (MySQLSelectStatement) visit(ctx.queryExpressionParens());
-        result.getCombines().add((CombineSegment) visitCombineClause(ctx.combineClause()));
+        result.setCombine((CombineSegment) visitCombineClause(ctx.combineClause()));
         return result;
     }
     
diff --git a/sql-parser/dialect/opengauss/src/main/java/org/apache/shardingsphere/sql/parser/opengauss/visitor/statement/impl/OpenGaussStatementSQLVisitor.java b/sql-parser/dialect/opengauss/src/main/java/org/apache/shardingsphere/sql/parser/opengauss/visitor/statement/impl/OpenGaussStatementSQLVisitor.java
index cf2cf4e558d..5dc4adccdfb 100644
--- a/sql-parser/dialect/opengauss/src/main/java/org/apache/shardingsphere/sql/parser/opengauss/visitor/statement/impl/OpenGaussStatementSQLVisitor.java
+++ b/sql-parser/dialect/opengauss/src/main/java/org/apache/shardingsphere/sql/parser/opengauss/visitor/statement/impl/OpenGaussStatementSQLVisitor.java
@@ -902,8 +902,13 @@ public abstract class OpenGaussStatementSQLVisitor extends OpenGaussStatementBas
         }
         if (null != ctx.selectClauseN() && !ctx.selectClauseN().isEmpty()) {
             OpenGaussSelectStatement result = (OpenGaussSelectStatement) visit(ctx.selectClauseN(0));
-            result.getCombines().add(new CombineSegment(
-                    ((TerminalNode) ctx.getChild(1)).getSymbol().getStartIndex(), ctx.getStop().getStopIndex(), getCombineType(ctx), (OpenGaussSelectStatement) visit(ctx.selectClauseN(1))));
+            CombineSegment combineSegment = new CombineSegment(((TerminalNode) ctx.getChild(1)).getSymbol().getStartIndex(), ctx.getStop().getStopIndex(), getCombineType(ctx),
+                    (OpenGaussSelectStatement) visit(ctx.selectClauseN(1)));
+            if (result.getCombine().isPresent()) {
+                result.getCombine().get().getSelectStatement().setCombine(combineSegment);
+            } else {
+                result.setCombine(combineSegment);
+            }
             return result;
         }
         return visit(ctx.selectWithParens());
diff --git a/sql-parser/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/postgresql/visitor/statement/impl/PostgreSQLStatementSQLVisitor.java b/sql-parser/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/postgresql/visitor/statement/impl/PostgreSQLStatementSQLVisitor.java
index baba4820605..58dd12ba80d 100644
--- a/sql-parser/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/postgresql/visitor/statement/impl/PostgreSQLStatementSQLVisitor.java
+++ b/sql-parser/dialect/postgresql/src/main/java/org/apache/shardingsphere/sql/parser/postgresql/visitor/statement/impl/PostgreSQLStatementSQLVisitor.java
@@ -872,8 +872,13 @@ public abstract class PostgreSQLStatementSQLVisitor extends PostgreSQLStatementP
         }
         if (null != ctx.selectClauseN() && !ctx.selectClauseN().isEmpty()) {
             PostgreSQLSelectStatement result = (PostgreSQLSelectStatement) visit(ctx.selectClauseN(0));
-            result.getCombines().add(new CombineSegment(
-                    ((TerminalNode) ctx.getChild(1)).getSymbol().getStartIndex(), ctx.getStop().getStopIndex(), getCombineType(ctx), (PostgreSQLSelectStatement) visit(ctx.selectClauseN(1))));
+            CombineSegment combineSegment = new CombineSegment(((TerminalNode) ctx.getChild(1)).getSymbol().getStartIndex(), ctx.getStop().getStopIndex(), getCombineType(ctx),
+                    (PostgreSQLSelectStatement) visit(ctx.selectClauseN(1)));
+            if (result.getCombine().isPresent()) {
+                result.getCombine().get().getSelectStatement().setCombine(combineSegment);
+            } else {
+                result.setCombine(combineSegment);
+            }
             return result;
         }
         return visit(ctx.selectWithParens());
diff --git a/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/extractor/TableExtractor.java b/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/extractor/TableExtractor.java
index 07b52ee410e..b63dc09e69e 100644
--- a/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/extractor/TableExtractor.java
+++ b/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/extractor/TableExtractor.java
@@ -91,8 +91,8 @@ public final class TableExtractor {
         if (SelectStatementHandler.getLockSegment(selectStatement).isPresent()) {
             extractTablesFromLock(SelectStatementHandler.getLockSegment(selectStatement).get());
         }
-        if (!selectStatement.getCombines().isEmpty()) {
-            selectStatement.getCombines().forEach(each -> extractTablesFromSelect(each.getSelectStatement()));
+        if (selectStatement.getCombine().isPresent()) {
+            extractTablesFromSelect(selectStatement.getCombine().get().getSelectStatement());
         }
     }
     
diff --git a/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/statement/dml/SelectStatement.java b/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/statement/dml/SelectStatement.java
index 904abc38a36..58756a57501 100644
--- a/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/statement/dml/SelectStatement.java
+++ b/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/statement/dml/SelectStatement.java
@@ -29,8 +29,6 @@ import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.Whe
 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.statement.AbstractSQLStatement;
 
-import java.util.Collection;
-import java.util.LinkedList;
 import java.util.Optional;
 
 /**
@@ -53,7 +51,7 @@ public abstract class SelectStatement extends AbstractSQLStatement implements DM
     
     private OrderBySegment orderBy;
     
-    private final Collection<CombineSegment> combines = new LinkedList<>();
+    private CombineSegment combine;
     
     /**
      * Get where.
@@ -90,4 +88,13 @@ public abstract class SelectStatement extends AbstractSQLStatement implements DM
     public Optional<OrderBySegment> getOrderBy() {
         return Optional.ofNullable(orderBy);
     }
+    
+    /**
+     * Get combine segment.
+     *
+     * @return combine segment
+     */
+    public Optional<CombineSegment> getCombine() {
+        return Optional.ofNullable(combine);
+    }
 }
diff --git a/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtil.java b/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtil.java
index 5ed91c8ed76..8aafb6a62a6 100644
--- a/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtil.java
+++ b/sql-parser/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtil.java
@@ -20,7 +20,6 @@ package org.apache.shardingsphere.sql.parser.sql.common.util;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import org.apache.shardingsphere.sql.parser.sql.common.constant.SubqueryType;
-import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.combine.CombineSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BetweenExpression;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.BinaryOperationExpression;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExistsSubqueryExpression;
@@ -65,13 +64,7 @@ public final class SubqueryExtractUtil {
         if (selectStatement.getWhere().isPresent()) {
             extractSubquerySegmentsFromExpression(result, selectStatement.getWhere().get().getExpr());
         }
-        extractSubquerySegmentsFromCombines(result, selectStatement.getCombines());
-    }
-    
-    private static void extractSubquerySegmentsFromCombines(final List<SubquerySegment> result, final Collection<CombineSegment> combineSegments) {
-        for (CombineSegment each : combineSegments) {
-            extractSubquerySegments(result, each.getSelectStatement());
-        }
+        selectStatement.getCombine().ifPresent(optional -> extractSubquerySegments(result, optional.getSelectStatement()));
     }
     
     private static void extractSubquerySegmentsFromProjections(final List<SubquerySegment> result, final ProjectionsSegment projections) {
diff --git a/sql-parser/statement/src/test/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtilTest.java b/sql-parser/statement/src/test/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtilTest.java
index 14d9345fb9c..5fa0d28c108 100644
--- a/sql-parser/statement/src/test/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtilTest.java
+++ b/sql-parser/statement/src/test/java/org/apache/shardingsphere/sql/parser/sql/common/util/SubqueryExtractUtilTest.java
@@ -170,7 +170,7 @@ public final class SubqueryExtractUtilTest {
     @Test
     public void assertGetSubquerySegmentsWithCombineSegment() {
         SelectStatement selectStatement = new MySQLSelectStatement();
-        selectStatement.getCombines().add(new CombineSegment(0, 0, CombineType.UNION, createSelectStatementForCombineSegment()));
+        selectStatement.setCombine(new CombineSegment(0, 0, CombineType.UNION, createSelectStatementForCombineSegment()));
         Collection<SubquerySegment> actual = SubqueryExtractUtil.getSubquerySegments(selectStatement);
         assertThat(actual.size(), is(1));
     }
diff --git a/test/optimize/src/test/java/org/apache/shardingsphere/infra/federation/converter/parameterized/engine/SQLNodeConverterEngineParameterizedTest.java b/test/optimize/src/test/java/org/apache/shardingsphere/infra/federation/converter/parameterized/engine/SQLNodeConverterEngineParameterizedTest.java
index 5944dcb10bb..5f9a925ea84 100644
--- a/test/optimize/src/test/java/org/apache/shardingsphere/infra/federation/converter/parameterized/engine/SQLNodeConverterEngineParameterizedTest.java
+++ b/test/optimize/src/test/java/org/apache/shardingsphere/infra/federation/converter/parameterized/engine/SQLNodeConverterEngineParameterizedTest.java
@@ -87,8 +87,6 @@ public final class SQLNodeConverterEngineParameterizedTest {
         SUPPORTED_SQL_CASE_IDS.add("select_distinct_with_single_count_group_by");
         SUPPORTED_SQL_CASE_IDS.add("select_bit_xor");
         SUPPORTED_SQL_CASE_IDS.add("select_with_schema");
-        SUPPORTED_SQL_CASE_IDS.add("select_with_union");
-        SUPPORTED_SQL_CASE_IDS.add("select_with_union_all");
         SUPPORTED_SQL_CASE_IDS.add("select_with_same_table_name_and_alias");
         SUPPORTED_SQL_CASE_IDS.add("select_count_like_concat");
         SUPPORTED_SQL_CASE_IDS.add("select_order_by_asc_and_index_desc");
@@ -128,6 +126,12 @@ public final class SQLNodeConverterEngineParameterizedTest {
         SUPPORTED_SQL_CASE_IDS.add("select_pagination_with_offset_fetch");
         SUPPORTED_SQL_CASE_IDS.add("select_pagination_with_limit_offset_and_row_count");
         SUPPORTED_SQL_CASE_IDS.add("select_pagination_with_limit_row_count");
+        SUPPORTED_SQL_CASE_IDS.add("select_with_union");
+        SUPPORTED_SQL_CASE_IDS.add("select_with_union_all");
+        SUPPORTED_SQL_CASE_IDS.add("select_union");
+        SUPPORTED_SQL_CASE_IDS.add("select_intersect");
+        SUPPORTED_SQL_CASE_IDS.add("select_except");
+        SUPPORTED_SQL_CASE_IDS.add("select_minus");
     }
     // CHECKSTYLE:ON
     
diff --git a/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/asserts/statement/dml/impl/SelectStatementAssert.java b/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/asserts/statement/dml/impl/SelectStatementAssert.java
index ec8163e717a..53bb42754c5 100644
--- a/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/asserts/statement/dml/impl/SelectStatementAssert.java
+++ b/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/asserts/statement/dml/impl/SelectStatementAssert.java
@@ -19,9 +19,9 @@ package org.apache.shardingsphere.test.sql.parser.parameterized.asserts.statemen
 
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.combine.CombineSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.pagination.limit.LimitSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.predicate.LockSegment;
-import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.combine.CombineSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.ModelSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.WindowSegment;
 import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.WithSegment;
@@ -43,13 +43,12 @@ import org.apache.shardingsphere.test.sql.parser.parameterized.asserts.segment.w
 import org.apache.shardingsphere.test.sql.parser.parameterized.asserts.segment.with.WithClauseAssert;
 import org.apache.shardingsphere.test.sql.parser.parameterized.jaxb.cases.domain.statement.dml.SelectStatementTestCase;
 
-import java.util.Collection;
 import java.util.Optional;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
-import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -76,7 +75,7 @@ public final class SelectStatementAssert {
         assertTable(assertContext, actual, expected);
         assertLockClause(assertContext, actual, expected);
         assertWithClause(assertContext, actual, expected);
-        assertCombines(assertContext, actual, expected);
+        assertCombineClause(assertContext, actual, expected);
         assertModelClause(assertContext, actual, expected);
     }
     
@@ -181,19 +180,15 @@ public final class SelectStatementAssert {
         }
     }
     
-    private static void assertCombines(final SQLCaseAssertContext assertContext, final SelectStatement actual, final SelectStatementTestCase expected) {
-        if (expected.getCombine().isEmpty()) {
-            return;
-        }
-        Collection<CombineSegment> combineSegments = actual.getCombines();
-        assertFalse(assertContext.getText("Actual combine segment should exist."), combineSegments.isEmpty());
-        assertThat(assertContext.getText("Combine size assertion error: "), combineSegments.size(), is(expected.getCombine().size()));
-        int count = 0;
-        for (CombineSegment each : combineSegments) {
-            assertThat(assertContext.getText("Combine type assertion error: "), each.getCombineType().name(), is(expected.getCombine().get(count).getCombineType()));
-            SQLSegmentAssert.assertIs(assertContext, each, expected.getCombine().get(count));
-            assertIs(assertContext, each.getSelectStatement(), expected.getCombine().get(count).getSelectClause());
-            count++;
+    private static void assertCombineClause(final SQLCaseAssertContext assertContext, final SelectStatement actual, final SelectStatementTestCase expected) {
+        Optional<CombineSegment> combineSegment = actual.getCombine();
+        if (null == expected.getCombineClause()) {
+            assertFalse(assertContext.getText("Actual combine segment should not exist."), combineSegment.isPresent());
+        } else {
+            assertTrue(assertContext.getText("Actual combine segment should exist."), combineSegment.isPresent());
+            assertThat(assertContext.getText("Combine type assertion error: "), combineSegment.get().getCombineType().name(), is(expected.getCombineClause().getCombineType()));
+            SQLSegmentAssert.assertIs(assertContext, combineSegment.get(), expected.getCombineClause());
+            assertIs(assertContext, combineSegment.get().getSelectStatement(), expected.getCombineClause().getSelectClause());
         }
     }
     
diff --git a/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/jaxb/cases/domain/statement/dml/SelectStatementTestCase.java b/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/jaxb/cases/domain/statement/dml/SelectStatementTestCase.java
index 3754835f898..5722807dbbf 100644
--- a/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/jaxb/cases/domain/statement/dml/SelectStatementTestCase.java
+++ b/test/parser/src/main/java/org/apache/shardingsphere/test/sql/parser/parameterized/jaxb/cases/domain/statement/dml/SelectStatementTestCase.java
@@ -34,8 +34,6 @@ import org.apache.shardingsphere.test.sql.parser.parameterized.jaxb.cases.domain
 import org.apache.shardingsphere.test.sql.parser.parameterized.jaxb.cases.domain.statement.SQLParserTestCase;
 
 import javax.xml.bind.annotation.XmlElement;
-import java.util.LinkedList;
-import java.util.List;
 
 /**
  * Select statement test case.
@@ -78,7 +76,7 @@ public final class SelectStatementTestCase extends SQLParserTestCase {
     private ExpectedWithClause withClause;
     
     @XmlElement(name = "combine")
-    private List<ExpectedCombine> combine = new LinkedList<>();
+    private ExpectedCombine combineClause;
     
     @XmlElement(name = "model")
     private ExpectedModelClause modelClause;
diff --git a/test/parser/src/main/resources/case/ddl/create-view.xml b/test/parser/src/main/resources/case/ddl/create-view.xml
index e4c023db6f5..85796961278 100644
--- a/test/parser/src/main/resources/case/ddl/create-view.xml
+++ b/test/parser/src/main/resources/case/ddl/create-view.xml
@@ -74,30 +74,30 @@
         <select>
             <!-- FIXME support VALUES (1) projection parse -->
             <projections start-index="-1" stop-index="-1" />
+            <combine combine-type="UNION_ALL" start-index="58" stop-index="107">
+                <select>
+                    <projections start-index="75" stop-index="77">
+                        <expression-projection text="n+1" start-index="75" stop-index="77" />
+                    </projections>
+                    <from>
+                        <simple-table name="nums_1_100" start-index="84" stop-index="93" />
+                    </from>
+                    <where start-index="95" stop-index="107">
+                        <expr>
+                            <binary-operation-expression start-index="101" stop-index="107">
+                                <left>
+                                    <column name="n" start-index="101" stop-index="101"/>
+                                </left>
+                                <right>
+                                    <literal-expression value="100" start-index="105" stop-index="107"/>
+                                </right>
+                                <operator>=</operator>
+                            </binary-operation-expression>
+                        </expr>
+                    </where>
+                </select>
+            </combine>
         </select>
-        <combine combine-type="UNION_ALL" start-index="58" stop-index="107">
-            <select>
-                <projections start-index="75" stop-index="77">
-                    <expression-projection text="n+1" alias="a" start-index="75" stop-index="77" />
-                </projections>
-                <from>
-                    <simple-table name="nums_1_100" start-index="84" stop-index="93" />
-                </from>
-                <where start-index="95" stop-index="107">
-                    <expr>
-                        <binary-operation-expression start-index="101" stop-index="107">
-                            <left>
-                                <column name="n" start-index="101" stop-index="101"/>
-                            </left>
-                            <right>
-                                <literal-expression value="100" start-index="105" stop-index="107"/>
-                            </right>
-                            <operator>=</operator>
-                        </binary-operation-expression>
-                    </expr>
-                </where>
-            </select>
-        </combine>
     </create-view>
     
     <create-view sql-case-id="create_view_with_option" view-definition="SELECT * FROM t_order">
diff --git a/test/parser/src/main/resources/case/dml/select-union.xml b/test/parser/src/main/resources/case/dml/select-union.xml
index 2333fb54bf9..ab9c67c199f 100644
--- a/test/parser/src/main/resources/case/dml/select-union.xml
+++ b/test/parser/src/main/resources/case/dml/select-union.xml
@@ -43,7 +43,7 @@
         <from>
             <simple-table name="table1" start-index="14" stop-index="19" />
         </from>
-        <union union-type="UNION_ALL" start-index="21" stop-index="50">
+        <combine combine-type="UNION_ALL" start-index="21" stop-index="50">
             <select>
                 <projections start-index="38" stop-index="38">
                     <shorthand-projection start-index="38" stop-index="38" />
@@ -52,7 +52,7 @@
                     <simple-table name="table2" start-index="45" stop-index="50" />
                 </from>
             </select>
-        </union>
+        </combine>
     </select>
 
     <select sql-case-id="select_union">
@@ -89,16 +89,16 @@
                 <from>
                     <simple-table name="table2" start-index="45" stop-index="50" />
                 </from>
-            </select>
-        </combine>
-        <combine combine-type="INTERSECT" start-index="52" stop-index="81">
-            <select>
-                <projections start-index="69" stop-index="69">
-                    <shorthand-projection start-index="69" stop-index="69" />
-                </projections>
-                <from>
-                    <simple-table name="table3" start-index="76" stop-index="81" />
-                </from>
+                <combine combine-type="INTERSECT" start-index="52" stop-index="81">
+                    <select>
+                        <projections start-index="69" stop-index="69">
+                            <shorthand-projection start-index="69" stop-index="69" />
+                        </projections>
+                        <from>
+                            <simple-table name="table3" start-index="76" stop-index="81" />
+                        </from>
+                    </select>
+                </combine>
             </select>
         </combine>
     </select>
@@ -118,16 +118,16 @@
                 <from>
                     <simple-table name="table2" start-index="46" stop-index="51" />
                 </from>
-            </select>
-        </combine>
-        <combine combine-type="EXCEPT_ALL" start-index="53" stop-index="83">
-            <select>
-                <projections start-index="71" stop-index="71">
-                    <shorthand-projection start-index="71" stop-index="71" />
-                </projections>
-                <from>
-                    <simple-table name="table3" start-index="78" stop-index="83" />
-                </from>
+                <combine combine-type="EXCEPT_ALL" start-index="53" stop-index="83">
+                    <select>
+                        <projections start-index="71" stop-index="71">
+                            <shorthand-projection start-index="71" stop-index="71" />
+                        </projections>
+                        <from>
+                            <simple-table name="table3" start-index="78" stop-index="83" />
+                        </from>
+                    </select>
+                </combine>
             </select>
         </combine>
     </select>