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

strongduanmu 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 b866ef37ec3 Fix SQL Federation pagination binding for long LIMIT 
parameters (#39237)
b866ef37ec3 is described below

commit b866ef37ec39ba12b145fa31ec5bfab98dc2c97d
Author: 林桉 <[email protected]>
AuthorDate: Thu Jul 30 19:19:35 2026 +0800

    Fix SQL Federation pagination binding for long LIMIT parameters (#39237)
    
    * sql-federation: support long pagination parameters
    
    * sql-federation: fix execution plan bindable entry
    
    * sql-federation: fix nested pagination parameter binding
---
 .../impl/StandardSQLFederationProcessor.java       |  53 +++++-
 .../impl/StandardSQLFederationProcessorTest.java   | 191 ++++++++++++++++++++-
 ...group-by-order-by-for-db-tbl-sql-federation.xml |   5 +
 3 files changed, 237 insertions(+), 12 deletions(-)

diff --git 
a/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessor.java
 
b/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessor.java
index 039d6b08cfe..96fe9cf3163 100644
--- 
a/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessor.java
+++ 
b/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessor.java
@@ -28,6 +28,7 @@ import org.apache.calcite.schema.Table;
 import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
+import 
org.apache.shardingsphere.infra.binder.context.segment.select.pagination.PaginationContext;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
 import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
@@ -52,11 +53,14 @@ import 
org.apache.shardingsphere.sqlfederation.executor.context.ExecutorContext;
 import 
org.apache.shardingsphere.sqlfederation.executor.enumerable.implementor.EnumerableScanImplementor;
 import 
org.apache.shardingsphere.sqlfederation.resultset.SQLFederationResultSet;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -66,6 +70,10 @@ import java.util.Map;
 @RequiredArgsConstructor
 public final class StandardSQLFederationProcessor implements 
SQLFederationProcessor {
     
+    private static final BigInteger MIN_PAGINATION_PARAMETER = 
BigInteger.valueOf(Integer.MIN_VALUE);
+    
+    private static final BigInteger MAX_PAGINATION_PARAMETER = 
BigInteger.valueOf(Integer.MAX_VALUE);
+    
     private final ShardingSphereStatistics statistics;
     
     private final JDBCExecutor jdbcExecutor;
@@ -127,12 +135,11 @@ public final class StandardSQLFederationProcessor 
implements SQLFederationProces
     
     @Override
     public ResultSet executePlan(final 
DriverExecutionPrepareEngine<JDBCExecutionUnit, Connection> prepareEngine, 
final JDBCExecutorCallback<? extends ExecuteResult> queryCallback,
-                                 final SQLFederationExecutionPlan 
executionPlan, final SQLFederationRelConverter converter,
-                                 final SQLFederationContext federationContext, 
final SchemaPlus schemaPlus) {
+                                 final SQLFederationExecutionPlan 
executionPlan, final SQLFederationRelConverter converter, final 
SQLFederationContext federationContext, final SchemaPlus schemaPlus) {
         Bindable<Object> executablePlan = 
SQLFederationExecutionPlan.toBindable(executionPlan.getPhysicalPlan(), 
Collections.emptyMap(), null, Prefer.ARRAY);
-        Map<String, Object> params = 
createParameters(federationContext.getQueryContext().getParameters());
-        Enumerator<Object> enumerator = executablePlan.bind(new 
ExecutorBindContext(converter, params)).enumerator();
         SelectStatementContext selectStatementContext = 
(SelectStatementContext) 
federationContext.getQueryContext().getSqlStatementContext();
+        Map<String, Object> params = createParameters(selectStatementContext, 
federationContext.getQueryContext().getParameters());
+        Enumerator<Object> enumerator = executablePlan.bind(new 
ExecutorBindContext(converter, params)).enumerator();
         List<Projection> expandProjections = 
selectStatementContext.getProjectionsContext().getExpandProjections();
         SQLFederationResultSet result = new SQLFederationResultSet(enumerator, 
schemaPlus, expandProjections,
                 selectStatementContext.getSqlStatement().getDatabaseType(), 
executionPlan.getResultColumnType(), federationContext.getProcessId());
@@ -142,15 +149,49 @@ public final class StandardSQLFederationProcessor 
implements SQLFederationProces
         return result;
     }
     
-    private Map<String, Object> createParameters(final List<Object> params) {
+    private Map<String, Object> createParameters(final SelectStatementContext 
selectStatementContext, final List<Object> params) {
+        Collection<Integer> paginationParameterIndexes = 
getPaginationParameterIndexes(selectStatementContext);
         Map<String, Object> result = new HashMap<>(params.size(), 1F);
         int index = 0;
         for (Object each : params) {
-            result.put("?" + index++, each);
+            result.put("?" + index, paginationParameterIndexes.contains(index) 
? convertPaginationParameter(each) : each);
+            index++;
+        }
+        return result;
+    }
+    
+    private Collection<Integer> getPaginationParameterIndexes(final 
SelectStatementContext selectStatementContext) {
+        Collection<Integer> result = new LinkedList<>();
+        collectPaginationParameterIndexes(result, 
selectStatementContext.getPaginationContext());
+        for (SelectStatementContext each : 
selectStatementContext.getSubqueryContexts().values()) {
+            result.addAll(getPaginationParameterIndexes(each));
         }
         return result;
     }
     
+    private void collectPaginationParameterIndexes(final Collection<Integer> 
result, final PaginationContext paginationContext) {
+        paginationContext.getOffsetParameterIndex().ifPresent(result::add);
+        paginationContext.getRowCountParameterIndex().ifPresent(result::add);
+    }
+    
+    private Object convertPaginationParameter(final Object value) {
+        if (!(value instanceof Number)) {
+            return value;
+        }
+        BigInteger integerValue = getIntegerValue((Number) value);
+        
ShardingSpherePreconditions.checkState(integerValue.compareTo(MIN_PAGINATION_PARAMETER)
 >= 0 && integerValue.compareTo(MAX_PAGINATION_PARAMETER) <= 0,
+                () -> new IllegalArgumentException(String.format("SQL 
federation pagination parameter value `%s` is out of integer range.", value)));
+        return integerValue.intValue();
+    }
+    
+    private BigInteger getIntegerValue(final Number value) {
+        try {
+            return new BigDecimal(value.toString()).toBigIntegerExact();
+        } catch (final NumberFormatException | ArithmeticException ex) {
+            throw new IllegalArgumentException(String.format("SQL federation 
pagination parameter value `%s` must be an integer.", value), ex);
+        }
+    }
+    
     @Override
     public Convention getConvention() {
         return EnumerableConvention.INSTANCE;
diff --git 
a/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessorTest.java
 
b/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessorTest.java
index 034b6b5ca7b..cd40206ed72 100644
--- 
a/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessorTest.java
+++ 
b/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/engine/processor/impl/StandardSQLFederationProcessorTest.java
@@ -20,8 +20,10 @@ package 
org.apache.shardingsphere.sqlfederation.engine.processor.impl;
 import org.apache.calcite.adapter.enumerable.EnumerableConvention;
 import org.apache.calcite.adapter.enumerable.EnumerableRel;
 import org.apache.calcite.adapter.enumerable.EnumerableRel.Prefer;
+import org.apache.calcite.DataContext;
 import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.runtime.Bindable;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.Table;
@@ -31,15 +33,30 @@ import 
org.apache.shardingsphere.database.connector.core.metadata.database.metad
 import 
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
+import 
org.apache.shardingsphere.infra.binder.context.segment.select.pagination.PaginationContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
 import org.apache.shardingsphere.infra.executor.sql.context.ExecutionUnit;
 import 
org.apache.shardingsphere.infra.executor.sql.execute.engine.driver.jdbc.JDBCExecutionUnit;
 import 
org.apache.shardingsphere.infra.executor.sql.execute.engine.driver.jdbc.JDBCExecutorCallback;
 import 
org.apache.shardingsphere.infra.executor.sql.execute.result.ExecuteResult;
 import 
org.apache.shardingsphere.infra.executor.sql.prepare.driver.DriverExecutionPrepareEngine;
+import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
+import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import 
org.apache.shardingsphere.infra.rule.attribute.table.TableMapperRuleAttribute;
 import org.apache.shardingsphere.infra.session.query.QueryContext;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ColumnProjectionSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionsSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.pagination.PaginationValueSegment;
+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.ParameterMarkerLimitValueSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.TableSegmentBoundInfo;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
+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.apache.shardingsphere.sqlfederation.compiler.SQLFederationExecutionPlan;
 import 
org.apache.shardingsphere.sqlfederation.compiler.context.CompilerContext;
@@ -48,20 +65,29 @@ import 
org.apache.shardingsphere.sqlfederation.compiler.metadata.schema.SQLFeder
 import 
org.apache.shardingsphere.sqlfederation.compiler.rel.converter.SQLFederationRelConverter;
 import org.apache.shardingsphere.sqlfederation.context.SQLFederationContext;
 import 
org.apache.shardingsphere.sqlfederation.engine.processor.SQLFederationProcessor;
+import 
org.apache.shardingsphere.sqlfederation.executor.context.ExecutorBindContext;
 import 
org.apache.shardingsphere.sqlfederation.resultset.SQLFederationResultSet;
 import 
org.apache.shardingsphere.sqlfederation.resultset.converter.DialectSQLFederationColumnTypeConverter;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.ArgumentCaptor;
 import org.mockito.MockedConstruction;
 import org.mockito.MockedStatic;
 
 import java.sql.Connection;
 import java.sql.ResultSet;
-import java.util.ArrayList;
+import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
@@ -83,6 +109,8 @@ class StandardSQLFederationProcessorTest {
     
     private final String schemaName = "foo_schema";
     
+    private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
+    
     private SQLFederationProcessor processor;
     
     @BeforeEach
@@ -196,7 +224,7 @@ class StandardSQLFederationProcessorTest {
         DriverExecutionPrepareEngine<JDBCExecutionUnit, Connection> 
prepareEngine = mock(DriverExecutionPrepareEngine.class);
         JDBCExecutorCallback<? extends ExecuteResult> callback = 
mock(JDBCExecutorCallback.class);
         SQLFederationContext federationContext = createFederationContext(true, 
null);
-        
when(federationContext.getQueryContext().getParameters()).thenReturn(new 
LinkedList<>());
+        
when(federationContext.getQueryContext().getParameters()).thenReturn(Collections.emptyList());
         CompilerContext compilerContext = mock(CompilerContext.class);
         SchemaPlus rootSchema = mockSchemaTreeWithTable();
         DialectSchemaOption schemaOption = mock(DialectSchemaOption.class);
@@ -212,7 +240,7 @@ class StandardSQLFederationProcessorTest {
         SQLFederationExecutionPlan executionPlan = 
mock(SQLFederationExecutionPlan.class);
         EnumerableRel physicalPlan = mock(EnumerableRel.class);
         when(executionPlan.getPhysicalPlan()).thenReturn(physicalPlan);
-        
when(executionPlan.getResultColumnType()).thenReturn(mock(org.apache.calcite.rel.type.RelDataType.class));
+        
when(executionPlan.getResultColumnType()).thenReturn(mock(RelDataType.class));
         SQLFederationRelConverter converter = 
mock(SQLFederationRelConverter.class);
         Bindable<Object> bindable = mock(Bindable.class);
         Enumerator<Object> enumerator = mock(Enumerator.class);
@@ -230,6 +258,88 @@ class StandardSQLFederationProcessorTest {
         }
     }
     
+    @SuppressWarnings("unchecked")
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("getPaginationParameterArguments")
+    void assertExecutePlanBindsPaginationParameters(final String name, final 
int offsetParameterIndex, final int rowCountParameterIndex, final List<Object> 
params,
+                                                    final Map<String, Object> 
expectedParams) throws SQLException {
+        SQLFederationContext federationContext = 
createFederationContext(false, offsetParameterIndex, rowCountParameterIndex, 
params);
+        SQLFederationExecutionPlan executionPlan = createExecutionPlan();
+        SQLFederationRelConverter converter = 
mock(SQLFederationRelConverter.class);
+        Bindable<Object> bindable = mockBindable();
+        ArgumentCaptor<DataContext> dataContextCaptor = 
ArgumentCaptor.forClass(DataContext.class);
+        try (
+                MockedStatic<SQLFederationExecutionPlan> mockedExecutionPlan = 
mockStatic(SQLFederationExecutionPlan.class);
+                MockedStatic<DatabaseTypedSPILoader> mockedSpiLoader = 
mockStatic(DatabaseTypedSPILoader.class)) {
+            mockedExecutionPlan.when(() -> 
SQLFederationExecutionPlan.toBindable(executionPlan.getPhysicalPlan(), 
Collections.emptyMap(), null, Prefer.ARRAY)).thenReturn(bindable);
+            mockedSpiLoader.when(() -> DatabaseTypedSPILoader
+                    
.getService(eq(DialectSQLFederationColumnTypeConverter.class), 
any(DatabaseType.class))).thenReturn(mock(DialectSQLFederationColumnTypeConverter.class));
+            ResultSet result = processor.executePlan(mock(), mock(), 
executionPlan, converter, federationContext, mock(SchemaPlus.class));
+            ((SQLFederationResultSet) result).close();
+        }
+        verify(bindable).bind(dataContextCaptor.capture());
+        assertThat(((ExecutorBindContext) 
dataContextCaptor.getValue()).getParameters(), is(expectedParams));
+    }
+    
+    private static Stream<Arguments> getPaginationParameterArguments() {
+        return Stream.of(
+                Arguments.of("row_count_long", -1, 0, 
Collections.singletonList((Object) 20L), Collections.singletonMap("?0", 
(Object) 20)),
+                Arguments.of("offset_and_row_count_long", 0, 1, 
Arrays.asList((Object) 5L, 20L), createExpectedParams("?0", 5, "?1", 20)),
+                Arguments.of("preserve_non_pagination_long", -1, 1, 
Arrays.asList((Object) 11L, 20L), createExpectedParams("?0", 11L, "?1", 20)),
+                Arguments.of("preserve_non_number_pagination_parameter", -1, 
0, Collections.singletonList((Object) "20"), Collections.singletonMap("?0", 
(Object) "20")));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    void assertExecutePlanBindsNestedPaginationParameter() throws SQLException 
{
+        SelectStatement nestedSelectStatement = createSelectStatement(-1, 1);
+        SubqueryTableSegment from = new SubqueryTableSegment(0, 0, new 
SubquerySegment(0, 0, nestedSelectStatement, ""));
+        SQLFederationContext federationContext = 
createFederationContext(false, createSelectStatement(-1, -1, from), 
Arrays.asList((Object) 11L, 20L));
+        SQLFederationExecutionPlan executionPlan = createExecutionPlan();
+        SQLFederationRelConverter converter = 
mock(SQLFederationRelConverter.class);
+        Bindable<Object> bindable = mockBindable();
+        ArgumentCaptor<DataContext> dataContextCaptor = 
ArgumentCaptor.forClass(DataContext.class);
+        try (
+                MockedStatic<SQLFederationExecutionPlan> mockedExecutionPlan = 
mockStatic(SQLFederationExecutionPlan.class);
+                MockedStatic<DatabaseTypedSPILoader> mockedSpiLoader = 
mockStatic(DatabaseTypedSPILoader.class)) {
+            mockedExecutionPlan.when(() -> 
SQLFederationExecutionPlan.toBindable(executionPlan.getPhysicalPlan(), 
Collections.emptyMap(), null, Prefer.ARRAY)).thenReturn(bindable);
+            mockedSpiLoader.when(() -> DatabaseTypedSPILoader
+                    
.getService(eq(DialectSQLFederationColumnTypeConverter.class), 
any(DatabaseType.class))).thenReturn(mock(DialectSQLFederationColumnTypeConverter.class));
+            ResultSet result = processor.executePlan(mock(), mock(), 
executionPlan, converter, federationContext, mock(SchemaPlus.class));
+            ((SQLFederationResultSet) result).close();
+        }
+        verify(bindable).bind(dataContextCaptor.capture());
+        assertThat(((ExecutorBindContext) 
dataContextCaptor.getValue()).getParameters(), is(createExpectedParams("?0", 
11L, "?1", 20)));
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    void assertExecutePlanRejectsOutOfRangePaginationParameter() {
+        SQLFederationContext federationContext = 
createFederationContext(false, -1, 0, Collections.singletonList((Object) 
(Integer.MAX_VALUE + 1L)));
+        SQLFederationExecutionPlan executionPlan = createExecutionPlan();
+        Bindable<Object> bindable = mockBindable();
+        try (MockedStatic<SQLFederationExecutionPlan> mockedExecutionPlan = 
mockStatic(SQLFederationExecutionPlan.class)) {
+            mockedExecutionPlan.when(() -> 
SQLFederationExecutionPlan.toBindable(executionPlan.getPhysicalPlan(), 
Collections.emptyMap(), null, Prefer.ARRAY)).thenReturn(bindable);
+            IllegalArgumentException ex = 
assertThrows(IllegalArgumentException.class,
+                    () -> processor.executePlan(mock(), mock(), executionPlan, 
mock(SQLFederationRelConverter.class), federationContext, 
mock(SchemaPlus.class)));
+            assertThat(ex.getMessage(), is("SQL federation pagination 
parameter value `2147483648` is out of integer range."));
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    void assertExecutePlanRejectsFractionalPaginationParameter() {
+        SQLFederationContext federationContext = 
createFederationContext(false, -1, 0, Collections.singletonList((Object) 
20.5D));
+        SQLFederationExecutionPlan executionPlan = createExecutionPlan();
+        Bindable<Object> bindable = mockBindable();
+        try (MockedStatic<SQLFederationExecutionPlan> mockedExecutionPlan = 
mockStatic(SQLFederationExecutionPlan.class)) {
+            mockedExecutionPlan.when(() -> 
SQLFederationExecutionPlan.toBindable(executionPlan.getPhysicalPlan(), 
Collections.emptyMap(), null, Prefer.ARRAY)).thenReturn(bindable);
+            IllegalArgumentException ex = 
assertThrows(IllegalArgumentException.class,
+                    () -> processor.executePlan(mock(), mock(), executionPlan, 
mock(SQLFederationRelConverter.class), federationContext, 
mock(SchemaPlus.class)));
+            assertThat(ex.getMessage(), is("SQL federation pagination 
parameter value `20.5` must be an integer."));
+        }
+    }
+    
     @SuppressWarnings("unchecked")
     @Test
     void assertExecutePlanPreviewAndNonPreview() {
@@ -253,7 +363,7 @@ class StandardSQLFederationProcessorTest {
         SQLFederationExecutionPlan executionPlan = 
mock(SQLFederationExecutionPlan.class);
         EnumerableRel physicalPlan = mock(EnumerableRel.class);
         when(executionPlan.getPhysicalPlan()).thenReturn(physicalPlan);
-        
when(executionPlan.getResultColumnType()).thenReturn(mock(org.apache.calcite.rel.type.RelDataType.class));
+        
when(executionPlan.getResultColumnType()).thenReturn(mock(RelDataType.class));
         SQLFederationRelConverter converter = 
mock(SQLFederationRelConverter.class);
         Bindable<Object> bindable = mock(Bindable.class);
         Enumerator<Object> enumerator = mock(Enumerator.class);
@@ -281,6 +391,9 @@ class StandardSQLFederationProcessorTest {
     private SQLFederationContext createFederationContext(final boolean 
preview, final TableSegmentBoundInfo tableSegmentBoundInfo) {
         SelectStatementContext sqlStatementContext = 
mock(SelectStatementContext.class, RETURNS_DEEP_STUBS);
         
when(sqlStatementContext.getTablesContext().getSchemaNames()).thenReturn(Collections.singleton("pg_catalog"));
+        
when(sqlStatementContext.getSqlStatement()).thenReturn(createSelectStatement(-1,
 -1));
+        when(sqlStatementContext.getPaginationContext()).thenReturn(new 
PaginationContext(null, null, Collections.emptyList()));
+        
when(sqlStatementContext.getSubqueryContexts()).thenReturn(Collections.emptyMap());
         SimpleTableSegment tableSegment = mock(SimpleTableSegment.class, 
RETURNS_DEEP_STUBS);
         if (null == tableSegmentBoundInfo) {
             
when(tableSegment.getTableName().getTableBoundInfo()).thenReturn(Optional.empty());
@@ -288,13 +401,79 @@ class StandardSQLFederationProcessorTest {
             
when(tableSegment.getTableName().getTableBoundInfo()).thenReturn(Optional.of(tableSegmentBoundInfo));
         }
         
when(sqlStatementContext.getTablesContext().getSimpleTables()).thenReturn(Collections.singleton(tableSegment));
+        return createFederationContext(preview, sqlStatementContext, 
Collections.singletonList((Object) 1));
+    }
+    
+    private SQLFederationContext createFederationContext(final boolean 
preview, final int offsetParameterIndex, final int rowCountParameterIndex, 
final List<Object> params) {
+        return createFederationContext(preview, 
createSelectStatementContext(createSelectStatement(offsetParameterIndex, 
rowCountParameterIndex)), params);
+    }
+    
+    private SQLFederationContext createFederationContext(final boolean 
preview, final SelectStatement selectStatement, final List<Object> params) {
+        return createFederationContext(preview, 
createSelectStatementContext(selectStatement), params);
+    }
+    
+    private SQLFederationContext createFederationContext(final boolean 
preview, final SelectStatementContext sqlStatementContext, final List<Object> 
params) {
         QueryContext queryContext = mock(QueryContext.class);
         
when(queryContext.getSqlStatementContext()).thenReturn(sqlStatementContext);
         when(queryContext.getSql()).thenReturn("SELECT 1");
-        when(queryContext.getParameters()).thenReturn(new 
ArrayList<>(Collections.singletonList(1)));
+        when(queryContext.getParameters()).thenReturn(params);
         return new SQLFederationContext(preview, queryContext, mock(), "pid");
     }
     
+    private SelectStatementContext createSelectStatementContext(final 
SelectStatement selectStatement) {
+        return new SelectStatementContext(selectStatement, mockMetaData(), 
databaseName, Collections.emptyList());
+    }
+    
+    private ShardingSphereMetaData mockMetaData() {
+        ShardingSphereDatabase database = mock(ShardingSphereDatabase.class, 
RETURNS_DEEP_STUBS);
+        
when(database.getRuleMetaData().getAttributes(TableMapperRuleAttribute.class)).thenReturn(Collections.emptyList());
+        ShardingSphereMetaData result = mock(ShardingSphereMetaData.class);
+        when(result.getDatabase(databaseName)).thenReturn(database);
+        return result;
+    }
+    
+    private SelectStatement createSelectStatement(final int 
offsetParameterIndex, final int rowCountParameterIndex) {
+        return createSelectStatement(offsetParameterIndex, 
rowCountParameterIndex, null);
+    }
+    
+    private SelectStatement createSelectStatement(final int 
offsetParameterIndex, final int rowCountParameterIndex, final TableSegment 
from) {
+        ProjectionsSegment projections = new ProjectionsSegment(0, 0);
+        projections.getProjections().add(new ColumnProjectionSegment(new 
ColumnSegment(0, 0, new IdentifierValue("foo_col"))));
+        return 
SelectStatement.builder().databaseType(databaseType).projections(projections).from(from).limit(createLimitSegment(offsetParameterIndex,
 rowCountParameterIndex)).build();
+    }
+    
+    private LimitSegment createLimitSegment(final int offsetParameterIndex, 
final int rowCountParameterIndex) {
+        PaginationValueSegment offset = -1 == offsetParameterIndex ? null : 
new ParameterMarkerLimitValueSegment(0, 0, offsetParameterIndex);
+        PaginationValueSegment rowCount = -1 == rowCountParameterIndex ? null 
: new ParameterMarkerLimitValueSegment(0, 0, rowCountParameterIndex);
+        return null == offset && null == rowCount ? null : new LimitSegment(0, 
0, offset, rowCount);
+    }
+    
+    private static Map<String, Object> createExpectedParams(final Object... 
values) {
+        Map<String, Object> result = new HashMap<>(values.length / 2, 1F);
+        for (int i = 0; i < values.length; i += 2) {
+            result.put((String) values[i], values[i + 1]);
+        }
+        return result;
+    }
+    
+    @SuppressWarnings("unchecked")
+    private SQLFederationExecutionPlan createExecutionPlan() {
+        SQLFederationExecutionPlan result = 
mock(SQLFederationExecutionPlan.class);
+        when(result.getPhysicalPlan()).thenReturn(mock(EnumerableRel.class));
+        when(result.getResultColumnType()).thenReturn(mock(RelDataType.class));
+        return result;
+    }
+    
+    @SuppressWarnings("unchecked")
+    private Bindable<Object> mockBindable() {
+        Bindable<Object> result = mock(Bindable.class);
+        Enumerator<Object> enumerator = mock(Enumerator.class);
+        Enumerable<Object> enumerable = mock(Enumerable.class);
+        when(enumerable.enumerator()).thenReturn(enumerator);
+        when(result.bind(any())).thenReturn(enumerable);
+        return result;
+    }
+    
     @SuppressWarnings({"rawtypes", "unchecked"})
     private SchemaPlus mockFlatSchemaWithTable(final Table table) {
         Lookup tableLookup = mock(Lookup.class);
diff --git 
a/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-pagination-group-by-order-by-for-db-tbl-sql-federation.xml
 
b/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-pagination-group-by-order-by-for-db-tbl-sql-federation.xml
index 69884882650..210ea8ceb80 100644
--- 
a/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-pagination-group-by-order-by-for-db-tbl-sql-federation.xml
+++ 
b/test/e2e/sql/src/test/resources/cases/dql/cases/db_tbl_sql_federation/e2e-dql-select-pagination-group-by-order-by-for-db-tbl-sql-federation.xml
@@ -38,6 +38,11 @@
         <assertion parameters="10:int" 
expected-data-source-name="read_dataset" />
     </test-case>
     
+    <test-case sql="SELECT * FROM t_order o INNER JOIN t_merchant m ON 
o.merchant_id = m.merchant_id WHERE o.user_id = ? ORDER BY o.order_id LIMIT ?" 
db-types="MySQL" scenario-types="db_tbl_sql_federation"
+               scenario-comments="Test MySQL federation pagination accepts 
long row count parameter.">
+        <assertion parameters="10:int, 2:long" 
expected-data-source-name="read_dataset" />
+    </test-case>
+    
     <test-case sql="SELECT * FROM t_order o INNER JOIN t_merchant m 
USING(merchant_id) WHERE o.user_id = ? ORDER BY o.order_id LIMIT 5, 2" 
db-types="MySQL,openGauss" scenario-types="db_tbl_sql_federation">
         <assertion parameters="10:int" 
expected-data-source-name="read_dataset" />
     </test-case>

Reply via email to