apache/shardingsphere · error · IllegalArgumentException

Expected blr_short NULL indicator, got: %s

Error message

Expected blr_short NULL indicator, got: %s

What it means

SQLFeatureNotSupportedException thrown by updateClob(int, Reader) on SQL federation ResultSets. This reader-based overload is one of six updateClob signatures, all implemented as final throwing methods in AbstractUnsupportedUpdateOperationSQLFederationResultSet. Since federated ResultSets are read-only in-memory merges, character data cannot be streamed into the current row; the Reader is never consumed and must be managed by the caller.

Source

Thrown at database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/batch/FirebirdParseBatchBlr.java:146

            int blrType = buffer.readUnsignedByte();
            FirebirdBatchColumnDescriptor descriptor = readDescriptor(buffer, blrType);
            offset = alignTo(offset, alignmentOf(blrType));
            final int fieldOffset = offset;
            offset += descriptor.getLength();
            netLength += isVarying(descriptor.getType())
                    ? HEADER_LENGTH + alignTo(descriptor.getLength() - Short.BYTES, 4)
                    : alignTo(descriptor.getLength(), 4);
            offset = appendNullIndicator(buffer, offset);
            fields.add(new FirebirdBatchColumnDescriptor(descriptor.getType(), descriptor.getLength(), descriptor.getScale(), fieldOffset));
        }
        return new FirebirdParseBatchBlr(fields, offset, netLength);
    }
    
    private static int appendNullIndicator(final ByteBuf buffer, final int offset) {
        int nullType = buffer.readUnsignedByte();
        buffer.skipBytes(1);
        if (BlrConstants.blr_short != nullType) {
            throw new IllegalArgumentException("Expected blr_short NULL indicator, got: " + nullType);
        }
        return alignTo(offset, Short.BYTES) + Short.BYTES;
    }
    
    private static boolean isVarying(final FirebirdBinaryColumnType type) {
        return FirebirdBinaryColumnType.VARYING == type || FirebirdBinaryColumnType.LEGACY_VARYING == type;
    }
    
    private static FirebirdBatchColumnDescriptor readDescriptor(final ByteBuf buffer, final int blrType) {
        if (BlrConstants.blr_blob2 == blrType) {
            buffer.skipBytes(4);
            return new FirebirdBatchColumnDescriptor(FirebirdBinaryColumnType.BLOB, Long.BYTES, 0, 0);
        }
        if (BlrConstants.blr_quad == blrType) {
            int scale = buffer.readByte();
            return new FirebirdBatchColumnDescriptor(FirebirdBinaryColumnType.BLOB, Long.BYTES, scale, 0);
        }
        FirebirdBinaryColumnType type = FirebirdBinaryColumnType.valueOfBLRType(blrType);

View on GitHub (pinned to e952770a21)

Solutions

  1. Replace with UPDATE ... SET col = ? WHERE ... using ps.setCharacterStream so the Reader feeds DML directly
  2. Ensure the Reader is closed by caller code since the throwing call does not consume it
  3. Check rs.getConcurrency() before editing and select the DML path for read-only cursors
  4. Keep editable statements out of federation via SQL rewrite or configuration

Example fix

// before
rs.updateClob(2, stringReader); // SQLFeatureNotSupportedException
rs.updateRow();

// after
try (PreparedStatement ps = conn.prepareStatement("UPDATE docs SET body = ? WHERE id = ?"); Reader r = stringReader) {
    ps.setCharacterStream(1, r);
    ps.setLong(2, rs.getLong("id"));
    ps.executeUpdate();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (rs.getConcurrency() != ResultSet.CONCUR_UPDATABLE) {
    // reader-based CLOB write unsupported: use UPDATE + setCharacterStream
}

Try / catch

try {
    rs.updateClob(columnIndex, reader);
} catch (SQLFeatureNotSupportedException e) {
    // reader not consumed: close it, then write via UPDATE DML
}

Prevention

When it happens

Trigger: Calling rs.updateClob(columnIndex, reader) on a federation ResultSet then rs.updateRow(). Typical when text is produced as a Reader (e.g. from a template engine or file) and piped into a CLOB column in place.

Common situations: Streaming text pipelines that previously ran on CONCUR_UPDATABLE ResultSets; ShardingSphere introduced into an existing application; code paths that mix read-then-edit within one iteration over a federated JOIN.

Related errors


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