YM-Sky opened a new issue, #39121:
URL: https://github.com/apache/shardingsphere/issues/39121

   ## Bug Report
   
   ### Which version of ShardingSphere did you use?
   
   Apache ShardingSphere-JDBC 5.5.3.
   
   Related modules:
   
   - `shardingsphere-jdbc`: 5.5.3
   - `shardingsphere-jdbc-dialect-mysql`: 5.5.3
   - `shardingsphere-sharding-mysql`: 5.5.3
   
   Environment:
   
   - Java 17
   - MySQL dialect
   - SQL Federation enabled
   - Apache Calcite resolved as 1.40.0
   
   ### Which project did you use? ShardingSphere-JDBC or ShardingSphere-Proxy?
   
   ShardingSphere-JDBC.
   
   ### Expected behavior
   
   For SQL Federation query results, 
`ResultSetMetaData#getColumnClassName(column)` should return the Java class 
name corresponding to the actual result column type.
   
   For example:
   
   - BIGINT id column: `java.lang.Long`
   - VARCHAR name column: `java.lang.String`
   - DECIMAL column: `java.math.BigDecimal`
   
   ### Actual behavior
   
   `ResultSetMetaData#getColumnClassName(column)` returns:
   
   ```text
   org.apache.calcite.sql.type.SqlTypeName
   ```
   
   This happens for normal result columns such as `id BIGINT`.
   
   When a mapper framework maps results to `Map<String, Object>`, it may use 
the reported column class name to select a type handler. Since ShardingSphere 
reports the enum class `SqlTypeName`, MyBatis/MyBatis-Plus chooses an enum type 
handler and tries to parse the actual database value as a `SqlTypeName` enum 
constant.
   
   Stack trace excerpt:
   
   ```text
   Caused by: org.apache.ibatis.executor.result.ResultMapException:
   Error attempting to get column 'id' from result set.
   Cause: java.lang.IllegalArgumentException:
   No enum constant org.apache.calcite.sql.type.SqlTypeName.2076978610812633090
   
   at org.apache.ibatis.type.BaseTypeHandler.getResult(BaseTypeHandler.java:88)
   at 
com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler.getResult(CompositeEnumTypeHandler.java:62)
   at 
org.apache.ibatis.executor.resultset.DefaultResultSetHandler.applyAutomaticMappings(DefaultResultSetHandler.java:582)
   
   Caused by: java.lang.IllegalArgumentException:
   No enum constant org.apache.calcite.sql.type.SqlTypeName.2076978610812633090
   at java.lang.Enum.valueOf(Enum.java:273)
   at 
org.apache.ibatis.type.EnumTypeHandler.getNullableResult(EnumTypeHandler.java:49)
   ```
   
   ### Reason analyze (If you can)
   
   I checked the ShardingSphere 5.5.3 source locally.
   
   Source location:
   
   ```text
   
D:\source\shardingsphere\shardingsphere-5.5.3\kernel\sql-federation\core\src\main\java\org\apache\shardingsphere\sqlfederation\resultset\SQLFederationResultSetMetaData.java
   ```
   
   Current implementation:
   
   ```java
   @Override
   public String getColumnClassName(final int column) {
       return resultColumnType.getFieldList().get(column - 
1).getType().getSqlTypeName().getClass().getName();
   }
   ```
   
   `getSqlTypeName()` returns a Calcite enum value. Calling 
`getClass().getName()` on that enum value always returns:
   
   ```text
   org.apache.calcite.sql.type.SqlTypeName
   ```
   
   It does not return the actual Java class for the result column.
   
   ### Steps to reproduce the behavior, such as: SQL to execute, sharding rule 
