apache/shardingsphere · error · IllegalArgumentException

Expected blr_end

Error message

Expected blr_end

What it means

SQLFeatureNotSupportedException thrown by updateBlob(int, InputStream, long) on SQL federation ResultSets. The length-parameterized stream overload is one of the six updateBlob signatures, all final and throwing in AbstractUnsupportedUpdateOperationSQLFederationResultSet. Because federated ResultSets are read-only, the length hint never matters: the operation is refused before the stream is touched. This mirrors the JDBC 3-era pattern of bounded BLOB writes, which federation simply does not offer.

Source

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

        int version = buffer.readUnsignedByte();
        if (BlrConstants.blr_version4 != version && BlrConstants.blr_version5 != version) {
            throw new IllegalArgumentException("Unsupported BLR version: " + version);
        }
        if (BlrConstants.blr_begin != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_begin");
        }
        if (BlrConstants.blr_message != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_message");
        }
        buffer.skipBytes(1);
        FirebirdParseBatchBlr result = parseFormat(buffer);
        validateTermination(buffer, startReaderIndex, blrLength);
        return result;
    }
    
    private static void validateTermination(final ByteBuf buffer, final int startReaderIndex, final int blrLength) {
        if (remainingWithinBlr(buffer, startReaderIndex, blrLength) < 1 || BlrConstants.blr_end != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_end");
        }
        if (remainingWithinBlr(buffer, startReaderIndex, blrLength) < 1 || BlrConstants.blr_eoc != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_eoc");
        }
        if (0 != remainingWithinBlr(buffer, startReaderIndex, blrLength)) {
            throw new IllegalArgumentException("Unexpected trailing bytes in BLR");
        }
    }
    
    private static int remainingWithinBlr(final ByteBuf buffer, final int startReaderIndex, final int blrLength) {
        return blrLength - (buffer.readerIndex() - startReaderIndex);
    }
    
    private static void validateSupported(final List<FirebirdBatchColumnDescriptor> fields) {
        for (FirebirdBatchColumnDescriptor each : fields) {
            if (FirebirdBinaryColumnType.BLOB == each.getType()) {
                // TODO Implement BATCH_REGBLOB, BATCH_BLOB_STREAM and BATCH_SET_BPB before accepting BLOB fields.
                throw new FirebirdProtocolException("BLOB fields are not supported in Firebird batch operations");

View on GitHub (pinned to e952770a21)

Solutions

  1. Use UPDATE with ps.setBinaryStream(index, stream, length) to preserve the bounded-write semantics
  2. Check getConcurrency() before planning cursor edits; fall back to DML when CONCUR_READ_ONLY
  3. Re-route the query away from federation if in-place edits are a hard requirement

Example fix

// before
rs.updateBlob(2, in, fileSize); // SQLFeatureNotSupportedException

// after
try (PreparedStatement ps = conn.prepareStatement("UPDATE files SET content = ? WHERE id = ?")) {
    ps.setBinaryStream(1, in, fileSize);
    ps.setLong(2, rs.getLong("id"));
    ps.executeUpdate();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (rs.getConcurrency() != ResultSet.CONCUR_UPDATABLE) {
    // bounded stream write unsupported: use UPDATE + setBinaryStream(1, in, length)
}

Try / catch

try {
    rs.updateBlob(columnIndex, inputStream, length);
} catch (SQLFeatureNotSupportedException e) {
    // fall back to bounded setBinaryStream via DML
}

Prevention

When it happens

Trigger: Calling rs.updateBlob(columnIndex, inputStream, length) on a federation ResultSet then rs.updateRow(). Encountered in code that knows the exact byte count (e.g. a staged temp file) and uses the bounded API.

Common situations: Upload code migrated from older drivers where the bounded overload was the only stream option; batch importers writing known-size binaries in place; ShardingSphere introduced into an existing stack without a cursor-usage audit.

Related errors


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