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]