configuration, when exception occur etc.
   
   Use ShardingSphere-JDBC 5.5.3 with MySQL dialect and SQL Federation enabled.
   
   Sanitized YAML, preserving the real rule structure:
   
   ```yaml
   databaseName: logic_db
   
   mode:
     type: Standalone
     repository:
       type: JDBC
   
   transaction:
     defaultType: LOCAL
   
   dataSources:
     ds_0:
       dataSourceClassName: com.zaxxer.hikari.HikariDataSource
       driverClassName: com.mysql.cj.jdbc.Driver
       jdbcUrl: 
jdbc:mysql://127.0.0.1:3306/demo_master?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
       username: root
       password: password
     ds_1:
       dataSourceClassName: com.zaxxer.hikari.HikariDataSource
       driverClassName: com.mysql.cj.jdbc.Driver
       jdbcUrl: 
jdbc:mysql://127.0.0.1:3306/demo_sharding_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
       username: root
       password: password
   
   sqlFederation:
     sqlFederationEnabled: true
     allQueryUseSQLFederation: false
     executionPlanCache:
       initialCapacity: 2000
       maximumSize: 65535
   
   props:
     sql-show: true
   
   rules:
     - !SINGLE
       tables:
         - ds_0.mnt_base_user
   
     - !SHARDING
       tables: {}
       defaultDatabaseStrategy:
         complex:
           shardingColumns: tenant_id
           shardingAlgorithmName: custom_database_tenant_sharding_algorithm
       defaultTableStrategy:
         complex:
           shardingColumns: tenant_id
           shardingAlgorithmName: tenant_sharding_algorithm
       shardingAlgorithms:
         custom_database_tenant_sharding_algorithm:
           type: CLASS_BASED
           props:
             strategy: COMPLEX
             algorithmClassName: 
com.example.sharding.CustomDatabaseTenantIdShardingAlgorithm
             dbPrefix: ds_
             tenantIdFiled: tenant_id
         tenant_sharding_algorithm:
           type: CLASS_BASED
           props:
             strategy: COMPLEX
             algorithmClassName: 
com.example.sharding.CustomTableTenantIdShardingAlgorithm
             tenantIdFiled: tenant_id
   ```
   
   Run a SQL Federation query:
   
   ```sql
   SELECT so.id AS id, u.name AS record_user_name
   FROM ims_sales_order so
   LEFT JOIN mnt_base_user u ON so.record_user_id = u.id
   WHERE so.tenant_id = 1
   ORDER BY so.create_time DESC
   LIMIT 20
   ```
   
   Then inspect metadata:
   
   ```java
   try (ResultSet rs = ps.executeQuery()) {
       ResultSetMetaData md = rs.getMetaData();
       System.out.println(md.getColumnClassName(1));
   }
   ```
   
   Expected:
   
   ```text
   java.lang.Long
   ```
   
   Actual:
   
   ```text
   org.apache.calcite.sql.type.SqlTypeName
   ```
   
   ### Example codes for reproduce this issue (such as a github link).
   
   ```java
   try (Connection connection = dataSource.getConnection();
        PreparedStatement ps = connection.prepareStatement("""
            SELECT so.id AS id, u.name AS record_user_name
            FROM ims_sales_order so
            LEFT JOIN mnt_base_user u ON so.record_user_id = u.id
            WHERE so.tenant_id = 1
            ORDER BY so.create_time DESC
            LIMIT 20
            """);
        ResultSet rs = ps.executeQuery()) {
   
       ResultSetMetaData md = rs.getMetaData();
   
       // Actual in SQL Federation: org.apache.calcite.sql.type.SqlTypeName
       // Expected for BIGINT id: java.lang.Long
       System.out.println(md.getColumnClassName(1));
   }
   ```
   
   ### Suggested fix
   
   `SQLFederationResultSetMetaData#getColumnClassName` should map Calcite 
`SqlTypeName` or `RelDataType` to the correct JDBC Java class name.
   
   Possible mapping examples:
   
   - BIGINT -> `java.lang.Long`
   - INTEGER -> `java.lang.Integer`
   - VARCHAR/CHAR -> `java.lang.String`
   - DECIMAL -> `java.math.BigDecimal`
   - BOOLEAN -> `java.lang.Boolean`
   - DATE/TIME/TIMESTAMP -> corresponding JDBC date/time classes
   - Unknown fallback -> `java.lang.Object`
   
   The method should not return `getSqlTypeName().getClass().getName()`, 
because that returns the enum implementation class, not the result column Java 
class.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: 
[email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to