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

terrymanu 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 4877c93a7d4 Fix incorrect return value of 
SQLFederationResultSetMetaData#getColumnClassName (#39217)
4877c93a7d4 is described below

commit 4877c93a7d44fa1fb94548d8745ad475b63a870c
Author: Prahlad Bhakat <[email protected]>
AuthorDate: Sun Aug 23 08:47:29 2026 +0530

    Fix incorrect return value of 
SQLFederationResultSetMetaData#getColumnClassName (#39217)
    
    * Fix getColumnClassName return value
    
    * Format SQL federation result set metadata
    
    * Fix SQL federation column class name
    
    * Fix code style
    
    * Fix SQL federation column class name
    
    * Add representative decimal/temporal/binary/fallback coverage and a real 
ShardingSphere-JDBC SQL Federation regression test for #39121
    
    * Fix checkstyle newline
    
    * Remove generated effective-pom.xml
---
 .../resultset/SQLFederationResultSetMetaData.java  |  18 ++-
 .../DialectSQLFederationColumnTypeConverter.java   |  12 ++
 .../SQLFederationResultSetMetaDataTest.java        |  70 +++++++++++-
 .../resultset/SQLFederationResultSetTest.java      |  45 ++++++++
 .../MySQLSQLFederationColumnTypeConverter.java     |   7 ++
 .../MySQLSQLFederationColumnTypeConverterTest.java |   9 ++
 test/e2e/driver/pom.xml                            |   6 +
 .../SQLFederationResultSetColumnClassNameTest.java | 127 +++++++++++++++++++++
 8 files changed, 284 insertions(+), 10 deletions(-)

diff --git 
a/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaData.java
 
b/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaData.java
index f1794a6f197..625a71afbe5 100644
--- 
a/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaData.java
+++ 
b/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaData.java
@@ -17,9 +17,10 @@
 
 package org.apache.shardingsphere.sqlfederation.resultset;
 
+import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.SqlType;
+import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeFactoryImpl.JavaType;
 import org.apache.calcite.schema.Schema;
 import org.apache.calcite.schema.Table;
@@ -31,6 +32,7 @@ import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.
 import 
org.apache.shardingsphere.sqlfederation.compiler.sql.type.SQLFederationDataTypeFactory;
 import 
org.apache.shardingsphere.sqlfederation.resultset.converter.DialectSQLFederationColumnTypeConverter;
 
+import java.lang.reflect.Type;
 import java.math.BigInteger;
 import java.sql.ResultSetMetaData;
 import java.util.List;
@@ -44,7 +46,7 @@ public final class SQLFederationResultSetMetaData extends 
SQLFederationWrapperAd
     
     private final Schema sqlFederationSchema;
     
-    private final RelDataTypeFactory typeFactory;
+    private final JavaTypeFactory typeFactory;
     
     private final List<Projection> expandProjections;
     
