You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2025/07/02 04:39:17 UTC

(shardingsphere) branch master updated: Refactor PaginationContextEngine (#35854)

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

zhangliang 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 fd9aa85dd11 Refactor PaginationContextEngine (#35854)
fd9aa85dd11 is described below

commit fd9aa85dd11b18f940d66e6269df69196727c454
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Wed Jul 2 12:39:10 2025 +0800

    Refactor PaginationContextEngine (#35854)
    
    * Refactor PaginationContextEngine
    
    * Refactor PaginationContextEngine
---
 .../impl/ShardingRemoveTokenGeneratorTest.java     |  5 +-
 .../pagination/engine/PaginationContextEngine.java | 28 ++++++----
 .../statement/type/dml/SelectStatementContext.java |  8 ++-
 .../engine/PaginationContextEngineTest.java        | 63 +++++++++++++++-------
 4 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/rewrite/token/generator/impl/ShardingRemoveTokenGeneratorTest.java b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/rewrite/token/generator/impl/ShardingRemoveTokenGeneratorTest.java
index 0705eeb20d4..805b48a5028 100644
--- a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/rewrite/token/generator/impl/ShardingRemoveTokenGeneratorTest.java
+++ b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/rewrite/token/generator/impl/ShardingRemoveTokenGeneratorTest.java
@@ -20,10 +20,12 @@ package org.apache.shardingsphere.sharding.rewrite.token.generator.impl;
 import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
 import org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
 import org.apache.shardingsphere.infra.database.core.metadata.database.enums.NullsOrderType;
+import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
 import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.rewrite.sql.token.common.pojo.SQLToken;
 import org.apache.shardingsphere.infra.rewrite.sql.token.common.pojo.generic.RemoveToken;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.sql.parser.statement.core.enums.AggregationType;
 import org.apache.shardingsphere.sql.parser.statement.core.enums.OrderDirection;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationDistinctProjectionSegment;
@@ -83,8 +85,9 @@ class ShardingRemoveTokenGeneratorTest {
         selectStatement.setProjections(createProjectionsSegment());
         ShardingSphereDatabase database = mock(ShardingSphereDatabase.class, RETURNS_DEEP_STUBS);
         when(database.getName()).thenReturn("foo_db");
+        DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
         return new SelectStatementContext(
-                mock(), selectStatement, Collections.emptyList(), new ShardingSphereMetaData(Collections.singleton(database), mock(), mock(), mock()), "foo_db", Collections.emptyList());
+                databaseType, selectStatement, Collections.emptyList(), new ShardingSphereMetaData(Collections.singleton(database), mock(), mock(), mock()), "foo_db", Collections.emptyList());
     }
     
     private ProjectionsSegment createProjectionsSegment() {
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngine.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngine.java
index 42830bdb13a..326d1860e23 100644
--- a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngine.java
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngine.java
@@ -21,10 +21,9 @@ import lombok.RequiredArgsConstructor;
 import org.apache.shardingsphere.infra.binder.context.segment.select.pagination.PaginationContext;
 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.ProjectionsContext;
 import org.apache.shardingsphere.infra.database.core.metadata.database.metadata.option.pagination.DialectPaginationOption;
-import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
-import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.limit.LimitSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.top.TopProjectionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.predicate.WhereSegment;
@@ -44,7 +43,7 @@ import java.util.Optional;
 @RequiredArgsConstructor
 public final class PaginationContextEngine {
     
-    private final DatabaseType databaseType;
+    private final DialectPaginationOption paginationOption;
     
     /**
      * Create pagination context.
@@ -61,24 +60,35 @@ public final class PaginationContextEngine {
         if (limitSegment.isPresent()) {
             return new LimitPaginationContextEngine().createPaginationContext(limitSegment.get(), params);
         }
-        Optional<TopProjectionSegment> topProjectionSegment = findTopProjection(selectStatement);
         Collection<ExpressionSegment> expressions = new LinkedList<>();
         for (WhereSegment each : whereSegments) {
             expressions.add(each.getExpr());
         }
+        Optional<TopProjectionSegment> topProjectionSegment = findTopProjection(selectStatement);
         if (topProjectionSegment.isPresent()) {
             return new TopPaginationContextEngine().createPaginationContext(topProjectionSegment.get(), expressions, params);
         }
-        if (!expressions.isEmpty()) {
-            DialectPaginationOption paginationOption = new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getPaginationOption();
-            if (paginationOption.isContainsRowNumber()) {
-                return new RowNumberPaginationContextEngine(paginationOption).createPaginationContext(expressions, projectionsContext, params);
-            }
+        if (!expressions.isEmpty() && paginationOption.isContainsRowNumber()) {
+            return new RowNumberPaginationContextEngine(paginationOption).createPaginationContext(expressions, projectionsContext, params);
         }
         return new PaginationContext(null, null, params);
     }
     
     private Optional<TopProjectionSegment> findTopProjection(final SelectStatement selectStatement) {
+        for (ProjectionSegment each : selectStatement.getProjections().getProjections()) {
+            if (each instanceof SubqueryProjectionSegment) {
+                return findTopProjection0(((SubqueryProjectionSegment) each).getSubquery().getSelect());
+            }
+        }
+        return findTopProjection0(selectStatement);
+    }
+    
+    private Optional<TopProjectionSegment> findTopProjection0(final SelectStatement selectStatement) {
+        for (ProjectionSegment each : selectStatement.getProjections().getProjections()) {
+            if (each instanceof TopProjectionSegment) {
+                return Optional.of((TopProjectionSegment) each);
+            }
+        }
         List<SubqueryTableSegment> subqueryTableSegments = selectStatement.getFrom().map(SQLUtils::getSubqueryTableSegmentFromTableSegment).orElse(Collections.emptyList());
         for (SubqueryTableSegment subquery : subqueryTableSegments) {
             SelectStatement subquerySelect = subquery.getSubquery().getSelect();
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/statement/type/dml/SelectStatementContext.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/statement/type/dml/SelectStatementContext.java
index 826a5a2f9fc..be283cb9fa0 100644
--- a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/statement/type/dml/SelectStatementContext.java
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/context/statement/type/dml/SelectStatementContext.java
@@ -39,7 +39,9 @@ import org.apache.shardingsphere.infra.binder.context.segment.select.projection.
 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.SubqueryProjection;
 import org.apache.shardingsphere.infra.binder.context.segment.table.TablesContext;
 import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import org.apache.shardingsphere.infra.database.core.metadata.database.metadata.option.pagination.DialectPaginationOption;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
+import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
 import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
 import org.apache.shardingsphere.infra.exception.dialect.exception.syntax.database.NoDatabaseSelectedException;
 import org.apache.shardingsphere.infra.exception.dialect.exception.syntax.database.UnknownDatabaseException;
@@ -130,7 +132,8 @@ public final class SelectStatementContext implements SQLStatementContext, WhereC
         groupByContext = new GroupByContextEngine().createGroupByContext(sqlStatement);
         orderByContext = new OrderByContextEngine(databaseType).createOrderBy(sqlStatement, groupByContext);
         projectionsContext = new ProjectionsContextEngine(databaseType).createProjectionsContext(sqlStatement.getProjections(), groupByContext, orderByContext);
-        paginationContext = new PaginationContextEngine(databaseType).createPaginationContext(sqlStatement, projectionsContext, params, whereSegments);
+        DialectPaginationOption paginationOption = new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getPaginationOption();
+        paginationContext = new PaginationContextEngine(paginationOption).createPaginationContext(sqlStatement, projectionsContext, params, whereSegments);
         containsEnhancedTable = isContainsEnhancedTable(metaData, tablesContext.getDatabaseNames(), currentDatabaseName);
     }
     
@@ -420,6 +423,7 @@ public final class SelectStatementContext implements SQLStatementContext, WhereC
     
     @Override
     public void setUpParameters(final List<Object> params) {
-        paginationContext = new PaginationContextEngine(getDatabaseType()).createPaginationContext(sqlStatement, projectionsContext, params, whereSegments);
+        DialectPaginationOption paginationOption = new DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getPaginationOption();
+        paginationContext = new PaginationContextEngine(paginationOption).createPaginationContext(sqlStatement, projectionsContext, params, whereSegments);
     }
 }
diff --git a/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngineTest.java b/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngineTest.java
index 29bbbd9948b..8e4887322e5 100644
--- a/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngineTest.java
+++ b/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/context/segment/select/pagination/engine/PaginationContextEngineTest.java
@@ -19,16 +19,21 @@ package org.apache.shardingsphere.infra.binder.context.segment.select.pagination
 
 import org.apache.shardingsphere.infra.binder.context.segment.select.pagination.PaginationContext;
 import org.apache.shardingsphere.infra.binder.context.segment.select.projection.ProjectionsContext;
-import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
-import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.infra.database.core.metadata.database.metadata.option.pagination.DialectPaginationOption;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionsSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.limit.LimitSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.limit.LimitValueSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.limit.NumberLiteralLimitValueSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.rownum.NumberLiteralRowNumberValueSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.top.TopProjectionSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.predicate.WhereSegment;
 import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 import org.junit.jupiter.api.Test;
 
 import java.util.Collections;
@@ -42,53 +47,71 @@ import static org.mockito.Mockito.mock;
 class PaginationContextEngineTest {
     
     @Test
-    void assertCreatePaginationContextWhenLimitSegmentIsPresent() {
+    void assertCreatePaginationContextWithLimitSegment() {
         SelectStatement selectStatement = new SelectStatement();
         selectStatement.setLimit(new LimitSegment(0, 10, new NumberLiteralLimitValueSegment(0, 10, 100L),
-                new NumberLiteralLimitValueSegment(0, 10, 100L)));
-        PaginationContext paginationContext = new PaginationContextEngine(getDatabaseType("SQL92")).createPaginationContext(
-                selectStatement, mock(ProjectionsContext.class), Collections.emptyList(), Collections.emptyList());
+                new NumberLiteralLimitValueSegment(11, 20, 200L)));
+        PaginationContext paginationContext = new PaginationContextEngine(
+                new DialectPaginationOption(false, "")).createPaginationContext(selectStatement, mock(ProjectionsContext.class), Collections.emptyList(), Collections.emptyList());
         assertTrue(paginationContext.getOffsetSegment().isPresent());
+        assertThat(paginationContext.getOffsetSegment().get(), instanceOf(LimitValueSegment.class));
         assertTrue(paginationContext.getRowCountSegment().isPresent());
+        assertThat(paginationContext.getRowCountSegment().get(), instanceOf(LimitValueSegment.class));
     }
     
     @Test
-    void assertCreatePaginationContextWhenLimitSegmentAbsentAndTopSegmentPresent() {
+    void assertCreatePaginationContextWithTopSegment() {
         SelectStatement subquerySelectStatement = new SelectStatement();
         subquerySelectStatement.setProjections(new ProjectionsSegment(0, 0));
-        subquerySelectStatement.getProjections().getProjections().add(new TopProjectionSegment(0, 10, null, "rowNumberAlias"));
+        subquerySelectStatement.getProjections().getProjections().add(new TopProjectionSegment(0, 10, null, ""));
         SelectStatement selectStatement = new SelectStatement();
         selectStatement.setProjections(new ProjectionsSegment(0, 0));
         selectStatement.getProjections().getProjections().add(new SubqueryProjectionSegment(new SubquerySegment(0, 0, subquerySelectStatement, ""), ""));
-        PaginationContext paginationContext = new PaginationContextEngine(getDatabaseType("SQLServer")).createPaginationContext(
-                selectStatement, mock(ProjectionsContext.class), Collections.emptyList(), Collections.emptyList());
+        PaginationContext paginationContext = new PaginationContextEngine(
+                new DialectPaginationOption(false, "")).createPaginationContext(selectStatement, mock(ProjectionsContext.class), Collections.emptyList(), Collections.emptyList());
         assertFalse(paginationContext.getOffsetSegment().isPresent());
         assertFalse(paginationContext.getRowCountSegment().isPresent());
     }
     
     @Test
-    void assertCreatePaginationContextWhenLimitSegmentTopSegmentAbsentAndWhereSegmentPresent() {
+    void assertCreatePaginationContextWithWhereAndRowNumberSegment() {
         SelectStatement selectStatement = new SelectStatement();
         selectStatement.setProjections(new ProjectionsSegment(0, 0));
-        WhereSegment where = new WhereSegment(0, 10, null);
+        BinaryOperationExpression binaryOperationExpr = new BinaryOperationExpression(
+                0, 5, new ColumnSegment(0, 5, new IdentifierValue("ROW_NUMBER")), new LiteralExpressionSegment(5, 10, 100), "<", "");
+        WhereSegment where = new WhereSegment(0, 10, binaryOperationExpr);
         selectStatement.setWhere(where);
         ProjectionsContext projectionsContext = new ProjectionsContext(0, 0, false, Collections.emptyList());
-        PaginationContext paginationContext = new PaginationContextEngine(getDatabaseType("SQLServer")).createPaginationContext(
-                selectStatement, projectionsContext, Collections.emptyList(), Collections.singletonList(where));
+        PaginationContext paginationContext = new PaginationContextEngine(
+                new DialectPaginationOption(true, "ROW_NUMBER")).createPaginationContext(selectStatement, projectionsContext, Collections.emptyList(), Collections.singletonList(where));
         assertFalse(paginationContext.getOffsetSegment().isPresent());
-        assertFalse(paginationContext.getRowCountSegment().isPresent());
+        assertTrue(paginationContext.getRowCountSegment().isPresent());
+        assertThat(paginationContext.getRowCountSegment().get(), instanceOf(NumberLiteralRowNumberValueSegment.class));
     }
     
     @Test
-    void assertCreatePaginationContextWhenResultIsPaginationContext() {
+    void assertCreatePaginationContextWithWhereAndWithoutRowNumberSegment() {
         SelectStatement selectStatement = new SelectStatement();
         selectStatement.setProjections(new ProjectionsSegment(0, 0));
+        BinaryOperationExpression binaryOperationExpr = new BinaryOperationExpression(
+                0, 5, new ColumnSegment(0, 5, new IdentifierValue("ROW_NUMBER")), new LiteralExpressionSegment(5, 10, 100), "<", "");
+        WhereSegment where = new WhereSegment(0, 10, binaryOperationExpr);
+        selectStatement.setWhere(where);
         ProjectionsContext projectionsContext = new ProjectionsContext(0, 0, false, Collections.emptyList());
-        assertThat(new PaginationContextEngine(getDatabaseType("SQL92")).createPaginationContext(
-                selectStatement, projectionsContext, Collections.emptyList(), Collections.emptyList()), instanceOf(PaginationContext.class));
+        PaginationContext paginationContext = new PaginationContextEngine(
+                new DialectPaginationOption(false, "")).createPaginationContext(selectStatement, projectionsContext, Collections.emptyList(), Collections.singletonList(where));
+        assertFalse(paginationContext.getOffsetSegment().isPresent());
+        assertFalse(paginationContext.getRowCountSegment().isPresent());
     }
     
-    private DatabaseType getDatabaseType(final String databaseType) {
-        return TypedSPILoader.getService(DatabaseType.class, databaseType);
+    @Test
+    void assertCreatePaginationContextWithEmpty() {
+        SelectStatement selectStatement = new SelectStatement();
+        selectStatement.setProjections(new ProjectionsSegment(0, 0));
+        ProjectionsContext projectionsContext = new ProjectionsContext(0, 0, false, Collections.emptyList());
+        PaginationContext paginationContext = new PaginationContextEngine(
+                new DialectPaginationOption(false, "")).createPaginationContext(selectStatement, projectionsContext, Collections.emptyList(), Collections.emptyList());
+        assertFalse(paginationContext.getOffsetSegment().isPresent());
+        assertFalse(paginationContext.getRowCountSegment().isPresent());
     }
 }