apache/shardingsphere · error · FirebirdProtocolException

BLOB fields are not supported in Firebird batch operations

Error message

BLOB fields are not supported in Firebird batch operations

What it means

SQLFeatureNotSupportedException thrown by updateClob(String, Clob) on SQL federation ResultSets. The column-label overload of updateClob is final and throws in AbstractUnsupportedUpdateOperationSQLFederationResultSet, matching the index overload. SQL federation only materializes read-only merged results, so attaching a Clob to a named column of the current row cannot be honored.

Source

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

            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");
            }
        }
    }
    
    private static FirebirdParseBatchBlr parseFormat(final ByteBuf buffer) {
        int count = buffer.readUnsignedByte();
        count += buffer.readUnsignedByte() << 8;
        int columnCount = count / 2;
        List<FirebirdBatchColumnDescriptor> fields = new ArrayList<>(columnCount);
        int offset = 0;
        int netLength = 0;
        for (int i = 0; i < columnCount; i++) {
            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())

View on GitHub (pinned to e952770a21)

Solutions

  1. Use PreparedStatement UPDATE with setClob/setCharacterStream bound by column name in SQL
  2. Guard cursor-edit paths with rs.getConcurrency() and fall back to DML when read-only
  3. Rewrite the query or federation config so the statement is not federated when edits are needed

Example fix

// before
rs.updateClob("body", clob); // SQLFeatureNotSupportedException

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

Strategy: try-catch

Validate before calling

if (rs.getConcurrency() == ResultSet.CONCUR_READ_ONLY) {
    // label-based updateClob unsupported: use UPDATE + setClob
}

Try / catch

try {
    rs.updateClob("body", clob);
} catch (SQLFeatureNotSupportedException e) {
    // fall back to UPDATE DML keyed by primary key
}

Prevention

When it happens

Trigger: Calling rs.updateClob("body", clob) on a federated ResultSet and then rs.updateRow(). Encountered in label-based text persistence code.

Common situations: CMS or auditing features patching large text columns in place; frameworks mapping fields by label during flush; driver/proxy migration to ShardingSphere without auditing updatable-ResultSet usage.

Related errors


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