You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by ch...@apache.org on 2025/06/11 10:08:40 UTC
(shardingsphere) branch master updated: Support mysql quantify operator parse and upgrade calcite to 1.40.0 (#35675)
This is an automated email from the ASF dual-hosted git repository.
chengzhang 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 473967dfd07 Support mysql quantify operator parse and upgrade calcite to 1.40.0 (#35675)
473967dfd07 is described below
commit 473967dfd07b27afe580d0bdb0c338aedd74a3ad
Author: Zhengqiang Duan <du...@apache.org>
AuthorDate: Wed Jun 11 18:08:35 2025 +0800
Support mysql quantify operator parse and upgrade calcite to 1.40.0 (#35675)
* Support mysql quantify operator parse and upgrade calcite to 1.40.0
* update license and release note
* fix checkstyle
* fix parse it
* fix sql federation it
---
RELEASE-NOTES.md | 1 +
distribution/proxy/src/main/release-docs/LICENSE | 4 +-
.../dml/expression/ExpressionSegmentBinder.java | 5 ++
.../expression/type/OuterJoinExpressionBinder.java | 2 +-
....java => QuantifySubqueryExpressionBinder.java} | 25 ++++----
.../compiler/compiler/SQLStatementCompiler.java | 19 ++++---
.../builder/SQLFederationPlannerBuilder.java | 2 +-
.../transformation/PushFilterIntoScanRule.java | 19 +++++++
.../transformation/PushProjectIntoScanRule.java | 3 +-
.../rel/converter/SQLFederationRelConverter.java | 2 +-
.../rel/rewriter/LogicalScanRelRewriter.java | 6 +-
.../segment/expression/ExpressionConverter.java | 11 +++-
.../impl/BinaryOperationExpressionConverter.java | 66 ++++++++++++++++------
.../impl/QuantifySubqueryExpressionConverter.java | 43 ++++++++++++++
.../resources/cases/federation-query-sql-cases.xml | 8 +--
.../visitor/statement/MySQLStatementVisitor.java | 16 +++++-
.../statement/core/extractor/TableExtractor.java | 4 ++
.../dml/expr/QuantifySubqueryExpression.java | 43 ++++++++++++++
pom.xml | 2 +-
.../segment/expression/ExpressionAssert.java | 14 +++++
.../jaxb/segment/impl/expr/ExpectedExpression.java | 3 +
.../expr/ExpectedQuantifySubqueryExpression.java | 39 +++++++++++++
.../main/resources/case/dml/select-expression.xml | 51 +++++++++--------
23 files changed, 308 insertions(+), 80 deletions(-)
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 7371dc54191..aaef113477e 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -45,6 +45,7 @@
1. Encrypt: Use EncryptDerivedColumnSuffix to enhance encrypt table subquery rewrite logic - [#34829](https://github.com/apache/shardingsphere/pull/34829)
1. Encrypt: Add quotes to encrypt rewrite derived columns - [#34950](https://github.com/apache/shardingsphere/pull/34950)
1. SQL Router: Improve support for executing tableless SQL with single data source - [#35659](https://github.com/apache/shardingsphere/pull/35659)
+1. SQL Parser: Support mysql quantify operator parse and upgrade calcite to 1.40.0 - [#35675](https://github.com/apache/shardingsphere/pull/35675)
### Bug Fixes
diff --git a/distribution/proxy/src/main/release-docs/LICENSE b/distribution/proxy/src/main/release-docs/LICENSE
index 0c69969089f..84b892828fa 100644
--- a/distribution/proxy/src/main/release-docs/LICENSE
+++ b/distribution/proxy/src/main/release-docs/LICENSE
@@ -222,8 +222,8 @@ The text of each license is the standard Apache 2.0 license.
avatica-core 1.26.0: https://calcite.apache.org/avatica, Apache 2.0
avatica-metrics 1.26.0: https://calcite.apache.org/avatica, Apache 2.0
caffeine 2.9.3: https://github.com/ben-manes/caffeine, Apache 2.0
- calcite-core 1.39.0: https://calcite.apache.org, Apache 2.0
- calcite-linq4j 1.39.0: https://calcite.apache.org, Apache 2.0
+ calcite-core 1.40.0: https://calcite.apache.org, Apache 2.0
+ calcite-linq4j 1.40.0: https://calcite.apache.org, Apache 2.0
commons-codec 1.16.0: https://github.com/apache/commons-codec, Apache 2.0
commons-dbcp2 2.11.0: https://commons.apache.org/proper/commons-dbcp, Apache 2.0
commons-exec 1.3: https://github.com/apache/commons-exec, Apache 2.0
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/ExpressionSegmentBinder.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/ExpressionSegmentBinder.java
index 1a47b2ba240..d81bfcdb47e 100644
--- a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/ExpressionSegmentBinder.java
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/ExpressionSegmentBinder.java
@@ -34,6 +34,7 @@ import org.apache.shardingsphere.infra.binder.engine.segment.dml.expression.type
import org.apache.shardingsphere.infra.binder.engine.segment.dml.expression.type.InExpressionBinder;
import org.apache.shardingsphere.infra.binder.engine.segment.dml.expression.type.NotExpressionBinder;
import org.apache.shardingsphere.infra.binder.engine.segment.dml.expression.type.OuterJoinExpressionBinder;
+import org.apache.shardingsphere.infra.binder.engine.segment.dml.expression.type.QuantifySubqueryExpressionBinder;
import org.apache.shardingsphere.infra.binder.engine.segment.dml.expression.type.SubquerySegmentBinder;
import org.apache.shardingsphere.infra.binder.engine.segment.dml.from.context.TableSegmentBinderContext;
import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext;
@@ -46,6 +47,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.Expr
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.FunctionSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubqueryExpressionSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationDistinctProjectionSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationProjectionSegment;
@@ -109,6 +111,9 @@ public final class ExpressionSegmentBinder {
if (segment instanceof OuterJoinExpression) {
return OuterJoinExpressionBinder.bind((OuterJoinExpression) segment, parentSegmentType, binderContext, tableBinderContexts, outerTableBinderContexts);
}
+ if (segment instanceof QuantifySubqueryExpression) {
+ return QuantifySubqueryExpressionBinder.bind((QuantifySubqueryExpression) segment, binderContext, tableBinderContexts);
+ }
// TODO support more ExpressionSegment bound
return segment;
}
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java
index 06eda64bb82..268842ce9ad 100644
--- a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java
@@ -41,7 +41,7 @@ public final class OuterJoinExpressionBinder {
* @param binderContext SQL statement binder context
* @param tableBinderContexts table binder contexts
* @param outerTableBinderContexts outer table binder contexts
- * @return bound case when expression
+ * @return bound outer join expression
*/
public static OuterJoinExpression bind(final OuterJoinExpression segment, final SegmentType parentSegmentType,
final SQLStatementBinderContext binderContext, final Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts,
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/QuantifySubqueryExpressionBinder.java
similarity index 54%
copy from infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java
copy to infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/QuantifySubqueryExpressionBinder.java
index 06eda64bb82..bfb192efb0f 100644
--- a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/OuterJoinExpressionBinder.java
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/expression/type/QuantifySubqueryExpressionBinder.java
@@ -21,32 +21,27 @@ import com.cedarsoftware.util.CaseInsensitiveMap.CaseInsensitiveString;
import com.google.common.collect.Multimap;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.infra.binder.engine.segment.SegmentType;
import org.apache.shardingsphere.infra.binder.engine.segment.dml.from.context.TableSegmentBinderContext;
import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext;
-import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
-import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.join.OuterJoinExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
/**
- * Outer join expression binder.
+ * Quantify subquery expression binder.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public final class OuterJoinExpressionBinder {
+public final class QuantifySubqueryExpressionBinder {
/**
- * Bind outer join expression.
+ * Bind quantify subquery expression.
*
- * @param segment outer join expression segment
- * @param parentSegmentType parent segment type
+ * @param segment quantify subquery expression
* @param binderContext SQL statement binder context
* @param tableBinderContexts table binder contexts
- * @param outerTableBinderContexts outer table binder contexts
- * @return bound case when expression
+ * @return bound quantify subquery expression
*/
- public static OuterJoinExpression bind(final OuterJoinExpression segment, final SegmentType parentSegmentType,
- final SQLStatementBinderContext binderContext, final Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts,
- final Multimap<CaseInsensitiveString, TableSegmentBinderContext> outerTableBinderContexts) {
- ColumnSegment boundColumnName = ColumnSegmentBinder.bind(segment.getColumnName(), parentSegmentType, binderContext, tableBinderContexts, outerTableBinderContexts);
- return new OuterJoinExpression(segment.getStartIndex(), segment.getStopIndex(), boundColumnName, segment.getJoinOperator(), segment.getText());
+ public static QuantifySubqueryExpression bind(final QuantifySubqueryExpression segment, final SQLStatementBinderContext binderContext,
+ final Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts) {
+ return new QuantifySubqueryExpression(segment.getStartIndex(), segment.getStopIndex(), SubquerySegmentBinder.bind(segment.getSubquery(), binderContext, tableBinderContexts),
+ segment.getQuantifyOperator());
}
}
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/SQLStatementCompiler.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/SQLStatementCompiler.java
index 1c055adfb84..d9f2ea77860 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/SQLStatementCompiler.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/compiler/SQLStatementCompiler.java
@@ -54,26 +54,31 @@ public final class SQLStatementCompiler {
SqlNode sqlNode = SQLNodeConverterEngine.convert(sqlStatement);
RelNode logicalPlan = converter.convertQuery(sqlNode, true, true).rel;
RelDataType resultColumnType = converter.getValidatedNodeType(sqlNode);
- RelNode rewritePlan = rewrite(logicalPlan, databaseType);
- RelNode physicalPlan = optimize(rewritePlan, converter);
+ RelNode rewrittenPlan = rewrite(logicalPlan, databaseType);
+ RelNode physicalPlan = optimize(rewrittenPlan, converter, databaseType);
RelMetadataQueryBase.THREAD_PROVIDERS.remove();
return new SQLFederationExecutionPlan(physicalPlan, resultColumnType);
}
private RelNode rewrite(final RelNode logicalPlan, final String databaseType) {
- RelNode rewrittenPlan = LogicalScanRelRewriter.rewrite(logicalPlan, databaseType);
+ RelNode rewrittenPlan = rewriteTableScan(logicalPlan, databaseType);
RelOptPlanner hepPlanner = SQLFederationPlannerBuilder.buildHepPlanner();
hepPlanner.setRoot(rewrittenPlan);
return hepPlanner.findBestExp();
}
- private RelNode optimize(final RelNode rewritePlan, final SQLFederationRelConverter converter) {
+ private RelNode optimize(final RelNode logicalPlan, final SQLFederationRelConverter converter, final String databaseType) {
+ RelNode rewrittenPlan = rewriteTableScan(logicalPlan, databaseType);
RelOptPlanner planner = converter.getCluster().getPlanner();
- if (rewritePlan.getTraitSet().contains(convention)) {
- planner.setRoot(rewritePlan);
+ if (rewrittenPlan.getTraitSet().contains(convention)) {
+ planner.setRoot(rewrittenPlan);
} else {
- planner.setRoot(planner.changeTraits(rewritePlan, converter.getCluster().traitSet().replace(convention)));
+ planner.setRoot(planner.changeTraits(rewrittenPlan, converter.getCluster().traitSet().replace(convention)));
}
return planner.findBestExp();
}
+
+ private RelNode rewriteTableScan(final RelNode logicalPlan, final String databaseType) {
+ return LogicalScanRelRewriter.rewrite(logicalPlan, databaseType);
+ }
}
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/builder/SQLFederationPlannerBuilder.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/builder/SQLFederationPlannerBuilder.java
index df511483019..a9d33e3ec39 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/builder/SQLFederationPlannerBuilder.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/builder/SQLFederationPlannerBuilder.java
@@ -105,9 +105,9 @@ public final class SQLFederationPlannerBuilder {
builder.addGroupBegin().addRuleCollection(getFilterRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
builder.addGroupBegin().addRuleCollection(getProjectRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
builder.addGroupBegin().addRuleCollection(getAggregationRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
- builder.addGroupBegin().addRuleCollection(getCalcRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
builder.addGroupBegin().addRuleCollection(getSubQueryRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
builder.addGroupBegin().addRuleCollection(getSortRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
+ builder.addGroupBegin().addRuleCollection(getCalcRules()).addGroupEnd().addMatchOrder(HepMatchOrder.BOTTOM_UP);
builder.addMatchLimit(DEFAULT_MATCH_LIMIT);
return new HepPlanner(builder.build());
}
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushFilterIntoScanRule.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushFilterIntoScanRule.java
index 8ad028f0434..da08cbb7f81 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushFilterIntoScanRule.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushFilterIntoScanRule.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.logical.LogicalFilter;
import org.apache.calcite.rel.rules.TransformationRule;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexSubQuery;
import org.apache.shardingsphere.sqlfederation.compiler.rel.operator.logical.LogicalScan;
import org.immutables.value.Value;
@@ -54,9 +55,27 @@ public final class PushFilterIntoScanRule extends RelRule<PushFilterIntoScanRule
}
LogicalFilter logicalFilter = call.rel(0);
RexNode condition = logicalFilter.getCondition();
+ if (isConditionContainsRexSubQuery(condition)) {
+ return false;
+ }
return !(condition instanceof RexCall) || !containsCorrelate(((RexCall) condition).getOperands());
}
+ private boolean isConditionContainsRexSubQuery(final RexNode condition) {
+ if (condition instanceof RexSubQuery) {
+ return true;
+ }
+ if (!(condition instanceof RexCall)) {
+ return false;
+ }
+ for (RexNode each : ((RexCall) condition).getOperands()) {
+ if (each instanceof RexSubQuery) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean containsCorrelate(final Collection<RexNode> operands) {
for (RexNode each : operands) {
if (each.toString().contains(CORRELATE_REFERENCE)) {
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
index 18c68ca6115..614c4fb20ea 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/planner/rule/transformation/PushProjectIntoScanRule.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rel.rules.TransformationRule;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexSubQuery;
import org.apache.shardingsphere.sqlfederation.compiler.rel.operator.logical.LogicalScan;
import org.immutables.value.Value;
@@ -54,7 +55,7 @@ public final class PushProjectIntoScanRule extends RelRule<PushProjectIntoScanRu
}
LogicalProject logicalProject = call.rel(0);
for (RexNode each : logicalProject.getProjects()) {
- if (containsCastFunction(each)) {
+ if (each instanceof RexSubQuery || containsCastFunction(each)) {
return false;
}
}
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/converter/SQLFederationRelConverter.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/converter/SQLFederationRelConverter.java
index 6b538cff433..8aa4a6dd999 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/converter/SQLFederationRelConverter.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/converter/SQLFederationRelConverter.java
@@ -98,7 +98,7 @@ public final class SQLFederationRelConverter {
? new ShardingSphereViewExpander(sqlParserRule, databaseType, createSqlToRelConverter(catalogReader, validator, cluster, sqlParserRule, databaseType, false))
: (rowType, queryString, schemaPath, viewPath) -> null;
// TODO remove withRemoveSortInSubQuery when calcite can expand view which contains order by correctly
- Config converterConfig = SqlToRelConverter.config().withTrimUnusedFields(true).withRemoveSortInSubQuery(false).withExpand(true);
+ Config converterConfig = SqlToRelConverter.config().withTrimUnusedFields(true).withRemoveSortInSubQuery(false);
return new SqlToRelConverter(expander, validator, catalogReader, cluster, StandardConvertletTable.INSTANCE, converterConfig);
}
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/rewriter/LogicalScanRelRewriter.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/rewriter/LogicalScanRelRewriter.java
index f47f80b2908..8c04ad36e36 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/rewriter/LogicalScanRelRewriter.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/rel/rewriter/LogicalScanRelRewriter.java
@@ -22,6 +22,7 @@ import lombok.RequiredArgsConstructor;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelShuttleImpl;
import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.logical.LogicalTableScan;
import org.apache.shardingsphere.sqlfederation.compiler.rel.operator.logical.LogicalScan;
/**
@@ -34,7 +35,10 @@ public final class LogicalScanRelRewriter extends RelShuttleImpl {
@Override
public RelNode visit(final TableScan tableScan) {
- return new LogicalScan(tableScan, databaseType);
+ if (tableScan instanceof LogicalTableScan) {
+ return new LogicalScan(tableScan, databaseType);
+ }
+ return tableScan;
}
/**
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/ExpressionConverter.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/ExpressionConverter.java
index 19a37bce9bb..59cb8451573 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/ExpressionConverter.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/ExpressionConverter.java
@@ -35,6 +35,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InEx
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.IntervalExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.RowExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.TypeCastExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.UnaryOperationExpression;
@@ -50,7 +51,9 @@ import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segmen
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.CaseWhenExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.CollateExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.ColumnConverter;
+import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.ExistsSubqueryExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.ExtractArgExpressionConverter;
+import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.FunctionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.InExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.IntervalExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.ListExpressionConverter;
@@ -58,13 +61,12 @@ import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segmen
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.MatchExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.NotExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.ParameterMarkerExpressionConverter;
+import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.QuantifySubqueryExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.RowExpressionConverter;
+import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.SubqueryExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.TypeCastExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.UnaryOperationExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.VariableSegmentConverter;
-import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.ExistsSubqueryExpressionConverter;
-import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.FunctionConverter;
-import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl.SubqueryExpressionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.projection.impl.AggregationProjectionConverter;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.projection.impl.DataTypeConverter;
@@ -157,6 +159,9 @@ public final class ExpressionConverter {
if (segment instanceof IntervalExpression) {
return IntervalExpressionConverter.convert((IntervalExpression) segment);
}
+ if (segment instanceof QuantifySubqueryExpression) {
+ return QuantifySubqueryExpressionConverter.convert((QuantifySubqueryExpression) segment);
+ }
throw new UnsupportedSQLOperationException("unsupported TableSegment type: " + segment.getClass());
}
}
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/BinaryOperationExpressionConverter.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/BinaryOperationExpressionConverter.java
index 8a2793f1d97..d3543529340 100644
--- a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/BinaryOperationExpressionConverter.java
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/BinaryOperationExpressionConverter.java
@@ -30,6 +30,7 @@ import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.operator.common.SQLExtensionOperatorTable;
import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.ExpressionConverter;
@@ -79,6 +80,7 @@ public final class BinaryOperationExpressionConverter {
register(SqlStdOperatorTable.CONCAT);
register(SqlStdOperatorTable.PATTERN_ALTER);
register(SqlStdOperatorTable.MOD);
+ SqlStdOperatorTable.QUANTIFY_OPERATORS.forEach(BinaryOperationExpressionConverter::register);
register(SQLExtensionOperatorTable.DIV);
register(SQLExtensionOperatorTable.CARET);
register(SQLExtensionOperatorTable.AMPERSAND);
@@ -119,27 +121,57 @@ public final class BinaryOperationExpressionConverter {
}
private static SqlOperator convertOperator(final BinaryOperationExpression segment) {
- String operator = segment.getOperator();
- if ("IS".equalsIgnoreCase(operator)) {
- String literals = String.valueOf(((LiteralExpressionSegment) segment.getRight()).getLiterals());
- if ("NULL".equalsIgnoreCase(literals)) {
- operator = "IS NULL";
- } else if ("NOT NULL".equalsIgnoreCase(literals)) {
- operator = "IS NOT NULL";
- } else if ("FALSE".equalsIgnoreCase(literals)) {
- operator = "IS FALSE";
- } else if ("NOT FALSE".equalsIgnoreCase(literals)) {
- operator = "IS NOT FALSE";
- } else if ("TRUE".equalsIgnoreCase(literals)) {
- operator = "IS TRUE";
- } else if ("NOT TRUE".equalsIgnoreCase(literals)) {
- operator = "IS NOT TRUE";
- }
- }
+ String operator = getOperator(segment);
Preconditions.checkState(REGISTRY.containsKey(operator), "Unsupported SQL operator: `%s`", operator);
return REGISTRY.get(operator);
}
+ private static String getOperator(final BinaryOperationExpression segment) {
+ String result = null;
+ if ("IS".equalsIgnoreCase(segment.getOperator())) {
+ result = findIsOperator(segment).orElse(null);
+ }
+ if (segment.getRight() instanceof QuantifySubqueryExpression) {
+ result = findQuantifyOperator(segment.getOperator(), (QuantifySubqueryExpression) segment.getRight()).orElse(null);
+ }
+ return null == result ? segment.getOperator() : result;
+ }
+
+ private static Optional<String> findIsOperator(final BinaryOperationExpression segment) {
+ String literals = String.valueOf(((LiteralExpressionSegment) segment.getRight()).getLiterals());
+ if ("NULL".equalsIgnoreCase(literals)) {
+ return Optional.of(SqlStdOperatorTable.IS_NULL.getName());
+ } else if ("NOT NULL".equalsIgnoreCase(literals)) {
+ return Optional.of(SqlStdOperatorTable.IS_NOT_NULL.getName());
+ } else if ("FALSE".equalsIgnoreCase(literals)) {
+ return Optional.of(SqlStdOperatorTable.IS_FALSE.getName());
+ } else if ("NOT FALSE".equalsIgnoreCase(literals)) {
+ return Optional.of(SqlStdOperatorTable.IS_NOT_FALSE.getName());
+ } else if ("TRUE".equalsIgnoreCase(literals)) {
+ return Optional.of(SqlStdOperatorTable.IS_TRUE.getName());
+ } else if ("NOT TRUE".equalsIgnoreCase(literals)) {
+ return Optional.of(SqlStdOperatorTable.IS_NOT_TRUE.getName());
+ }
+ return Optional.empty();
+ }
+
+ private static Optional<String> findQuantifyOperator(final String operator, final QuantifySubqueryExpression quantifySubquery) {
+ if ("=".equals(operator)) {
+ return "ALL".equalsIgnoreCase(quantifySubquery.getQuantifyOperator()) ? Optional.of(SqlStdOperatorTable.ALL_EQ.getName()) : Optional.of(SqlStdOperatorTable.SOME_EQ.getName());
+ } else if (">".equals(operator)) {
+ return "ALL".equalsIgnoreCase(quantifySubquery.getQuantifyOperator()) ? Optional.of(SqlStdOperatorTable.ALL_GT.getName()) : Optional.of(SqlStdOperatorTable.SOME_GT.getName());
+ } else if (">=".equals(operator)) {
+ return "ALL".equalsIgnoreCase(quantifySubquery.getQuantifyOperator()) ? Optional.of(SqlStdOperatorTable.ALL_GE.getName()) : Optional.of(SqlStdOperatorTable.SOME_GE.getName());
+ } else if ("<".equals(operator)) {
+ return "ALL".equalsIgnoreCase(quantifySubquery.getQuantifyOperator()) ? Optional.of(SqlStdOperatorTable.ALL_LT.getName()) : Optional.of(SqlStdOperatorTable.SOME_LT.getName());
+ } else if ("<=".equals(operator)) {
+ return "ALL".equalsIgnoreCase(quantifySubquery.getQuantifyOperator()) ? Optional.of(SqlStdOperatorTable.ALL_LE.getName()) : Optional.of(SqlStdOperatorTable.SOME_LE.getName());
+ } else if ("!=".equals(operator) || "<>".equals(operator)) {
+ return "ALL".equalsIgnoreCase(quantifySubquery.getQuantifyOperator()) ? Optional.of(SqlStdOperatorTable.ALL_NE.getName()) : Optional.of(SqlStdOperatorTable.SOME_NE.getName());
+ }
+ return Optional.empty();
+ }
+
private static List<SqlNode> convertSqlNodes(final BinaryOperationExpression segment, final SqlOperator operator) {
SqlNode left = ExpressionConverter.convert(segment.getLeft()).orElseThrow(IllegalStateException::new);
List<SqlNode> result = new LinkedList<>();
diff --git a/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/QuantifySubqueryExpressionConverter.java b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/QuantifySubqueryExpressionConverter.java
new file mode 100644
index 00000000000..d78dd3cb454
--- /dev/null
+++ b/kernel/sql-federation/compiler/src/main/java/org/apache/shardingsphere/sqlfederation/compiler/sql/ast/converter/segment/expression/impl/QuantifySubqueryExpressionConverter.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.segment.expression.impl;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
+import org.apache.shardingsphere.sqlfederation.compiler.sql.ast.converter.statement.select.SelectStatementConverter;
+
+import java.util.Optional;
+
+/**
+ * Quantify subquery expression converter.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class QuantifySubqueryExpressionConverter {
+
+ /**
+ * Convert quantify subquery expression to sql node.
+ *
+ * @param expression quantify subquery expression
+ * @return sql node
+ */
+ public static Optional<SqlNode> convert(final QuantifySubqueryExpression expression) {
+ return null == expression ? Optional.empty() : Optional.of(new SelectStatementConverter().convert(expression.getSubquery().getSelect()));
+ }
+}
diff --git a/kernel/sql-federation/compiler/src/test/resources/cases/federation-query-sql-cases.xml b/kernel/sql-federation/compiler/src/test/resources/cases/federation-query-sql-cases.xml
index 58542f2ca53..4a2520d521b 100644
--- a/kernel/sql-federation/compiler/src/test/resources/cases/federation-query-sql-cases.xml
+++ b/kernel/sql-federation/compiler/src/test/resources/cases/federation-query-sql-cases.xml
@@ -46,15 +46,15 @@
</test-case>
<test-case sql="SELECT t_order_federate.order_id, t_order_federate.user_id FROM t_order_federate WHERE EXISTS (SELECT * FROM t_user_info WHERE t_order_federate.user_id = t_user_info.user_id)">
- <assertion expected-result="EnumerableCalc(expr#0..3=[{inputs}], expr#4=[IS NOT NULL($t3)], proj#0..1=[{exprs}], $condition=[$t4]) EnumerableCorrelate(correlation=[$cor0], joinType=[left], requiredColumns=[{1}]) EnumerableScan(table=[[federate_jdbc, t_order_federate]], sql=[SELECT * FROM `federate_jdbc`.`t_order_federate`], dynamicParameters=[null]) EnumerableAggregate(group=[{}], agg#0=[MIN($0)]) EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], expr#3=[$cor0], [...]
+ <assertion expected-result="EnumerableCalc(expr#0..3=[{inputs}], proj#0..1=[{exprs}]) EnumerableCorrelate(correlation=[$cor0], joinType=[inner], requiredColumns=[{1}]) EnumerableScan(table=[[federate_jdbc, t_order_federate]], sql=[SELECT * FROM `federate_jdbc`.`t_order_federate`], dynamicParameters=[null]) EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..1=[{inputs}], expr#2=[true], expr#3=[$cor0], expr#4=[$t3.user_id], expr#5=[CAST($t4) [...]
</test-case>
<test-case sql="SELECT t_order_federate.order_id, t_order_federate.user_id FROM t_order_federate WHERE t_order_federate.user_id IN (SELECT t_user_info.user_id FROM t_user_info)">
- <assertion expected-result="EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}]) EnumerableHashJoin(condition=[=($1, $2)], joinType=[inner]) EnumerableScan(table=[[federate_jdbc, t_order_federate]], sql=[SELECT `order_id`, `user_id` FROM `federate_jdbc`.`t_order_federate`], dynamicParameters=[null]) EnumerableAggregate(group=[{0}]) EnumerableScan(table=[[federate_jdbc, t_user_info]], sql=[SELECT `user_id` FROM `federate_jdbc`.`t_user_info`], dynamicParameters [...]
+ <assertion expected-result="EnumerableCalc(expr#0..3=[{inputs}], proj#0..1=[{exprs}]) EnumerableHashJoin(condition=[=($1, $3)], joinType=[inner]) EnumerableScan(table=[[federate_jdbc, t_order_federate]], sql=[SELECT * FROM `federate_jdbc`.`t_order_federate`], dynamicParameters=[null]) EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..1=[{inputs}], user_id=[$t0]) EnumerableScan(table=[[federate_jdbc, t_user_info]], sql=[SELECT * FR [...]
</test-case>
<test-case sql="SELECT t_order_federate.order_id, t_order_federate.user_id FROM t_order_federate WHERE user_id BETWEEN (SELECT user_id FROM t_user_info WHERE user_id = 1) AND (SELECT user_id FROM t_user_info WHERE user_id = 3)">
- <assertion expected-result="EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}]) EnumerableNestedLoopJoin(condition=[<=($1, $2)], joinType=[inner]) EnumerableCalc(expr#0..2=[{inputs}], proj#0..1=[{exprs}]) EnumerableNestedLoopJoin(condition=[>=($1, $2)], joinType=[inner]) EnumerableScan(table=[[federate_jdbc, t_order_federate]], sql=[SELECT `order_id`, `user_id` FROM `federate_jdbc`.`t_order_federate`], dynamicParameters=[null]) EnumerableAggre [...]
+ <assertion expected-result="EnumerableScan(table=[[federate_jdbc, t_order_federate]], sql=[SELECT `order_id`, `user_id` FROM `federate_jdbc`.`t_order_federate` WHERE `user_id` >= (((SELECT `user_id` FROM `federate_jdbc`.`t_user_info` WHERE `user_id` = 1))) AND `user_id` <= (((SELECT `user_id` FROM `federate_jdbc`.`t_user_info` WHERE `user_id [...]
</test-case>
<test-case sql="SELECT order_id, user_id FROM t_order_federate UNION SELECT 1, user_id FROM t_user_info WHERE information = 'before'">
@@ -386,7 +386,7 @@
</test-case>
<test-case sql="SELECT * FROM t_order o WHERE o.order_id IN (SELECT i.order_id FROM t_order_item i INNER JOIN t_product p ON i.product_id = p.product_id WHERE p.product_id = 10) ORDER BY order_id">
- <assertion expected-result="EnumerableSort(sort0=[$0], dir0=[ASC]) EnumerableCalc(expr#0..6=[{inputs}], proj#0..5=[{exprs}]) EnumerableHashJoin(condition=[=($0, $6)], joinType=[inner]) EnumerableScan(table=[[federate_jdbc, t_order]], sql=[SELECT * FROM `federate_jdbc`.`t_order`], dynamicParameters=[null]) EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..2=[{inputs}], order_id=[$t0]) EnumerableHashJoin(condition=[=($1, $2)], joinType=[inn [...]
+ <assertion expected-result="EnumerableSort(sort0=[$0], dir0=[ASC]) EnumerableCalc(expr#0..6=[{inputs}], proj#0..5=[{exprs}]) EnumerableHashJoin(condition=[=($0, $6)], joinType=[inner]) EnumerableScan(table=[[federate_jdbc, t_order]], sql=[SELECT * FROM `federate_jdbc`.`t_order`], dynamicParameters=[null]) EnumerableAggregate(group=[{0}]) EnumerableCalc(expr#0..11=[{inputs}], expr#12=[CAST($t6):INTEGER], expr#13=[10], expr#14=[=($t12, [...]
</test-case>
<test-case sql="SELECT MAX(p.price) AS max_price, MIN(p.price) AS min_price, SUM(p.price) AS sum_price, AVG(p.price) AS avg_price, COUNT(1) AS count FROM t_order o INNER JOIN t_order_item i ON o.order_id = i.order_id INNER JOIN t_product p ON i.product_id = p.product_id GROUP BY o.order_id HAVING SUM(p.price) > 10 ORDER BY max_price">
diff --git a/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java b/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
index bac6c709dfe..4f7ee200b00 100644
--- a/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
+++ b/parser/sql/dialect/mysql/src/main/java/org/apache/shardingsphere/sql/parser/mysql/visitor/statement/MySQLStatementVisitor.java
@@ -183,6 +183,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.Inte
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.IntervalUnit;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.RowExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.UnaryOperationExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ValuesExpression;
@@ -506,14 +507,25 @@ public abstract class MySQLStatementVisitor extends MySQLStatementBaseVisitor<AS
if (null != ctx.predicate()) {
right = (ExpressionSegment) visit(ctx.predicate());
} else {
- right = new SubqueryExpressionSegment(
- new SubquerySegment(ctx.subquery().start.getStartIndex(), ctx.subquery().stop.getStopIndex(), (SelectStatement) visit(ctx.subquery()), getOriginalText(ctx.subquery())));
+ right = createRightExpression(ctx);
}
String operator = null == ctx.SAFE_EQ_() ? ctx.comparisonOperator().getText() : ctx.SAFE_EQ_().getText();
String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
}
+ private ExpressionSegment createRightExpression(final BooleanPrimaryContext ctx) {
+ SubqueryExpressionSegment result = new SubqueryExpressionSegment(
+ new SubquerySegment(ctx.subquery().start.getStartIndex(), ctx.subquery().stop.getStopIndex(), (SelectStatement) visit(ctx.subquery()), getOriginalText(ctx.subquery())));
+ if (null != ctx.ANY() || null != ctx.ALL()) {
+ int startIndex = null == ctx.ANY() ? ctx.ALL().getSymbol().getStartIndex() : ctx.ANY().getSymbol().getStartIndex();
+ String quantifyOperator = null == ctx.ANY() ? ctx.ALL().getText() : ctx.ANY().getText();
+ return new QuantifySubqueryExpression(startIndex, result.getStopIndex(), result.getSubquery(), quantifyOperator);
+ } else {
+ return result;
+ }
+ }
+
@Override
public final ASTNode visitPredicate(final PredicateContext ctx) {
if (null != ctx.IN()) {
diff --git a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/extractor/TableExtractor.java b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/extractor/TableExtractor.java
index 310f2238af6..60227c13f87 100644
--- a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/extractor/TableExtractor.java
+++ b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/extractor/TableExtractor.java
@@ -34,6 +34,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.Func
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.TypeCastExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonTableExpressionSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubqueryExpressionSegment;
@@ -193,6 +194,9 @@ public final class TableExtractor {
if (expressionSegment instanceof TypeCastExpression) {
extractTablesFromExpression(((TypeCastExpression) expressionSegment).getExpression());
}
+ if (expressionSegment instanceof QuantifySubqueryExpression) {
+ extractTablesFromExpression(((QuantifySubqueryExpression) expressionSegment).getSubquery());
+ }
}
private void extractTablesFromCaseWhenExpression(final CaseWhenExpression expressionSegment) {
diff --git a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/expr/QuantifySubqueryExpression.java b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/expr/QuantifySubqueryExpression.java
new file mode 100644
index 00000000000..32a91211422
--- /dev/null
+++ b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/expr/QuantifySubqueryExpression.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
+
+/**
+ * Quantify subquery expression.
+ */
+@RequiredArgsConstructor
+@Getter
+public final class QuantifySubqueryExpression implements ExpressionSegment {
+
+ private final int startIndex;
+
+ private final int stopIndex;
+
+ private final SubquerySegment subquery;
+
+ private final String quantifyOperator;
+
+ @Override
+ public String getText() {
+ return quantifyOperator + " (" + subquery.getText() + ")";
+ }
+}
diff --git a/pom.xml b/pom.xml
index 22ae415fcf9..c3a09519c51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,7 @@
<checker-qual.version>3.39.0</checker-qual.version>
<error_prone_annotations.version>2.22.0</error_prone_annotations.version>
<j2objc-annotations.version>1.3</j2objc-annotations.version>
- <calcite.version>1.39.0</calcite.version>
+ <calcite.version>1.40.0</calcite.version>
<commons-lang3.version>3.15.0</commons-lang3.version>
<commons-codec.version>1.16.0</commons-codec.version>
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
index 65e605d6776..f339cb97fd0 100644
--- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/expression/ExpressionAssert.java
@@ -34,6 +34,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.Inte
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.KeyValueSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.RowExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.TypeCastExpression;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.UnaryOperationExpression;
@@ -81,6 +82,7 @@ import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.s
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedMatchExpression;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedMultisetExpression;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedNotExpression;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedQuantifySubqueryExpression;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedRowExpression;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedTypeCastExpression;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.ExpectedUnaryOperationExpression;
@@ -479,6 +481,16 @@ public final class ExpressionAssert {
}
}
+ private static void assertQuantifySubqueryExpression(final SQLCaseAssertContext assertContext, final QuantifySubqueryExpression actual, final ExpectedQuantifySubqueryExpression expected) {
+ if (null == expected) {
+ assertNull(actual, assertContext.getText("Actual quantify subquery expression should not exist."));
+ } else {
+ assertNotNull(actual, assertContext.getText("Actual quantify subquery expression should exist."));
+ assertThat(assertContext.getText("Quantify operator assertion error: "), actual.getQuantifyOperator(), is(expected.getOperator()));
+ assertSubquery(assertContext, actual.getSubquery(), expected.getSubquery());
+ }
+ }
+
/**
* Assert expression by actual expression segment class type.
*
@@ -709,6 +721,8 @@ public final class ExpressionAssert {
assertJsonNullClauseSegment(assertContext, (JsonNullClauseSegment) actual, expected.getJsonNullClauseSegment());
} else if (actual instanceof IntervalExpression) {
assertIntervalExpression(assertContext, (IntervalExpression) actual, expected.getIntervalExpression());
+ } else if (actual instanceof QuantifySubqueryExpression) {
+ assertQuantifySubqueryExpression(assertContext, (QuantifySubqueryExpression) actual, expected.getQuantifySubqueryExpression());
} else {
throw new UnsupportedOperationException(String.format("Unsupported expression: %s", actual.getClass().getName()));
}
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
index 50e62624462..c320167320f 100644
--- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedExpression.java
@@ -116,6 +116,9 @@ public final class ExpectedExpression extends AbstractExpectedSQLSegment {
@XmlElement(name = "interval-expression")
private ExpectedIntervalExpression intervalExpression;
+ @XmlElement(name = "quantify-subquery-expression")
+ private ExpectedQuantifySubqueryExpression quantifySubqueryExpression;
+
@XmlElement(name = "multiset-expression")
private ExpectedMultisetExpression multisetExpression;
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedQuantifySubqueryExpression.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedQuantifySubqueryExpression.java
new file mode 100644
index 00000000000..d9d379fdd78
--- /dev/null
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/expr/ExpectedQuantifySubqueryExpression.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.AbstractExpectedSQLSegment;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.expr.simple.ExpectedSubquery;
+
+import javax.xml.bind.annotation.XmlElement;
+
+/**
+ * Expected quantify subquery expression.
+ */
+@Getter
+@Setter
+public final class ExpectedQuantifySubqueryExpression extends AbstractExpectedSQLSegment {
+
+ @XmlElement
+ private ExpectedSubquery subquery;
+
+ @XmlElement
+ private String operator;
+}
diff --git a/test/it/parser/src/main/resources/case/dml/select-expression.xml b/test/it/parser/src/main/resources/case/dml/select-expression.xml
index 7fb29ec6360..f40c12b919a 100644
--- a/test/it/parser/src/main/resources/case/dml/select-expression.xml
+++ b/test/it/parser/src/main/resources/case/dml/select-expression.xml
@@ -1016,30 +1016,33 @@
</left>
<operator>></operator>
<right>
- <subquery start-index="49" stop-index="98">
- <select>
- <from start-index="69" stop-index="80">
- <simple-table name="t_order_item" start-index="69" stop-index="80" />
- </from>
- <projections distinct-row="false" start-index="57" stop-index="62">
- <column-projection name="status" start-index="57" stop-index="62" />
- </projections>
- <where start-index="82" stop-index="97">
- <expr>
- <binary-operation-expression start-index="88" stop-index="97">
- <left>
- <column name="status" start-index="88" stop-index="93" />
- </left>
- <operator>></operator>
- <right>
- <literal-expression value="1" start-index="97" stop-index="97" />
- <parameter-marker-expression parameter-index="0" start-index="97" stop-index="97" />
- </right>
- </binary-operation-expression>
- </expr>
- </where>
- </select>
- </subquery>
+ <quantify-subquery-expression>
+ <operator>ALL</operator>
+ <subquery start-index="49" stop-index="98">
+ <select>
+ <from start-index="69" stop-index="80">
+ <simple-table name="t_order_item" start-index="69" stop-index="80" />
+ </from>
+ <projections distinct-row="false" start-index="57" stop-index="62">
+ <column-projection name="status" start-index="57" stop-index="62" />
+ </projections>
+ <where start-index="82" stop-index="97">
+ <expr>
+ <binary-operation-expression start-index="88" stop-index="97">
+ <left>
+ <column name="status" start-index="88" stop-index="93" />
+ </left>
+ <operator>></operator>
+ <right>
+ <literal-expression value="1" start-index="97" stop-index="97" />
+ <parameter-marker-expression parameter-index="0" start-index="97" stop-index="97" />
+ </right>
+ </binary-operation-expression>
+ </expr>
+ </where>
+ </select>
+ </subquery>
+ </quantify-subquery-expression>
</right>
</binary-operation-expression>
</expr>