@@ -59,7 +61,7 @@ public final class SQLFederationResultSetMetaData extends 
SQLFederationWrapperAd
     public SQLFederationResultSetMetaData(final Schema sqlFederationSchema, 
final List<Projection> expandProjections, final DatabaseType databaseType, 
final RelDataType resultColumnType,
                                           final Map<Integer, String> 
indexAndColumnLabels, final DialectSQLFederationColumnTypeConverter 
columnTypeConverter) {
         this.sqlFederationSchema = sqlFederationSchema;
-        typeFactory = SQLFederationDataTypeFactory.getInstance();
+        typeFactory = (JavaTypeFactory) 
SQLFederationDataTypeFactory.getInstance();
         this.expandProjections = expandProjections;
         this.databaseType = databaseType;
         this.resultColumnType = resultColumnType;
@@ -180,7 +182,15 @@ public final class SQLFederationResultSetMetaData extends 
SQLFederationWrapperAd
     
     @Override
     public String getColumnClassName(final int column) {
-        return resultColumnType.getFieldList().get(column - 
1).getType().getSqlTypeName().getClass().getName();
+        RelDataType relDataType = resultColumnType.getFieldList().get(column - 
1).getType();
+        Optional<Class<?>> convertedClass = null == columnTypeConverter
+                ? Optional.empty()
+                : 
columnTypeConverter.convertColumnValueClass(relDataType.getSqlTypeName());
+        if (convertedClass.isPresent()) {
+            return convertedClass.get().getName();
+        }
+        Type javaType = Primitive.box(typeFactory.getJavaClass(relDataType));
+        return javaType instanceof Class ? ((Class<?>) javaType).getName() : 
Object.class.getName();
     }
     
     private Optional<String> findTableName(final int column) {
diff --git 
a/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/converter/DialectSQLFederationColumnTypeConverter.java
 
b/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/converter/DialectSQLFederationColumnTypeConverter.java
index 1426639ff2d..075d77fa9b3 100644
--- 
a/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/converter/DialectSQLFederationColumnTypeConverter.java
+++ 
b/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/converter/DialectSQLFederationColumnTypeConverter.java
@@ -21,6 +21,8 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPI;
 import org.apache.shardingsphere.infra.spi.annotation.SingletonSPI;
 
+import java.util.Optional;
+
 /**
  * Dialect SQL federation column type converter.
  */
@@ -42,4 +44,14 @@ public interface DialectSQLFederationColumnTypeConverter 
extends DatabaseTypedSP
      * @return converted column type
      */
     int convertColumnType(SqlTypeName sqlTypeName);
+    
+    /**
+     * Convert column value class.
+     *
+     * @param sqlTypeName original SQL type name
+     * @return actual Java class of the converted value
+     */
+    default Optional<Class<?>> convertColumnValueClass(final SqlTypeName 
sqlTypeName) {
+        return Optional.empty();
+    }
 }
diff --git 
a/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaDataTest.java
 
b/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaDataTest.java
index 0e23a03cf9c..94edb42aed7 100644
--- 
a/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaDataTest.java
+++ 
b/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetMetaDataTest.java
@@ -18,6 +18,7 @@
 package org.apache.shardingsphere.sqlfederation.resultset;
 
 import org.apache.calcite.avatica.SqlType;
+import org.apache.calcite.avatica.util.ByteString;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeFactoryImpl.JavaType;
@@ -31,23 +32,31 @@ import 
org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import 
org.apache.shardingsphere.sqlfederation.compiler.sql.type.SQLFederationDataTypeFactory;
 import 
org.apache.shardingsphere.sqlfederation.resultset.converter.DialectSQLFederationColumnTypeConverter;
 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 java.math.BigDecimal;
 import java.math.BigInteger;
 import java.sql.ResultSetMetaData;
 import java.sql.Types;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
-import static org.hamcrest.Matchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -352,12 +361,61 @@ class SQLFederationResultSetMetaDataTest {
     
     @Test
     void assertGetColumnClassName() {
-        JavaType varcharType = mock(JavaType.class);
-        when(varcharType.getJavaClass()).thenReturn(SqlTypeName.class);
-        when(varcharType.getSqlTypeName()).thenReturn(SqlTypeName.VARCHAR);
+        RelDataType varcharType = 
SQLFederationDataTypeFactory.getInstance().createSqlType(SqlTypeName.VARCHAR);
+        SQLFederationResultSetMetaData metaData = new 
SQLFederationResultSetMetaData(
+                mock(), Collections.emptyList(), databaseType,
+                createResultType(new String[]{"foo_col"}, varcharType),
+                Collections.singletonMap(1, "foo_label"), null);
+        assertThat(metaData.getColumnClassName(1), is(String.class.getName()));
+    }
+    
+    @Test
+    void assertGetColumnClassNameForBigInteger() {
+        JavaType javaBigIntegerType = mock(JavaType.class);
+        when(javaBigIntegerType.getJavaClass()).thenReturn(BigInteger.class);
+        
when(javaBigIntegerType.getSqlTypeName()).thenReturn(SqlTypeName.DECIMAL);
+        RelDataType resultType = createResultType(new String[]{"foo_col"}, 
javaBigIntegerType);
+        SQLFederationResultSetMetaData metaData = new 
SQLFederationResultSetMetaData(
+                mock(), Collections.emptyList(), databaseType, resultType, 
Collections.singletonMap(1, "foo_label"), mock());
+        assertThat(metaData.getColumnClassName(1), 
is(BigInteger.class.getName()));
+    }
+    
+    @Test
+    void assertGetColumnClassNameForConvertedValueClass() {
+        RelDataType booleanType = mock(RelDataType.class);
+        when(booleanType.getSqlTypeName()).thenReturn(SqlTypeName.BOOLEAN);
+        DialectSQLFederationColumnTypeConverter converter = 
mock(DialectSQLFederationColumnTypeConverter.class);
+        
doReturn(Optional.of(Integer.class)).when(converter).convertColumnValueClass(SqlTypeName.BOOLEAN);
+        RelDataType resultType = createResultType(new String[]{"foo_col"}, 
booleanType);
+        SQLFederationResultSetMetaData metaData = new 
SQLFederationResultSetMetaData(
+                mock(), Collections.emptyList(), databaseType, resultType, 
Collections.singletonMap(1, "foo_label"), converter);
+        assertThat(metaData.getColumnClassName(1), 
is(Integer.class.getName()));
+    }
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("columnClassNameSource")
+    void assertGetColumnClassNameByType(final String name, final SqlTypeName 
sqlTypeName, final String expectedClassName) {
+        RelDataType relDataType = 
SQLFederationDataTypeFactory.getInstance().createSqlType(sqlTypeName);
+        RelDataType resultType = createResultType(new String[]{"foo_col"}, 
relDataType);
         SQLFederationResultSetMetaData metaData = new 
SQLFederationResultSetMetaData(
-                mock(), Collections.emptyList(), databaseType, 
createResultType(new String[]{"foo_col"}, varcharType), 
Collections.singletonMap(1, "foo_label"), mock());
-        assertThat(metaData.getColumnClassName(1), 
is(SqlTypeName.VARCHAR.getClass().getName()));
+                mock(), Collections.emptyList(), databaseType, resultType, 
Collections.singletonMap(1, "foo_label"), null);
+        assertThat(metaData.getColumnClassName(1), is(expectedClassName));
+    }
+    
+    private static Iterable<Arguments> columnClassNameSource() {
+        return Arrays.asList(
+                Arguments.of("tinyint", SqlTypeName.TINYINT, 
Byte.class.getName()),
+                Arguments.of("smallint", SqlTypeName.SMALLINT, 
Short.class.getName()),
+                Arguments.of("integer", SqlTypeName.INTEGER, 
Integer.class.getName()),
+                Arguments.of("bigint", SqlTypeName.BIGINT, 
Long.class.getName()),
+                Arguments.of("float", SqlTypeName.FLOAT, 
Double.class.getName()),
+                Arguments.of("real", SqlTypeName.REAL, Float.class.getName()),
+                Arguments.of("double", SqlTypeName.DOUBLE, 
Double.class.getName()),
+                Arguments.of("decimal", SqlTypeName.DECIMAL, 
BigDecimal.class.getName()),
+                Arguments.of("date", SqlTypeName.DATE, 
Integer.class.getName()),
+                Arguments.of("timestamp", SqlTypeName.TIMESTAMP, 
Long.class.getName()),
+                Arguments.of("varbinary", SqlTypeName.VARBINARY, 
ByteString.class.getName()),
+                Arguments.of("any_fallback", SqlTypeName.ANY, 
Object.class.getName()));
     }
     
     private RelDataType createRowType(final boolean nullable, final int 
precision, final int scale) {
diff --git 
a/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetTest.java
 
b/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetTest.java
index 5793386f7ee..d3af1da0cda 100644
--- 
a/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetTest.java
+++ 
b/kernel/sql-federation/core/src/test/java/org/apache/shardingsphere/sqlfederation/resultset/SQLFederationResultSetTest.java
@@ -19,11 +19,14 @@ package org.apache.shardingsphere.sqlfederation.resultset;
 
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
 import 
org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import 
org.apache.shardingsphere.sqlfederation.compiler.metadata.schema.SQLFederationSchema;
+import 
org.apache.shardingsphere.sqlfederation.compiler.sql.type.SQLFederationDataTypeFactory;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -46,6 +49,7 @@ import java.sql.SQLXML;
 import java.sql.Statement;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
@@ -580,6 +584,47 @@ class SQLFederationResultSetTest {
         assertThat(federationResultSet.getObject("order_id"), is(10));
     }
     
+    @Test
+    void assertColumnClassNameMatchesGetObjectValueClass() throws SQLException 
{
+        assertColumnClassNameMatchesGetObjectValueClass(SqlTypeName.BIGINT, 
1L);
+        assertColumnClassNameMatchesGetObjectValueClass(SqlTypeName.TINYINT, 
(byte) 1);
+        assertColumnClassNameMatchesGetObjectValueClass(SqlTypeName.SMALLINT, 
(short) 1);
+        assertColumnClassNameMatchesGetObjectValueClass(SqlTypeName.FLOAT, 1D);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private void assertColumnClassNameMatchesGetObjectValueClass(final 
SqlTypeName sqlTypeName, final Object value) throws SQLException {
+        Enumerator<Object> testEnumerator = mock(Enumerator.class);
+        when(testEnumerator.moveNext()).thenReturn(true, false);
+        when(testEnumerator.current()).thenReturn(new Object[]{value});
+        RelDataType columnType = 
SQLFederationDataTypeFactory.getInstance().createSqlType(sqlTypeName);
+        RelDataType resultType = createResultType(new String[]{"value"}, 
columnType);
+        SQLFederationResultSet resultSet = new SQLFederationResultSet(
+                testEnumerator, mock(SQLFederationSchema.class),
+                Collections.singletonList(new ColumnProjection(null, "value", 
null, databaseType)),
+                databaseType, resultType, "contract");
+        try {
+            assertTrue(resultSet.next());
+            Object actualValue = resultSet.getObject(1);
+            assertThat(resultSet.getMetaData().getColumnClassName(1), 
is(actualValue.getClass().getName()));
+        } finally {
+            resultSet.close();
+        }
+    }
+    
+    private RelDataType createResultType(final String[] names, final 
RelDataType... types) {
+        List<RelDataTypeField> fields = new ArrayList<>(names.length);
+        for (int i = 0; i < names.length; i++) {
+            RelDataTypeField field = mock(RelDataTypeField.class);
+            when(field.getName()).thenReturn(names[i]);
+            when(field.getType()).thenReturn(types[i]);
+            fields.add(field);
+        }
+        RelDataType result = mock(RelDataType.class);
+        when(result.getFieldList()).thenReturn(fields);
+        return result;
+    }
+    
     @Test
     void assertGetCalendarValueNullSetsWasNull() throws SQLException {
         when(enumerator.current()).thenReturn(new Object[]{null, 1, "OK", 1});
diff --git 
a/kernel/sql-federation/dialect/mysql/src/main/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverter.java
 
b/kernel/sql-federation/dialect/mysql/src/main/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverter.java
index d8d5a38c5ae..4e11060ba6b 100644
--- 
a/kernel/sql-federation/dialect/mysql/src/main/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverter.java
+++ 
b/kernel/sql-federation/dialect/mysql/src/main/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverter.java
@@ -20,6 +20,8 @@ package org.apache.shardingsphere.sqlfederation.mysql;
 import org.apache.calcite.sql.type.SqlTypeName;
 import 
org.apache.shardingsphere.sqlfederation.resultset.converter.DialectSQLFederationColumnTypeConverter;
 
+import java.util.Optional;
+
 /**
  * SQL federation column type converter for MySQL.
  */
@@ -42,6 +44,11 @@ public final class MySQLSQLFederationColumnTypeConverter 
implements DialectSQLFe
         return result;
     }
     
+    @Override
+    public Optional<Class<?>> convertColumnValueClass(final SqlTypeName 
sqlTypeName) {
+        return SqlTypeName.BOOLEAN == sqlTypeName ? Optional.of(Integer.class) 
: Optional.empty();
+    }
+    
     @Override
     public String getDatabaseType() {
         return "MySQL";
diff --git 
a/kernel/sql-federation/dialect/mysql/src/test/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverterTest.java
 
b/kernel/sql-federation/dialect/mysql/src/test/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverterTest.java
index e63aec9cd98..84a4f768702 100644
--- 
a/kernel/sql-federation/dialect/mysql/src/test/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverterTest.java
+++ 
b/kernel/sql-federation/dialect/mysql/src/test/java/org/apache/shardingsphere/sqlfederation/mysql/MySQLSQLFederationColumnTypeConverterTest.java
@@ -22,11 +22,13 @@ import 
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoa
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import 
org.apache.shardingsphere.sqlfederation.resultset.converter.DialectSQLFederationColumnTypeConverter;
+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 java.util.Arrays;
+import java.util.Optional;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
@@ -37,6 +39,13 @@ class MySQLSQLFederationColumnTypeConverterTest {
     
     private final DialectSQLFederationColumnTypeConverter converter = 
DatabaseTypedSPILoader.getService(DialectSQLFederationColumnTypeConverter.class,
 databaseType);
     
+    @Test
+    void assertConvertColumnValueClass() {
+        assertThat(converter.convertColumnValueClass(SqlTypeName.BOOLEAN), 
is(Optional.of(Integer.class)));
+        assertThat(converter.convertColumnValueClass(SqlTypeName.INTEGER), 
is(Optional.empty()));
+        assertThat(converter.convertColumnValueClass(SqlTypeName.ANY), 
is(Optional.empty()));
+    }
+    
     @ParameterizedTest(name = "{0}")
     @MethodSource("convertValueSource")
     void assertConvertColumnValue(final String name, final Object input, final 
Object expected) {
diff --git a/test/e2e/driver/pom.xml b/test/e2e/driver/pom.xml
index 5cf6d4bc00b..b6e68a4e6ff 100644
--- a/test/e2e/driver/pom.xml
+++ b/test/e2e/driver/pom.xml
@@ -75,6 +75,12 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.shardingsphere</groupId>
+            <artifactId>shardingsphere-sql-federation-mysql</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.shardingsphere</groupId>
             <artifactId>shardingsphere-broadcast-core</artifactId>
diff --git 
a/test/e2e/driver/src/test/java/org/apache/shardingsphere/test/e2e/driver/federation/SQLFederationResultSetColumnClassNameTest.java
 
b/test/e2e/driver/src/test/java/org/apache/shardingsphere/test/e2e/driver/federation/SQLFederationResultSetColumnClassNameTest.java
new file mode 100644
index 00000000000..a2c684f0ed8
--- /dev/null
+++ 
b/test/e2e/driver/src/test/java/org/apache/shardingsphere/test/e2e/driver/federation/SQLFederationResultSetColumnClassNameTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.e2e.driver.federation;
+
+import 
org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
+import 
org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource;
+import org.apache.shardingsphere.test.e2e.driver.AbstractDriverTest;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Regression test for <a 
href="https://github.com/apache/shardingsphere/issues/39121";>#39121</a>.
+ *
+ * <p>Runs a real query through ShardingSphere-JDBC with SQL federation forced 
on for every {@code SELECT}
+ * ({@code allQueryUseSQLFederation: true}), so the {@code ResultSet} returned 
to the caller is the actual
+ * production {@code SQLFederationResultSet} produced by the compiler and 
dialect SPI, rather than a manually
+ * constructed one. Asserts that {@code 
getMetaData().getColumnClassName(column)} always agrees with the
+ * runtime class of {@code getObject(column)}, both for a plain numeric column 
and for a column whose value
+ * class is rewritten by {@code DialectSQLFederationColumnTypeConverter} 
(MySQL {@code BOOLEAN -> Integer}).</p>
+ */
+class SQLFederationResultSetColumnClassNameTest extends AbstractDriverTest {
+    
+    private static final String YAML_CONFIG = String.join("\n",
+            "databaseName: federation_db",
+            "",
+            "rules:",
+            "- !SINGLE",
+            "  tables:",
+            "    - \"*.*\"",
+            "",
+            "sqlFederation:",
+            "  sqlFederationEnabled: true",
+            "  allQueryUseSQLFederation: true",
+            "  executionPlanCache:",
+            "    initialCapacity: 2000",
+            "    maximumSize: 65535",
+            "",
+            "props:",
+            "  proxy-frontend-database-protocol-type: MySQL");
+    
+    private static ShardingSphereDataSource dataSource;
+    
+    @BeforeAll
+    static void initShardingSphereDataSource() throws SQLException, 
IOException {
+        if (null == dataSource) {
+            DataSource actualDataSource = 
getActualDataSources().get("single_jdbc");
+            dataSource = (ShardingSphereDataSource) 
YamlShardingSphereDataSourceFactory.createDataSource(actualDataSource, 
YAML_CONFIG.getBytes(StandardCharsets.UTF_8));
+        }
+        try (Connection connection = dataSource.getConnection(); Statement 
statement = connection.createStatement()) {
+            statement.execute("CREATE TABLE IF NOT EXISTS 
t_federation_column_type (id BIGINT NOT NULL, enabled BOOLEAN NOT NULL, PRIMARY 
KEY (id))");
+        }
+    }
+    
+    @AfterAll
+    static void closeShardingSphereDataSource() throws Exception {
+        if (null == dataSource) {
+            return;
+        }
+        dataSource.close();
+        dataSource = null;
+    }
+    
+    @BeforeEach
+    void initData() throws SQLException {
+        try (
+                Connection connection = dataSource.getConnection();
+                PreparedStatement deleteStatement = 
connection.prepareStatement("DELETE FROM t_federation_column_type");
+                PreparedStatement insertStatement = 
connection.prepareStatement("INSERT INTO t_federation_column_type (id, enabled) 
VALUES (?, ?)")) {
+            deleteStatement.executeUpdate();
+            insertStatement.setLong(1, 1L);
+            insertStatement.setBoolean(2, true);
+            insertStatement.executeUpdate();
+        }
+    }
+    
+    @Test
+    void assertColumnClassNameMatchesGetObjectValueClassForBigint() throws 
SQLException {
+        assertColumnClassNameMatchesGetObjectValueClass("id");
+    }
+    
+    @Test
+    void 
assertColumnClassNameMatchesGetObjectValueClassForDialectConvertedBoolean() 
throws SQLException {
+        assertColumnClassNameMatchesGetObjectValueClass("enabled");
+    }
+    
+    private void assertColumnClassNameMatchesGetObjectValueClass(final String 
columnLabel) throws SQLException {
+        try (
+                Connection connection = dataSource.getConnection();
+                Statement statement = connection.createStatement();
+                ResultSet resultSet = statement.executeQuery("SELECT id, 
enabled FROM t_federation_column_type WHERE id = 1")) {
+            assertTrue(resultSet.next());
+            int columnIndex = resultSet.findColumn(columnLabel);
+            Object actualValue = resultSet.getObject(columnIndex);
+            
assertThat(resultSet.getMetaData().getColumnClassName(columnIndex), 
is(actualValue.getClass().getName()));
+        }
+    }
+}

Reply via email to