apache/shardingsphere · error · SQLFeatureNotSupportedException

`%s` cannot be unwrapped as `%s`

Error message

`%s` cannot be unwrapped as `%s`

What it means

RawResultSetMetaData implements the standard JDBC Wrapper contract minimally: unwrap(iface) succeeds only when iface.isInstance(this) — i.e. only RawResultSetMetaData itself or its supertypes. Requesting any other interface (JDBC4ResultSetMetaData, driver-specific metadata, etc.) throws SQLFeatureNotSupportedException with the class names formatted into the message. Raw metadata simply does not wrap any richer implementation.

Source

Thrown at infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/raw/metadata/RawResultSetMetaData.java:208

                return String.class.getName();
            default:
                return Object.class.getName();
        }
    }
    
    private RawQueryResultColumnMetaData getColumnMetaData(final int column) throws SQLException {
        if (column < 1 || column > columns.size()) {
            throw new SQLException(String.format("Column index out of range: %s", column));
        }
        return columns.get(column - 1);
    }
    
    @Override
    public <T> T unwrap(final Class<T> iface) throws SQLException {
        if (isWrapperFor(iface)) {
            return iface.cast(this);
        }
        throw new SQLFeatureNotSupportedException(String.format("`%s` cannot be unwrapped as `%s`", getClass().getName(), iface.getName()));
    }
    
    @Override
    public boolean isWrapperFor(final Class<?> iface) {
        return iface.isInstance(this);
    }
}

View on GitHub (pinned to e952770a21)

Solutions

  1. Call isWrapperFor(iface) first and skip the vendor-specific path when it returns false.
  2. Do vendor-specific metadata access on a connection that bypasses ShardingSphere, or accept the generic metadata interface only.
  3. Replace unwrap-based feature detection with SQL-level capability checks (DatabaseMetaData) that ShardingSphere forwards.

Example fix

// before
PgResultSetMetaData md = resultSet.getMetaData().unwrap(PgResultSetMetaData.class);

// after
ResultSetMetaData md = resultSet.getMetaData();
if (md.isWrapperFor(PgResultSetMetaData.class)) {
    // vendor path on a direct driver connection only
} else {
    // generic path: md.getColumnName(i), md.getColumnType(i), ...
}
Defensive patterns

Strategy: type-guard

Type guard

if (metaData.isWrapperFor(PgResultSetMetaData.class)) { ... } // only then unwrap

Try / catch

try { return metaData.unwrap(target); } catch (SQLFeatureNotSupportedException e) { /* fall back to generic ResultSetMetaData APIs */ }

Prevention

When it happens

Trigger: Calling resultSet.getMetaData().unwrap(org.postgresql.jdbc.PgResultSetMetaData.class) or unwrap(com.mysql.cj.jdbc.result.ResultSetMetaData.class) through ShardingSphere; generic framework code that tries unwrap(X) before falling back, where X is never the raw metadata class.

Common situations: Code migrated from a direct driver connection where unwrap of the driver metadata type worked; Spring/JDBC helper libraries probing for vendor-specific metadata to read vendor columns; tests asserting vendor metadata behavior through the ShardingSphere layer.

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/ffcf0d7462744399. Report an issue: GitHub.