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 56117f45091 Support select with statement sql bind and add bind test case (#34141) 56117f45091 is described below commit 56117f45091aefdb26dec82a96c039c462d8f9bb Author: Zhengqiang Duan <duanzhengqi...@apache.org> AuthorDate: Wed Dec 25 07:42:30 2024 +0800 Support select with statement sql bind and add bind test case (#34141) * Remove TablesContext#findTableNames method and use sql bind info to replace * fix unit test * optimize select statement binder * update release note * Support select with statement sql bind and add bind test case * update release note * fix build error --- RELEASE-NOTES.md | 1 + .../sql/EncryptSupportedSQLCheckersBuilder.java | 4 +- .../from/type/SimpleTableSegmentBinder.java | 7 + .../with/CommonTableExpressionSegmentBinder.java | 62 +++++++ .../engine/segment/with/WithSegmentBinder.java | 125 +++++++++++++ .../statement/dml/SelectStatementBinder.java | 4 +- .../statement/dml/UpdateStatementBinder.java | 3 +- .../binder/src/test/resources/cases/dml/select.xml | 206 +++++++++++++++++++++ .../binder/src/test/resources/sqls/dml/select.xml | 2 + .../asserts/segment/orderby/OrderByItemAssert.java | 2 + .../item/impl/ExpectedColumnOrderByItem.java | 4 + 11 files changed, 415 insertions(+), 5 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 318dd2e0892..37fad67b255 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -38,6 +38,7 @@ 1. SQL Binder: Support create index statement sql bind - [#34112](https://github.com/apache/shardingsphere/pull/34112) 1. SQL Parser: Support MySQL update with statement parse - [#34126](https://github.com/apache/shardingsphere/pull/34126) 1. SQL Binder: Remove TablesContext#findTableNames method and implement select order by, group by bind logic - [#34123](https://github.com/apache/shardingsphere/pull/34123) +1. SQL Binder: Support select with statement sql bind and add bind test case - [#34141](https://github.com/apache/shardingsphere/pull/34141) ### Bug Fixes diff --git a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java index 7a661c2e926..fce03c66f10 100644 --- a/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java +++ b/features/encrypt/core/src/main/java/org/apache/shardingsphere/encrypt/checker/sql/EncryptSupportedSQLCheckersBuilder.java @@ -17,10 +17,10 @@ package org.apache.shardingsphere.encrypt.checker.sql; -import org.apache.shardingsphere.encrypt.checker.sql.projection.EncryptInsertSelectProjectionSupportedChecker; -import org.apache.shardingsphere.encrypt.checker.sql.projection.EncryptSelectProjectionSupportedChecker; import org.apache.shardingsphere.encrypt.checker.sql.orderby.EncryptOrderByItemSupportedChecker; import org.apache.shardingsphere.encrypt.checker.sql.predicate.EncryptPredicateColumnSupportedChecker; +import org.apache.shardingsphere.encrypt.checker.sql.projection.EncryptInsertSelectProjectionSupportedChecker; +import org.apache.shardingsphere.encrypt.checker.sql.projection.EncryptSelectProjectionSupportedChecker; import org.apache.shardingsphere.encrypt.constant.EncryptOrder; import org.apache.shardingsphere.encrypt.rule.EncryptRule; import org.apache.shardingsphere.infra.checker.SupportedSQLChecker; diff --git a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/from/type/SimpleTableSegmentBinder.java b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/from/type/SimpleTableSegmentBinder.java index eb57d97f828..d471cb8cf01 100644 --- a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/from/type/SimpleTableSegmentBinder.java +++ b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/from/type/SimpleTableSegmentBinder.java @@ -24,6 +24,7 @@ import lombok.NoArgsConstructor; import org.apache.shardingsphere.infra.binder.engine.segment.from.context.TableSegmentBinderContext; import org.apache.shardingsphere.infra.binder.engine.segment.from.context.type.SimpleTableSegmentBinderContext; import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext; +import org.apache.shardingsphere.infra.binder.engine.util.SubqueryTableBindUtils; import org.apache.shardingsphere.infra.database.core.metadata.database.DialectDatabaseMetaData; import org.apache.shardingsphere.infra.database.core.metadata.database.enums.QuoteCharacter; import org.apache.shardingsphere.infra.database.core.type.DatabaseType; @@ -150,6 +151,12 @@ public final class SimpleTableSegmentBinder { if (binderContext.getSqlStatement() instanceof CreateTableStatement) { return new SimpleTableSegmentBinderContext(createProjectionSegments((CreateTableStatement) binderContext.getSqlStatement(), databaseName, schemaName, tableName)); } + CaseInsensitiveString caseInsensitiveTableName = new CaseInsensitiveString(tableName.getValue()); + if (binderContext.getExternalTableBinderContexts().containsKey(caseInsensitiveTableName)) { + TableSegmentBinderContext tableSegmentBinderContext = binderContext.getExternalTableBinderContexts().get(caseInsensitiveTableName).iterator().next(); + return new SimpleTableSegmentBinderContext( + SubqueryTableBindUtils.createSubqueryProjections(tableSegmentBinderContext.getProjectionSegments(), tableName, binderContext.getSqlStatement().getDatabaseType())); + } return new SimpleTableSegmentBinderContext(Collections.emptyList()); } diff --git a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/with/CommonTableExpressionSegmentBinder.java b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/with/CommonTableExpressionSegmentBinder.java new file mode 100644 index 00000000000..136c33e7fbd --- /dev/null +++ b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/with/CommonTableExpressionSegmentBinder.java @@ -0,0 +1,62 @@ +/* + * 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.infra.binder.engine.segment.with; + +import com.cedarsoftware.util.CaseInsensitiveMap.CaseInsensitiveString; +import com.google.common.collect.LinkedHashMultimap; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.shardingsphere.infra.binder.engine.segment.from.context.type.SimpleTableSegmentBinderContext; +import org.apache.shardingsphere.infra.binder.engine.segment.from.type.SubqueryTableSegmentBinder; +import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext; +import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonTableExpressionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ColumnProjectionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment; + +import java.util.stream.Collectors; + +/** + * Common table expression segment binder. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class CommonTableExpressionSegmentBinder { + + /** + * Bind common table expression segment. + * + * @param segment common table expression segment + * @param binderContext SQL statement binder context + * @param recursive recursive + * @return bound common table expression segment + */ + public static CommonTableExpressionSegment bind(final CommonTableExpressionSegment segment, final SQLStatementBinderContext binderContext, final boolean recursive) { + if (recursive && segment.getAliasName().isPresent()) { + binderContext.getExternalTableBinderContexts().put(new CaseInsensitiveString(segment.getAliasName().get()), + new SimpleTableSegmentBinderContext(segment.getColumns().stream().map(ColumnProjectionSegment::new).collect(Collectors.toList()))); + } + SubqueryTableSegment subqueryTableSegment = new SubqueryTableSegment(segment.getStartIndex(), segment.getStopIndex(), segment.getSubquery()); + subqueryTableSegment.setAlias(segment.getAliasSegment()); + SubqueryTableSegment boundSubquerySegment = + SubqueryTableSegmentBinder.bind(subqueryTableSegment, binderContext, LinkedHashMultimap.create(), binderContext.getExternalTableBinderContexts()); + CommonTableExpressionSegment result = new CommonTableExpressionSegment( + segment.getStartIndex(), segment.getStopIndex(), boundSubquerySegment.getAliasSegment().orElse(null), boundSubquerySegment.getSubquery()); + // TODO bind with columns + result.getColumns().addAll(segment.getColumns()); + return result; + } +} diff --git a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/with/WithSegmentBinder.java b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/with/WithSegmentBinder.java new file mode 100644 index 00000000000..ca4af9da753 --- /dev/null +++ b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/with/WithSegmentBinder.java @@ -0,0 +1,125 @@ +/* + * 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.infra.binder.engine.segment.with; + +import com.cedarsoftware.util.CaseInsensitiveMap; +import com.cedarsoftware.util.CaseInsensitiveMap.CaseInsensitiveString; +import com.google.common.base.Strings; +import com.google.common.collect.Multimap; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.apache.shardingsphere.infra.binder.engine.segment.from.context.TableSegmentBinderContext; +import org.apache.shardingsphere.infra.binder.engine.segment.from.context.type.SimpleTableSegmentBinderContext; +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.expr.complex.CommonTableExpressionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ColumnProjectionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ShorthandProjectionSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WithSegment; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.ColumnSegmentBoundInfo; +import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.TableSegmentBoundInfo; +import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.Map; +import java.util.Optional; + +/** + * With segment binder. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class WithSegmentBinder { + + /** + * Bind with segment. + * + * @param segment with segment + * @param binderContext SQL statement binder context + * @param externalTableBinderContexts external table binder contexts + * @return bound with segment + */ + public static WithSegment bind(final WithSegment segment, final SQLStatementBinderContext binderContext, + final Multimap<CaseInsensitiveString, TableSegmentBinderContext> externalTableBinderContexts) { + Collection<CommonTableExpressionSegment> boundCommonTableExpressions = new LinkedList<>(); + for (CommonTableExpressionSegment each : segment.getCommonTableExpressions()) { + CommonTableExpressionSegment boundCommonTableExpression = CommonTableExpressionSegmentBinder.bind(each, binderContext, segment.isRecursive()); + boundCommonTableExpressions.add(boundCommonTableExpression); + if (segment.isRecursive() && each.getAliasName().isPresent()) { + externalTableBinderContexts.removeAll(new CaseInsensitiveString(each.getAliasName().get())); + } + bindWithColumns(each.getColumns(), boundCommonTableExpression); + each.getAliasName().ifPresent(optional -> externalTableBinderContexts.put(new CaseInsensitiveString(optional), createWithTableBinderContext(boundCommonTableExpression))); + } + return new WithSegment(segment.getStartIndex(), segment.getStopIndex(), boundCommonTableExpressions); + } + + private static SimpleTableSegmentBinderContext createWithTableBinderContext(final CommonTableExpressionSegment commonTableExpressionSegment) { + return new SimpleTableSegmentBinderContext(commonTableExpressionSegment.getSubquery().getSelect().getProjections().getProjections()); + } + + private static void bindWithColumns(final Collection<ColumnSegment> columns, final CommonTableExpressionSegment boundCommonTableExpression) { + if (columns.isEmpty()) { + return; + } + Map<String, ColumnProjectionSegment> columnProjections = extractWithSubqueryColumnProjections(boundCommonTableExpression); + columns.forEach(each -> { + ColumnProjectionSegment projectionSegment = columnProjections.get(each.getIdentifier().getValue()); + if (null != projectionSegment) { + each.setColumnBoundInfo(createColumnSegmentBoundInfo(each, projectionSegment.getColumn())); + } + }); + } + + private static Map<String, ColumnProjectionSegment> extractWithSubqueryColumnProjections(final CommonTableExpressionSegment boundCommonTableExpression) { + Map<String, ColumnProjectionSegment> result = new CaseInsensitiveMap<>(); + Collection<ProjectionSegment> projections = boundCommonTableExpression.getSubquery().getSelect().getProjections().getProjections(); + projections.forEach(each -> extractWithSubqueryColumnProjections(each, result)); + return result; + } + + private static void extractWithSubqueryColumnProjections(final ProjectionSegment projectionSegment, final Map<String, ColumnProjectionSegment> result) { + if (projectionSegment instanceof ColumnProjectionSegment) { + result.put(getColumnName((ColumnProjectionSegment) projectionSegment), (ColumnProjectionSegment) projectionSegment); + } + if (projectionSegment instanceof ShorthandProjectionSegment) { + ((ShorthandProjectionSegment) projectionSegment).getActualProjectionSegments().forEach(eachProjection -> { + if (eachProjection instanceof ColumnProjectionSegment) { + result.put(getColumnName((ColumnProjectionSegment) eachProjection), (ColumnProjectionSegment) eachProjection); + } + }); + } + } + + private static String getColumnName(final ColumnProjectionSegment columnProjection) { + return columnProjection.getAliasName().orElse(columnProjection.getColumn().getIdentifier().getValue()); + } + + private static ColumnSegmentBoundInfo createColumnSegmentBoundInfo(final ColumnSegment segment, final ColumnSegment inputColumnSegment) { + IdentifierValue originalDatabase = null == inputColumnSegment ? null : inputColumnSegment.getColumnBoundInfo().getOriginalDatabase(); + IdentifierValue originalSchema = null == inputColumnSegment ? null : inputColumnSegment.getColumnBoundInfo().getOriginalSchema(); + IdentifierValue segmentOriginalTable = segment.getColumnBoundInfo().getOriginalTable(); + IdentifierValue originalTable = Strings.isNullOrEmpty(segmentOriginalTable.getValue()) + ? Optional.ofNullable(inputColumnSegment).map(optional -> optional.getColumnBoundInfo().getOriginalTable()).orElse(segmentOriginalTable) + : segmentOriginalTable; + IdentifierValue segmentOriginalColumn = segment.getColumnBoundInfo().getOriginalColumn(); + IdentifierValue originalColumn = Optional.ofNullable(inputColumnSegment).map(optional -> optional.getColumnBoundInfo().getOriginalColumn()).orElse(segmentOriginalColumn); + return new ColumnSegmentBoundInfo(new TableSegmentBoundInfo(originalDatabase, originalSchema), originalTable, originalColumn); + } +} diff --git a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinder.java b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinder.java index 521d949a899..3f0cf8244b2 100644 --- a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinder.java +++ b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinder.java @@ -32,6 +32,7 @@ import org.apache.shardingsphere.infra.binder.engine.segment.order.OrderBySegmen import org.apache.shardingsphere.infra.binder.engine.segment.predicate.HavingSegmentBinder; import org.apache.shardingsphere.infra.binder.engine.segment.predicate.WhereSegmentBinder; import org.apache.shardingsphere.infra.binder.engine.segment.projection.ProjectionsSegmentBinder; +import org.apache.shardingsphere.infra.binder.engine.segment.with.WithSegmentBinder; import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinder; import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext; import org.apache.shardingsphere.infra.binder.engine.util.SubqueryTableBindUtils; @@ -59,6 +60,7 @@ public final class SelectStatementBinder implements SQLStatementBinder<SelectSta public SelectStatement bind(final SelectStatement sqlStatement, final SQLStatementBinderContext binderContext) { SelectStatement result = copy(sqlStatement); Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts = LinkedHashMultimap.create(); + sqlStatement.getWithSegment().ifPresent(optional -> result.setWithSegment(WithSegmentBinder.bind(optional, binderContext, binderContext.getExternalTableBinderContexts()))); Optional<TableSegment> boundTableSegment = sqlStatement.getFrom().map(optional -> TableSegmentBinder.bind(optional, binderContext, tableBinderContexts, outerTableBinderContexts)); boundTableSegment.ifPresent(result::setFrom); result.setProjections(ProjectionsSegmentBinder.bind(sqlStatement.getProjections(), binderContext, boundTableSegment.orElse(null), tableBinderContexts, outerTableBinderContexts)); @@ -71,7 +73,6 @@ public final class SelectStatementBinder implements SQLStatementBinder<SelectSta sqlStatement.getOrderBy().ifPresent(optional -> result.setOrderBy( OrderBySegmentBinder.bind(optional, binderContext, currentTableBinderContexts, tableBinderContexts, outerTableBinderContexts))); sqlStatement.getHaving().ifPresent(optional -> result.setHaving(HavingSegmentBinder.bind(optional, binderContext, currentTableBinderContexts, outerTableBinderContexts))); - // TODO support other segment bind in select statement return result; } @@ -90,7 +91,6 @@ public final class SelectStatementBinder implements SQLStatementBinder<SelectSta sqlStatement.getWindow().ifPresent(result::setWindow); sqlStatement.getModelSegment().ifPresent(result::setModelSegment); sqlStatement.getSubqueryType().ifPresent(result::setSubqueryType); - sqlStatement.getWithSegment().ifPresent(result::setWithSegment); result.addParameterMarkerSegments(sqlStatement.getParameterMarkerSegments()); result.getCommentSegments().addAll(sqlStatement.getCommentSegments()); result.getVariableNames().addAll(sqlStatement.getVariableNames()); diff --git a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/UpdateStatementBinder.java b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/UpdateStatementBinder.java index 1de69236fdd..ba4744cc5db 100644 --- a/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/UpdateStatementBinder.java +++ b/infra/binder/src/main/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/UpdateStatementBinder.java @@ -25,6 +25,7 @@ import org.apache.shardingsphere.infra.binder.engine.segment.assign.AssignmentSe import org.apache.shardingsphere.infra.binder.engine.segment.from.TableSegmentBinder; import org.apache.shardingsphere.infra.binder.engine.segment.from.context.TableSegmentBinderContext; import org.apache.shardingsphere.infra.binder.engine.segment.predicate.WhereSegmentBinder; +import org.apache.shardingsphere.infra.binder.engine.segment.with.WithSegmentBinder; import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinder; import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext; import org.apache.shardingsphere.sql.parser.statement.core.statement.dml.UpdateStatement; @@ -38,6 +39,7 @@ public final class UpdateStatementBinder implements SQLStatementBinder<UpdateSta public UpdateStatement bind(final UpdateStatement sqlStatement, final SQLStatementBinderContext binderContext) { UpdateStatement result = copy(sqlStatement); Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts = LinkedHashMultimap.create(); + sqlStatement.getWithSegment().ifPresent(optional -> result.setWithSegment(WithSegmentBinder.bind(optional, binderContext, binderContext.getExternalTableBinderContexts()))); result.setTable(TableSegmentBinder.bind(sqlStatement.getTable(), binderContext, tableBinderContexts, LinkedHashMultimap.create())); sqlStatement.getFrom().ifPresent(optional -> result.setFrom(TableSegmentBinder.bind(optional, binderContext, tableBinderContexts, LinkedHashMultimap.create()))); sqlStatement.getAssignmentSegment().ifPresent(optional -> result.setSetAssignment(AssignmentSegmentBinder.bind(optional, binderContext, tableBinderContexts, LinkedHashMultimap.create()))); @@ -50,7 +52,6 @@ public final class UpdateStatementBinder implements SQLStatementBinder<UpdateSta UpdateStatement result = sqlStatement.getClass().getDeclaredConstructor().newInstance(); sqlStatement.getOrderBy().ifPresent(result::setOrderBy); sqlStatement.getLimit().ifPresent(result::setLimit); - sqlStatement.getWithSegment().ifPresent(result::setWithSegment); result.addParameterMarkerSegments(sqlStatement.getParameterMarkerSegments()); result.getCommentSegments().addAll(sqlStatement.getCommentSegments()); return result; diff --git a/test/it/binder/src/test/resources/cases/dml/select.xml b/test/it/binder/src/test/resources/cases/dml/select.xml index 7f03799f5f7..8caabdcb4d9 100644 --- a/test/it/binder/src/test/resources/cases/dml/select.xml +++ b/test/it/binder/src/test/resources/cases/dml/select.xml @@ -87,4 +87,210 @@ </simple-table> </from> </select> + + <select sql-case-id="select_with_group_by_order_by"> + <projections start-index="7" stop-index="30"> + <column-projection name="order_id" start-index="7" stop-index="14"> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="order_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <aggregation-projection type="COUNT" expression="COUNT(1)" start-index="17" stop-index="24" alias="count"> + <parameter> + <literal-expression value="1" start-index="23" stop-index="23" /> + </parameter> + </aggregation-projection> + </projections> + <from start-index="37" stop-index="45"> + <simple-table name="t_order" start-index="37" stop-index="45" alias="o"> + <table-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + </table-bound> + </simple-table> + </from> + <group-by> + <column-item name="order_id" start-index="56" stop-index="63" > + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="order_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-item> + </group-by> + <having start-index="65" stop-index="80"> + <expr> + <binary-operation-expression start-index="72" stop-index="80"> + <left> + <column name="count" start-index="72" stop-index="76" /> + </left> + <operator>></operator> + <right> + <literal-expression start-index="80" stop-index="80" value="1" /> + </right> + </binary-operation-expression> + </expr> + </having> + <order-by> + <column-item name="order_id" start-index="91" stop-index="98"> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="order_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-item> + </order-by> + </select> + + <select sql-case-id="select_with_with_clause"> + <with start-index="0" stop-index="44"> + <common-table-expression name="t_order_tmp" start-index="5" stop-index="44"> + <subquery-expression start-index="5" stop-index="44"> + <select> + <projections start-index="28" stop-index="28"> + <shorthand-projection start-index="28" stop-index="28"> + <actual-projections> + <column-projection name="order_id" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="o" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="order_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="user_id" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="o" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="user_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="status" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="o" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="status" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="merchant_id" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="o" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="merchant_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="remark" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="o" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="remark" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="creation_date" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="o" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="creation_date" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + </actual-projections> + </shorthand-projection> + </projections> + <from> + <simple-table name="t_order" alias="o" start-index="35" stop-index="43"> + <table-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + </table-bound> + </simple-table> + </from> + </select> + </subquery-expression> + </common-table-expression> + </with> + <projections start-index="53" stop-index="53"> + <shorthand-projection start-index="53" stop-index="53"> + <actual-projections> + <column-projection name="order_id" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="t_order_tmp" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="order_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="user_id" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="t_order_tmp" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="user_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="status" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="t_order_tmp" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="status" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="merchant_id" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="t_order_tmp" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="merchant_id" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="remark" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="t_order_tmp" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="remark" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + <column-projection name="creation_date" start-index="0" stop-index="0" start-delimiter="`" end-delimiter="`"> + <owner name="t_order_tmp" start-index="0" stop-index="0" /> + <column-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + <original-table name="t_order" /> + <original-column name="creation_date" start-delimiter="`" end-delimiter="`" /> + </column-bound> + </column-projection> + </actual-projections> + </shorthand-projection> + </projections> + <from start-index="55" stop-index="70"> + <simple-table name="t_order_tmp" start-index="60" stop-index="70"> + <table-bound> + <original-database name="foo_db_1" /> + <original-schema name="foo_db_1" /> + </table-bound> + </simple-table> + </from> + </select> </sql-parser-test-cases> diff --git a/test/it/binder/src/test/resources/sqls/dml/select.xml b/test/it/binder/src/test/resources/sqls/dml/select.xml index aa09790e7ad..cca532f1c5f 100644 --- a/test/it/binder/src/test/resources/sqls/dml/select.xml +++ b/test/it/binder/src/test/resources/sqls/dml/select.xml @@ -18,4 +18,6 @@ <sql-cases> <sql-case id="select_with_shorthand_projection" value="SELECT * FROM t_order o" db-types="MySQL"/> + <sql-case id="select_with_group_by_order_by" value="SELECT order_id, COUNT(1) count FROM t_order o GROUP BY order_id HAVING count > 1 ORDER BY order_id" db-types="MySQL"/> + <sql-case id="select_with_with_clause" value="WITH t_order_tmp AS (SELECT * FROM t_order o) SELECT * FROM t_order_tmp" db-types="MySQL"/> </sql-cases> diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java index af1be821b57..6a6df5f5aab 100644 --- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java +++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java @@ -25,6 +25,7 @@ import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.ite import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.OrderByItemSegment; import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext; import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.SQLSegmentAssert; +import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.bound.ColumnBoundAssert; import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.expression.ExpressionAssert; import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.identifier.IdentifierValueAssert; import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.owner.OwnerAssert; @@ -106,6 +107,7 @@ public final class OrderByItemAssert { private static void assertColumnOrderByItem(final SQLCaseAssertContext assertContext, final ColumnOrderByItemSegment actual, final ExpectedColumnOrderByItem expected, final String type) { IdentifierValueAssert.assertIs(assertContext, actual.getColumn().getIdentifier(), expected, String.format("%s item", type)); + ColumnBoundAssert.assertIs(assertContext, actual.getColumn().getColumnBoundInfo(), expected.getColumnBound()); if (null == expected.getOwner()) { assertFalse(actual.getColumn().getOwner().isPresent(), assertContext.getText("Actual owner should not exist.")); } else { diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedColumnOrderByItem.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedColumnOrderByItem.java index f150058e9f4..f000d2b5234 100644 --- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedColumnOrderByItem.java +++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedColumnOrderByItem.java @@ -20,6 +20,7 @@ package org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb. import lombok.Getter; import lombok.Setter; import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.ExpectedIdentifierSQLSegment; +import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.bound.ExpectedColumnBoundInfo; import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.orderby.item.ExpectedOrderByItem; import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.table.ExpectedOwner; @@ -38,4 +39,7 @@ public final class ExpectedColumnOrderByItem extends ExpectedOrderByItem impleme @XmlElement private ExpectedOwner owner; + + @XmlElement(name = "column-bound") + private ExpectedColumnBoundInfo columnBound; }