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:
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:
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:
D:\source\shardingsphere\shardingsphere-5.5.3\kernel\sql-federation\core\src\main\java\org\apache\shardingsphere\sqlfederation\resultset\SQLFederationResultSetMetaData.java
Current implementation:
@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:
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:
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:
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:
try (ResultSet rs = ps.executeQuery()) {
ResultSetMetaData md = rs.getMetaData();
System.out.println(md.getColumnClassName(1));
}
Expected:
Actual:
org.apache.calcite.sql.type.SqlTypeName
Example codes for reproduce this issue (such as a github link).
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.
Bug Report
Which version of ShardingSphere did you use?
Apache ShardingSphere-JDBC 5.5.3.
Related modules:
shardingsphere-jdbc: 5.5.3shardingsphere-jdbc-dialect-mysql: 5.5.3shardingsphere-sharding-mysql: 5.5.3Environment:
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:
java.lang.Longjava.lang.Stringjava.math.BigDecimalActual behavior
ResultSetMetaData#getColumnClassName(column)returns: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 classSqlTypeName, MyBatis/MyBatis-Plus chooses an enum type handler and tries to parse the actual database value as aSqlTypeNameenum constant.Stack trace excerpt:
Reason analyze (If you can)
I checked the ShardingSphere 5.5.3 source locally.
Source location:
Current implementation:
getSqlTypeName()returns a Calcite enum value. CallinggetClass().getName()on that enum value always returns: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:
Run a SQL Federation query:
Then inspect metadata:
Expected:
Actual:
Example codes for reproduce this issue (such as a github link).
Suggested fix
SQLFederationResultSetMetaData#getColumnClassNameshould map CalciteSqlTypeNameorRelDataTypeto the correct JDBC Java class name.Possible mapping examples:
java.lang.Longjava.lang.Integerjava.lang.Stringjava.math.BigDecimaljava.lang.Booleanjava.lang.ObjectThe method should not return
getSqlTypeName().getClass().getName(), because that returns the enum implementation class, not the result column Java class.