This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 46ad251d27a Support op_seek_blob operation for stream BLOBs in 
Firebird Proxy (#39984)
46ad251d27a is described below

commit 46ad251d27aed18dd30ca1818f3844f8f6d7ab82
Author: rishi-rana <[email protected]>
AuthorDate: Sat Oct 10 08:43:36 2026 -0700

    Support op_seek_blob operation for stream BLOBs in Firebird Proxy (#39984)
    
    * Support op_seek_blob operation for stream BLOBs in Firebird Proxy
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * Point release note at the pull request
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * Respect declared BPB item lengths and accept an empty BPB in Firebird 
create blob
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
    
    * Fix remaining testing-standard violations in Firebird BPB and seek tests
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 5 <[email protected]>
---
 RELEASE-NOTES.md                                   |   1 +
 .../protocol/InvalidSegstrTypeException.java       |  27 ++--
 .../mapper/FirebirdDialectExceptionMapper.java     |   4 +
 .../firebird/vendor/FirebirdVendorError.java       |   4 +-
 .../protocol/InvalidSegstrTypeExceptionTest.java   |  33 +++++
 .../mapper/FirebirdDialectExceptionMapperTest.java |   6 +
 .../firebird/constant/FirebirdValueFormat.java     |   4 +-
 .../constant/buffer/FirebirdParameterBuffer.java   |  10 ++
 .../type/FirebirdBlobParameterBufferType.java      |  91 +++++++++++++
 .../blob/FirebirdCreateBlobCommandPacket.java      |  25 +++-
 .../type/blob/FirebirdBlobInfoReturnPacket.java    |   6 +-
 .../buffer/FirebirdParameterBufferTest.java        |  27 ++++
 .../type/FirebirdBlobParameterBufferTypeTest.java  |  42 +++---
 .../blob/FirebirdCreateBlobCommandPacketTest.java  |  37 ++++-
 .../blob/FirebirdBlobInfoReturnPacketTest.java     |  24 ++--
 .../command/FirebirdCommandExecutorFactory.java    |   2 +-
 .../blob/FirebirdCreateBlobCommandExecutor.java    |   2 +-
 .../blob/FirebirdOpenBlobCommandExecutor.java      |  14 +-
 .../blob/FirebirdSeekBlobCommandExecutor.java      |  26 +++-
 .../query/blob/cache/FirebirdBlobReadCache.java    |  73 ++++++++--
 .../query/blob/cache/FirebirdBlobWrite.java        |   3 +
 .../query/blob/cache/FirebirdBlobWriteCache.java   |  17 ++-
 .../query/info/FirebirdBlobInfoExecutor.java       |  19 ++-
 ...rebirdBatchBlobSegmentsCommandExecutorTest.java |   8 +-
 .../FirebirdCancelBlobCommandExecutorTest.java     |   2 +-
 .../blob/FirebirdCloseBlobCommandExecutorTest.java |   2 +-
 .../FirebirdCreateBlobCommandExecutorTest.java     |   9 ++
 .../FirebirdGetBlobSegmentCommandExecutorTest.java |  23 ++--
 .../blob/FirebirdOpenBlobCommandExecutorTest.java  |  35 ++++-
 .../FirebirdPutBlobSegmentCommandExecutorTest.java |  10 +-
 .../blob/FirebirdSeekBlobCommandExecutorTest.java  |  94 ++++++++++++-
 .../blob/cache/FirebirdBlobReadCacheTest.java      | 149 +++++++++++++++++----
 .../blob/cache/FirebirdBlobWriteCacheTest.java     |   8 +-
 .../query/info/FirebirdBlobInfoExecutorTest.java   |  18 ++-
 ...irebirdExecuteStatementCommandExecutorTest.java |   4 +-
 35 files changed, 710 insertions(+), 149 deletions(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 6fba64e9ecb..975bd92f717 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -11,6 +11,7 @@
 1. MCP: Add standalone ShardingSphere-MCP Server - 
[#38541](https://github.com/apache/shardingsphere/pull/38541)
 1. JDBC: Support executing DistSQL - 
[#37761](https://github.com/apache/shardingsphere/issues/37761)
 1. Transaction: Add optional module for Jakarta Transactions impl of Jakarta 
EE 9 Spec - [#26041](https://github.com/apache/shardingsphere/issues/26041)
+1. Proxy: Support `op_seek_blob` operation for stream BLOBs in Firebird - 
[#39984](https://github.com/apache/shardingsphere/pull/39984)
 
 ### CVE
 
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
 
b/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/exception/protocol/InvalidSegstrTypeException.java
similarity index 51%
copy from 
proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
copy to 
database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/exception/protocol/InvalidSegstrTypeException.java
index c59d501215c..122d1c7f95e 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
+++ 
b/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/exception/protocol/InvalidSegstrTypeException.java
@@ -15,26 +15,23 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob;
+package 
org.apache.shardingsphere.database.exception.firebird.exception.protocol;
 
-import lombok.RequiredArgsConstructor;
-import 
org.apache.shardingsphere.database.exception.core.exception.protocol.DatabaseProtocolException;
-import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.blob.FirebirdSeekBlobCommandPacket;
-import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
-import 
org.apache.shardingsphere.proxy.frontend.command.executor.CommandExecutor;
-
-import java.util.Collection;
+import lombok.Getter;
+import 
org.apache.shardingsphere.database.exception.core.exception.SQLDialectException;
 
 /**
- * Seek blob command executor for Firebird.
+ * Invalid BLOB type exception for Firebird.
  */
-@RequiredArgsConstructor
-public final class FirebirdSeekBlobCommandExecutor implements CommandExecutor {
+@Getter
+public final class InvalidSegstrTypeException extends SQLDialectException {
+    
+    private static final long serialVersionUID = -8377291875421953215L;
     
-    private final FirebirdSeekBlobCommandPacket packet;
+    private final int blobHandle;
     
-    @Override
-    public Collection<DatabasePacket> execute() {
-        throw new DatabaseProtocolException("SEEK BLOB is not supported at the 
moment, blob handle: %d", packet.getBlobHandle());
+    public InvalidSegstrTypeException(final int blobHandle) {
+        super(String.format("Invalid BLOB type for operation on BLOB with 
handle: %d", blobHandle));
+        this.blobHandle = blobHandle;
     }
 }
diff --git 
a/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapper.java
 
b/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapper.java
index 353c7adf429..4854d59a53b 100644
--- 
a/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapper.java
+++ 
b/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapper.java
@@ -36,6 +36,7 @@ import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidBatchParameterVersionException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrHandleException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrIdException;
+import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrTypeException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidStatementHandleException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidTransactionHandleException;
 import 
org.apache.shardingsphere.database.exception.firebird.vendor.FirebirdVendorError;
@@ -110,6 +111,9 @@ public final class FirebirdDialectExceptionMapper 
implements SQLDialectException
         if (sqlDialectException instanceof CannotUpdateOldBlobException) {
             return toSQLException(FirebirdVendorError.CANNOT_UPDATE_OLD_BLOB);
         }
+        if (sqlDialectException instanceof InvalidSegstrTypeException) {
+            return toSQLException(FirebirdVendorError.INVALID_SEGSTR_TYPE);
+        }
         return new UnknownSQLException(sqlDialectException).toSQLException();
     }
     
diff --git 
a/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/vendor/FirebirdVendorError.java
 
b/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/vendor/FirebirdVendorError.java
index 66898d4e2e3..3c4bebaac1b 100644
--- 
a/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/vendor/FirebirdVendorError.java
+++ 
b/database/exception/dialect/firebird/src/main/java/org/apache/shardingsphere/database/exception/firebird/vendor/FirebirdVendorError.java
@@ -72,7 +72,9 @@ public enum FirebirdVendorError implements VendorError {
     
     INVALID_SEGSTR_ID(XOpenSQLState.SYNTAX_ERROR, 
ISCConstants.isc_bad_segstr_id, "invalid BLOB ID"),
     
-    CANNOT_UPDATE_OLD_BLOB(XOpenSQLState.SYNTAX_ERROR, 
ISCConstants.isc_cannot_update_old_blob, "cannot update old BLOB");
+    CANNOT_UPDATE_OLD_BLOB(XOpenSQLState.SYNTAX_ERROR, 
ISCConstants.isc_cannot_update_old_blob, "cannot update old BLOB"),
+    
+    INVALID_SEGSTR_TYPE(XOpenSQLState.SYNTAX_ERROR, 
ISCConstants.isc_bad_segstr_type, "invalid BLOB type for operation");
     
     private final SQLState sqlState;
     
diff --git 
a/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/exception/protocol/InvalidSegstrTypeExceptionTest.java
 
b/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/exception/protocol/InvalidSegstrTypeExceptionTest.java
new file mode 100644
index 00000000000..538caabf87b
--- /dev/null
+++ 
b/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/exception/protocol/InvalidSegstrTypeExceptionTest.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.database.exception.firebird.exception.protocol;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+class InvalidSegstrTypeExceptionTest {
+    
+    @Test
+    void assertGetMessage() {
+        InvalidSegstrTypeException actual = new InvalidSegstrTypeException(42);
+        assertThat(actual.getMessage(), is("Invalid BLOB type for operation on 
BLOB with handle: 42"));
+        assertThat(actual.getBlobHandle(), is(42));
+    }
+}
diff --git 
a/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapperTest.java
 
b/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapperTest.java
index c288e9cda49..f94379c3fa2 100644
--- 
a/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapperTest.java
+++ 
b/database/exception/dialect/firebird/src/test/java/org/apache/shardingsphere/database/exception/firebird/mapper/FirebirdDialectExceptionMapperTest.java
@@ -38,6 +38,7 @@ import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidBatchParameterVersionException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrHandleException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrIdException;
+import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrTypeException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidStatementHandleException;
 import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidTransactionHandleException;
 import 
org.apache.shardingsphere.database.exception.firebird.vendor.FirebirdVendorError;
@@ -148,6 +149,11 @@ class FirebirdDialectExceptionMapperTest {
         assertSQLException(mapper.convert(new InvalidSegstrIdException(99L)), 
FirebirdVendorError.INVALID_SEGSTR_ID);
     }
     
+    @Test
+    void assertConvertWithInvalidSegstrType() {
+        assertSQLException(mapper.convert(new InvalidSegstrTypeException(42)), 
FirebirdVendorError.INVALID_SEGSTR_TYPE);
+    }
+    
     @Test
     void assertConvertWithCannotUpdateOldBlob() {
         assertSQLException(mapper.convert(new 
CannotUpdateOldBlobException(42)), FirebirdVendorError.CANNOT_UPDATE_OLD_BLOB);
diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/FirebirdValueFormat.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/FirebirdValueFormat.java
index 8961fed3281..8a3a0449398 100644
--- 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/FirebirdValueFormat.java
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/FirebirdValueFormat.java
@@ -36,7 +36,9 @@ public enum FirebirdValueFormat {
     
     INT(3),
     
-    STRING(4);
+    STRING(4),
+    
+    SIZED_INT(5);
     
     private final int code;
     
diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBuffer.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBuffer.java
index 56f0c81dc17..5ad66bee4d7 100644
--- 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBuffer.java
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBuffer.java
@@ -65,6 +65,8 @@ public final class FirebirdParameterBuffer {
                     parameterBuffer.skipBytes(4);
                 }
                 return parameterBuffer.readIntLE();
+            case SIZED_INT:
+                return readSizedIntValue(parameterBuffer, traditionalStyle ? 
parameterBuffer.readUnsignedByte() : parameterBuffer.readIntLE());
             case BOOLEAN:
                 return true;
             case STRING:
@@ -75,6 +77,14 @@ public final class FirebirdParameterBuffer {
         }
     }
     
+    private int readSizedIntValue(final ByteBuf parameterBuffer, final int 
length) {
+        int result = 0;
+        for (int i = 0; i < length; i++) {
+            result |= parameterBuffer.readUnsignedByte() << (i * Byte.SIZE);
+        }
+        return result;
+    }
+    
     /**
      * Get property value.
      *
diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/type/FirebirdBlobParameterBufferType.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/type/FirebirdBlobParameterBufferType.java
new file mode 100644
index 00000000000..5b0ec3ca23a
--- /dev/null
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/type/FirebirdBlobParameterBufferType.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.database.protocol.firebird.constant.buffer.type;
+
+import com.google.common.base.Preconditions;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import 
org.apache.shardingsphere.database.protocol.firebird.constant.FirebirdValueFormat;
+import 
org.apache.shardingsphere.database.protocol.firebird.constant.buffer.FirebirdParameterBuffer;
+import 
org.apache.shardingsphere.database.protocol.firebird.constant.buffer.FirebirdParameterBufferType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Firebird blob parameter buffer type.
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FirebirdBlobParameterBufferType implements 
FirebirdParameterBufferType {
+    
+    SOURCE_TYPE(1),
+    TARGET_TYPE(2),
+    TYPE(3),
+    SOURCE_INTERP(4),
+    TARGET_INTERP(5),
+    FILTER_PARAMETER(6),
+    STORAGE(7);
+    
+    private static final Map<Integer, FirebirdBlobParameterBufferType> 
FIREBIRD_BPB_TYPE_CACHE = new HashMap<>(values().length, 1F);
+    
+    private final int code;
+    
+    private final FirebirdValueFormat format;
+    
+    static {
+        for (FirebirdBlobParameterBufferType each : values()) {
+            FIREBIRD_BPB_TYPE_CACHE.put(each.code, each);
+        }
+    }
+    
+    FirebirdBlobParameterBufferType(final int code) {
+        this(code, FirebirdValueFormat.SIZED_INT);
+    }
+    
+    /**
+     * Value of.
+     *
+     * @param code bpb type code
+     * @return Firebird bpb type
+     */
+    public static FirebirdBlobParameterBufferType valueOf(final int code) {
+        FirebirdBlobParameterBufferType result = 
FIREBIRD_BPB_TYPE_CACHE.get(code);
+        Preconditions.checkNotNull(result, "Cannot find code '%d' in bpb 
type", code);
+        return result;
+    }
+    
+    /**
+     * Decides whether to use a traditional type for integers.
+     *
+     * @param version version of parameter buffer
+     * @return is traditional type
+     */
+    public static boolean isTraditionalType(final int version) {
+        return 1 == version;
+    }
+    
+    /**
+     * Creates parameter buffer of this type.
+     *
+     * @return Firebird blob parameter buffer
+     */
+    public static FirebirdParameterBuffer createBuffer() {
+        return new 
FirebirdParameterBuffer(FirebirdBlobParameterBufferType::valueOf, 
FirebirdBlobParameterBufferType::isTraditionalType);
+    }
+}
diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacket.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacket.java
index 496f23ee15b..c45d3d03186 100644
--- 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacket.java
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacket.java
@@ -19,6 +19,8 @@ package 
org.apache.shardingsphere.database.protocol.firebird.packet.command.quer
 
 import io.netty.buffer.ByteBuf;
 import lombok.Getter;
+import 
org.apache.shardingsphere.database.protocol.firebird.constant.buffer.FirebirdParameterBuffer;
+import 
org.apache.shardingsphere.database.protocol.firebird.constant.buffer.type.FirebirdBlobParameterBufferType;
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.FirebirdCommandPacket;
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.FirebirdCommandPacketType;
 import 
org.apache.shardingsphere.database.protocol.firebird.payload.FirebirdPacketPayload;
@@ -29,7 +31,9 @@ import 
org.apache.shardingsphere.database.protocol.firebird.payload.FirebirdPack
 @Getter
 public final class FirebirdCreateBlobCommandPacket extends 
FirebirdCommandPacket {
     
-    private final byte[] blobParameterBuffer;
+    private static final int BLOB_TYPE_STREAM = 1;
+    
+    private final FirebirdParameterBuffer bpb = 
FirebirdBlobParameterBufferType.createBuffer();
     
     private final int transactionId;
     
@@ -39,15 +43,26 @@ public final class FirebirdCreateBlobCommandPacket extends 
FirebirdCommandPacket
         payload.skipReserved(4);
         if (FirebirdCommandPacketType.CREATE_BLOB2 == commandType) {
             ByteBuf buffer = payload.readBuffer();
-            blobParameterBuffer = new byte[buffer.readableBytes()];
-            buffer.readBytes(blobParameterBuffer);
-        } else {
-            blobParameterBuffer = new byte[0];
+            if (buffer.isReadable()) {
+                bpb.parseBuffer(buffer);
+            }
         }
         transactionId = payload.readInt4();
         requestedBlobId = payload.readInt8();
     }
     
+    /**
+     * Is stream blob.
+     *
+     * <p>A BLOB without a blob parameter buffer, with an empty one, or with 
one that omits the type item, is a segmented BLOB.</p>
+     *
+     * @return stream blob or not
+     */
+    public boolean isStreamBlob() {
+        Integer blobType = bpb.getValue(FirebirdBlobParameterBufferType.TYPE);
+        return null != blobType && BLOB_TYPE_STREAM == blobType;
+    }
+    
     @Override
     protected void write(final FirebirdPacketPayload payload) {
     }
diff --git 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacket.java
 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacket.java
index 025bb7fd017..53ef0b1848f 100644
--- 
a/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacket.java
+++ 
b/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacket.java
@@ -36,10 +36,14 @@ public final class FirebirdBlobInfoReturnPacket extends 
FirebirdPacket {
     
     private static final int BLOB_TYPE_SEGMENTED = 0;
     
+    private static final int BLOB_TYPE_STREAM = 1;
+    
     private final List<FirebirdInfoPacketType> infoItems;
     
     private final int blobLength;
     
+    private final boolean streamBlob;
+    
     @Override
     protected void write(final FirebirdPacketPayload payload) {
         for (FirebirdInfoPacketType type : infoItems) {
@@ -65,7 +69,7 @@ public final class FirebirdBlobInfoReturnPacket extends 
FirebirdPacket {
                 writeIntValue(payload, type, getSegmentLength());
                 return;
             case TYPE:
-                writeIntValue(payload, type, BLOB_TYPE_SEGMENTED);
+                writeIntValue(payload, type, streamBlob ? BLOB_TYPE_STREAM : 
BLOB_TYPE_SEGMENTED);
                 return;
             default:
                 throw new DatabaseProtocolException("Unknown blob information 
request type %d", type.getCode());
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBufferTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBufferTest.java
index 32c508771cc..101ce35f23f 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBufferTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/FirebirdParameterBufferTest.java
@@ -96,6 +96,33 @@ class FirebirdParameterBufferTest {
         assertThat(parameterBuffer.getValue(stringType), is(expectedString));
     }
     
+    @Test
+    void assertParseTraditionalBufferWithSizedIntValues() {
+        when(intType.getFormat()).thenReturn(FirebirdValueFormat.SIZED_INT);
+        
when(booleanType.getFormat()).thenReturn(FirebirdValueFormat.SIZED_INT);
+        ByteBuf buffer = Unpooled.buffer();
+        buffer.writeByte(1);
+        buffer.writeBytes(new byte[]{1, 1, 7});
+        buffer.writeBytes(new byte[]{2, 2, (byte) 0xD2, 0x04});
+        FirebirdParameterBuffer parameterBuffer = new 
FirebirdParameterBuffer(createValueOf(), version -> 1 == version);
+        parameterBuffer.parseBuffer(buffer);
+        assertThat(parameterBuffer.<Integer>getValue(intType), is(7));
+        assertThat(parameterBuffer.<Integer>getValue(booleanType), is(1234));
+    }
+    
+    @Test
+    void assertParseExtendedBufferWithSizedIntValue() {
+        when(intType.getFormat()).thenReturn(FirebirdValueFormat.SIZED_INT);
+        ByteBuf buffer = Unpooled.buffer();
+        buffer.writeByte(2);
+        buffer.writeByte(1);
+        buffer.writeIntLE(2);
+        buffer.writeBytes(new byte[]{(byte) 0xD2, 0x04});
+        FirebirdParameterBuffer parameterBuffer = new 
FirebirdParameterBuffer(createValueOf(), version -> 1 == version);
+        parameterBuffer.parseBuffer(buffer);
+        assertThat(parameterBuffer.<Integer>getValue(intType), is(1234));
+    }
+    
     @Test
     void assertParseUnsupportedFormat() {
         
when(unsupportedType.getFormat()).thenReturn(FirebirdValueFormat.BINARY);
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/type/FirebirdBlobParameterBufferTypeTest.java
similarity index 50%
copy from 
proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
copy to 
database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/type/FirebirdBlobParameterBufferTypeTest.java
index 015af357624..91601fd105a 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/constant/buffer/type/FirebirdBlobParameterBufferTypeTest.java
@@ -15,31 +15,41 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob;
+package 
org.apache.shardingsphere.database.protocol.firebird.constant.buffer.type;
 
-import 
org.apache.shardingsphere.database.exception.core.exception.protocol.DatabaseProtocolException;
-import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.blob.FirebirdSeekBlobCommandPacket;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.when;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-@ExtendWith(MockitoExtension.class)
-class FirebirdSeekBlobCommandExecutorTest {
+class FirebirdBlobParameterBufferTypeTest {
     
-    @Mock
-    private FirebirdSeekBlobCommandPacket packet;
+    @Test
+    void assertValueOf() {
+        assertThat(FirebirdBlobParameterBufferType.valueOf(3), 
is(FirebirdBlobParameterBufferType.TYPE));
+    }
+    
+    @Test
+    void assertValueOfWithUnknownCode() {
+        assertThrows(NullPointerException.class, () -> 
FirebirdBlobParameterBufferType.valueOf(999));
+    }
+    
+    @Test
+    void assertIsTraditionalType() {
+        assertTrue(FirebirdBlobParameterBufferType.isTraditionalType(1));
+    }
+    
+    @Test
+    void assertIsTraditionalTypeWithNonTraditionalVersion() {
+        assertFalse(FirebirdBlobParameterBufferType.isTraditionalType(2));
+    }
     
     @Test
-    void assertExecuteThrowsUnsupported() {
-        when(packet.getBlobHandle()).thenReturn(42);
-        FirebirdSeekBlobCommandExecutor executor = new 
FirebirdSeekBlobCommandExecutor(packet);
-        DatabaseProtocolException actual = 
assertThrows(DatabaseProtocolException.class, executor::execute);
-        assertThat(actual.getMessage(), is("SEEK BLOB is not supported at the 
moment, blob handle: 42"));
+    void assertCreateBuffer() {
+        assertNotNull(FirebirdBlobParameterBufferType.createBuffer());
     }
 }
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacketTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacketTest.java
index c9fe9b34168..be2fc080316 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacketTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/blob/FirebirdCreateBlobCommandPacketTest.java
@@ -22,11 +22,18 @@ import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.Fireb
 import 
org.apache.shardingsphere.database.protocol.firebird.payload.FirebirdPacketPayload;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.util.stream.Stream;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -47,7 +54,7 @@ class FirebirdCreateBlobCommandPacketTest {
         verify(payload, never()).readBuffer();
         assertThat(packet.getTransactionId(), is(42));
         assertThat(packet.getRequestedBlobId(), is(0x0102030405060708L));
-        assertThat(packet.getBlobParameterBuffer().length, is(0));
+        assertFalse(packet.isStreamBlob());
         packet.write(payload);
         verify(payload).readInt4();
         verify(payload).readInt8();
@@ -56,7 +63,7 @@ class FirebirdCreateBlobCommandPacketTest {
     
     @Test
     void assertCreateBlobPacketWithBpb() {
-        when(payload.readBuffer()).thenReturn(Unpooled.wrappedBuffer(new 
byte[]{1, 2, 3, 4}));
+        when(payload.readBuffer()).thenReturn(Unpooled.wrappedBuffer(new 
byte[]{1, 3, 4, 1, 0, 0, 0}));
         when(payload.readInt4()).thenReturn(7);
         when(payload.readInt8()).thenReturn(11L);
         FirebirdCreateBlobCommandPacket packet = new 
FirebirdCreateBlobCommandPacket(FirebirdCommandPacketType.CREATE_BLOB2, 
payload);
@@ -64,7 +71,31 @@ class FirebirdCreateBlobCommandPacketTest {
         verify(payload).readBuffer();
         assertThat(packet.getTransactionId(), is(7));
         assertThat(packet.getRequestedBlobId(), is(11L));
-        assertThat(packet.getBlobParameterBuffer(), is(new byte[]{1, 2, 3, 
4}));
+        assertTrue(packet.isStreamBlob());
+    }
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("blobParameterBufferCases")
+    void assertIsStreamBlob(final String name, final byte[] 
blobParameterBuffer, final boolean expectedStreamBlob) {
+        
when(payload.readBuffer()).thenReturn(Unpooled.wrappedBuffer(blobParameterBuffer));
+        FirebirdCreateBlobCommandPacket packet = new 
FirebirdCreateBlobCommandPacket(FirebirdCommandPacketType.CREATE_BLOB2, 
payload);
+        if (expectedStreamBlob) {
+            assertTrue(packet.isStreamBlob());
+        } else {
+            assertFalse(packet.isStreamBlob());
+        }
+    }
+    
+    private static Stream<Arguments> blobParameterBufferCases() {
+        return Stream.of(
+                Arguments.of("empty buffer", new byte[0], false),
+                Arguments.of("version without items", new byte[]{1}, false),
+                Arguments.of("stream type of one byte", new byte[]{1, 3, 1, 
1}, true),
+                Arguments.of("stream type of two bytes", new byte[]{1, 3, 2, 
1, 0}, true),
+                Arguments.of("stream type of four bytes", new byte[]{1, 3, 4, 
1, 0, 0, 0}, true),
+                Arguments.of("segmented type of one byte", new byte[]{1, 3, 1, 
0}, false),
+                Arguments.of("type after another item of one byte", new 
byte[]{1, 1, 1, 7, 3, 1, 1}, true),
+                Arguments.of("buffer without type item", new byte[]{1, 1, 1, 
7}, false));
     }
     
     @Test
diff --git 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacketTest.java
 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacketTest.java
index cd648b53b0f..e376fdadc94 100644
--- 
a/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacketTest.java
+++ 
b/database/protocol/dialect/firebird/src/test/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/info/type/blob/FirebirdBlobInfoReturnPacketTest.java
@@ -59,20 +59,20 @@ class FirebirdBlobInfoReturnPacketTest {
     @Test
     void assertGetInfoItems() {
         List<FirebirdInfoPacketType> expectedInfoItems = 
Collections.singletonList(FirebirdCommonInfoPacketType.END);
-        assertThat(new FirebirdBlobInfoReturnPacket(expectedInfoItems, 
0).getInfoItems(), is(expectedInfoItems));
+        assertThat(new FirebirdBlobInfoReturnPacket(expectedInfoItems, 0, 
false).getInfoItems(), is(expectedInfoItems));
     }
     
     @Test
     void assertWriteCommonInfoWithRequestedEndWritesSingleTerminator() {
-        FirebirdBlobInfoReturnPacket packet = new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(FirebirdCommonInfoPacketType.END),
 0);
+        FirebirdBlobInfoReturnPacket packet = new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(FirebirdCommonInfoPacketType.END),
 0, false);
         packet.write((PacketPayload) payload);
         verify(payload, 
times(1)).writeInt1(FirebirdCommonInfoPacketType.END.getCode());
     }
     
     @ParameterizedTest(name = "{0}")
     @MethodSource("blobInfoCases")
-    void assertWriteBlobInfo(final String name, final 
FirebirdBlobInfoPacketType type, final int blobLength, final int expectedValue) 
{
-        FirebirdBlobInfoReturnPacket packet = new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(type), blobLength);
+    void assertWriteBlobInfo(final String name, final 
FirebirdBlobInfoPacketType type, final int blobLength, final boolean 
streamBlob, final int expectedValue) {
+        FirebirdBlobInfoReturnPacket packet = new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(type), blobLength, 
streamBlob);
         packet.write((PacketPayload) payload);
         InOrder order = inOrder(payload);
         order.verify(payload).writeInt1(type.getCode());
@@ -83,7 +83,8 @@ class FirebirdBlobInfoReturnPacketTest {
     
     @Test
     void assertWriteCommonInfoWithUnsupportedType() {
-        assertThrows(DatabaseProtocolException.class, () -> new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(FirebirdCommonInfoPacketType.TRUNCATED),
 0).write((PacketPayload) payload));
+        assertThrows(DatabaseProtocolException.class,
+                () -> new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(FirebirdCommonInfoPacketType.TRUNCATED),
 0, false).write((PacketPayload) payload));
     }
     
     @Test
@@ -93,7 +94,7 @@ class FirebirdBlobInfoReturnPacketTest {
         int actualSwitchValue = switchMap[typeIndex];
         switchMap[typeIndex] = 0;
         try {
-            assertThrows(DatabaseProtocolException.class, () -> new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(FirebirdBlobInfoPacketType.TYPE),
 0).write((PacketPayload) payload));
+            assertThrows(DatabaseProtocolException.class, () -> new 
FirebirdBlobInfoReturnPacket(Collections.singletonList(FirebirdBlobInfoPacketType.TYPE),
 0, false).write((PacketPayload) payload));
         } finally {
             switchMap[typeIndex] = actualSwitchValue;
         }
@@ -106,10 +107,11 @@ class FirebirdBlobInfoReturnPacketTest {
     
     private static Stream<Arguments> blobInfoCases() {
         return Stream.of(
-                Arguments.of("NUM_SEGMENTS without data", 
FirebirdBlobInfoPacketType.NUM_SEGMENTS, 0, 0),
-                Arguments.of("NUM_SEGMENTS with data", 
FirebirdBlobInfoPacketType.NUM_SEGMENTS, 3, 1),
-                Arguments.of("MAX_SEGMENT with data", 
FirebirdBlobInfoPacketType.MAX_SEGMENT, 4, 4),
-                Arguments.of("TOTAL_LENGTH with data", 
FirebirdBlobInfoPacketType.TOTAL_LENGTH, 5, 5),
-                Arguments.of("TYPE", FirebirdBlobInfoPacketType.TYPE, 1, 0));
+                Arguments.of("NUM_SEGMENTS without data", 
FirebirdBlobInfoPacketType.NUM_SEGMENTS, 0, false, 0),
+                Arguments.of("NUM_SEGMENTS with data", 
FirebirdBlobInfoPacketType.NUM_SEGMENTS, 3, false, 1),
+                Arguments.of("MAX_SEGMENT with data", 
FirebirdBlobInfoPacketType.MAX_SEGMENT, 4, false, 4),
+                Arguments.of("TOTAL_LENGTH with data", 
FirebirdBlobInfoPacketType.TOTAL_LENGTH, 5, false, 5),
+                Arguments.of("TYPE of segmented BLOB", 
FirebirdBlobInfoPacketType.TYPE, 1, false, 0),
+                Arguments.of("TYPE of stream BLOB", 
FirebirdBlobInfoPacketType.TYPE, 1, true, 1));
     }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/FirebirdCommandExecutorFactory.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/FirebirdCommandExecutorFactory.java
index 54dfcafca64..695e8897c2d 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/FirebirdCommandExecutorFactory.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/FirebirdCommandExecutorFactory.java
@@ -107,7 +107,7 @@ public final class FirebirdCommandExecutorFactory {
             case BATCH_SEGMENTS:
                 return new 
FirebirdBatchBlobSegmentsCommandExecutor((FirebirdBatchBlobSegmentsCommandPacket)
 commandPacket, connectionSession);
             case SEEK_BLOB:
-                return new 
FirebirdSeekBlobCommandExecutor((FirebirdSeekBlobCommandPacket) commandPacket);
+                return new 
FirebirdSeekBlobCommandExecutor((FirebirdSeekBlobCommandPacket) commandPacket, 
connectionSession);
             case CANCEL_BLOB:
                 return new 
FirebirdCancelBlobCommandExecutor((FirebirdCancelBlobCommandPacket) 
commandPacket, connectionSession);
             case CLOSE_BLOB:
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutor.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutor.java
index 274f79e33ed..27a4f366b11 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutor.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutor.java
@@ -44,7 +44,7 @@ public final class FirebirdCreateBlobCommandExecutor 
implements CommandExecutor
     public Collection<DatabasePacket> execute() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(connectionSession.getConnectionId());
         long blobId = 
FirebirdBlobIdGenerator.getInstance().nextBlobId(connectionSession.getConnectionId());
-        
FirebirdBlobWriteCache.getInstance().registerBlob(connectionSession.getConnectionId(),
 blobHandle, blobId);
+        
FirebirdBlobWriteCache.getInstance().registerBlob(connectionSession.getConnectionId(),
 blobHandle, blobId, packet.isStreamBlob());
         return Collections.singleton(new 
FirebirdGenericResponsePacket().setHandle(blobHandle).setId(blobId));
     }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutor.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutor.java
index 0e194e6cb48..127eda03f10 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutor.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutor.java
@@ -41,6 +41,11 @@ import java.util.Optional;
  * so it is registered with empty content rather than reported as an invalid 
BLOB id. A negative id refers to a
  * result BLOB returned to the client, while a positive id refers to a BLOB 
the client created and closed, which
  * Firebird allows to be opened again.</p>
+ *
+ * <p>Firebird stores the BLOB type with the BLOB and restores it when the 
BLOB is opened, so the type of a BLOB
+ * created by the client is taken from the blob parameter buffer of its 
create_blob2. A result BLOB is assembled by
+ * the proxy from backend column data without segment boundaries and carries 
no stored Firebird type, so it is a
+ * stream BLOB. The empty BLOB opened for the NULL quad is not a stream BLOB, 
matching Firebird.</p>
  */
 @RequiredArgsConstructor
 public final class FirebirdOpenBlobCommandExecutor implements CommandExecutor {
@@ -54,7 +59,7 @@ public final class FirebirdOpenBlobCommandExecutor implements 
CommandExecutor {
         long blobId = packet.getBlobId();
         byte[] blobContent = getBlobContent(blobId);
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(connectionSession.getConnectionId());
-        
FirebirdBlobReadCache.getInstance().registerBlob(connectionSession.getConnectionId(),
 blobHandle, blobContent);
+        
FirebirdBlobReadCache.getInstance().registerBlob(connectionSession.getConnectionId(),
 blobHandle, blobContent, isStreamBlob(blobId));
         return Collections.singleton(new 
FirebirdGenericResponsePacket().setHandle(blobHandle).setId(blobId));
     }
     
@@ -76,4 +81,11 @@ public final class FirebirdOpenBlobCommandExecutor 
implements CommandExecutor {
         Optional<byte[]> blobData = 
FirebirdBlobWriteCache.getInstance().getBlobData(connectionSession.getConnectionId(),
 blobId);
         return blobData.get();
     }
+    
+    private boolean isStreamBlob(final long blobId) {
+        if (0L == blobId) {
+            return false;
+        }
+        return blobId < 0L || 
FirebirdBlobWriteCache.getInstance().isStreamBlob(connectionSession.getConnectionId(),
 blobId);
+    }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
index c59d501215c..923a75b0da0 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutor.java
@@ -18,23 +18,45 @@
 package org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob;
 
 import lombok.RequiredArgsConstructor;
-import 
org.apache.shardingsphere.database.exception.core.exception.protocol.DatabaseProtocolException;
+import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrHandleException;
+import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrTypeException;
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.blob.FirebirdSeekBlobCommandPacket;
+import 
org.apache.shardingsphere.database.protocol.firebird.packet.generic.FirebirdGenericResponsePacket;
 import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
+import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
+import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import 
org.apache.shardingsphere.proxy.frontend.command.executor.CommandExecutor;
+import 
org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob.cache.FirebirdBlobReadCache;
+import 
org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob.generator.FirebirdBlobHandleGenerator;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.OptionalInt;
 
 /**
  * Seek blob command executor for Firebird.
+ *
+ * <p>Firebird defines seek for stream BLOBs only and returns the resulting 
position in the BLOB id of the response.
+ * A position outside the BLOB is not an error, it is limited to the BLOB 
content.</p>
+ *
+ * @see <a 
href="https://github.com/FirebirdSQL/firebird/blob/v5.0.3/src/jrd/blb.cpp";>Firebird
 blb::BLB_lseek</a>
  */
 @RequiredArgsConstructor
 public final class FirebirdSeekBlobCommandExecutor implements CommandExecutor {
     
     private final FirebirdSeekBlobCommandPacket packet;
     
+    private final ConnectionSession connectionSession;
+    
     @Override
     public Collection<DatabasePacket> execute() {
-        throw new DatabaseProtocolException("SEEK BLOB is not supported at the 
moment, blob handle: %d", packet.getBlobHandle());
+        int connectionId = connectionSession.getConnectionId();
+        int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().resolveBlobHandle(connectionId, 
packet.getBlobHandle());
+        Optional<Boolean> streamBlob = 
FirebirdBlobReadCache.getInstance().isStreamBlob(connectionId, blobHandle);
+        ShardingSpherePreconditions.checkState(streamBlob.isPresent(), () -> 
new InvalidSegstrHandleException(blobHandle));
+        ShardingSpherePreconditions.checkState(streamBlob.get(), () -> new 
InvalidSegstrTypeException(blobHandle));
+        OptionalInt position = 
FirebirdBlobReadCache.getInstance().seek(connectionId, blobHandle, 
packet.getSeekMode(), packet.getOffset());
+        return Collections.singleton(new 
FirebirdGenericResponsePacket().setId(position.getAsInt()));
     }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCache.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCache.java
index b401f9db3cb..5adae1f9641 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCache.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCache.java
@@ -34,12 +34,19 @@ import java.util.concurrent.ConcurrentHashMap;
  * <p>Read direction counterpart of the write cache: open_blob puts the whole 
BLOB content here,
  * and get_segment hands it out to the client chunk by chunk using a cursor 
over the original content.
  * Entries are keyed by connection id and blob handle, so concurrent 
connections and BLOBs do not interfere.</p>
+ *
+ * <p>A cursor lives until close_blob or cancel_blob releases its handle, so a 
fully read BLOB can be repositioned
+ * by seek_blob and read again, as Firebird clears the end of file state when 
a BLOB is positioned.</p>
  */
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public final class FirebirdBlobReadCache {
     
     private static final FirebirdBlobReadCache INSTANCE = new 
FirebirdBlobReadCache();
     
+    private static final int SEEK_MODE_RELATIVE = 1;
+    
+    private static final int SEEK_MODE_FROM_TAIL = 2;
+    
     private final Map<Integer, Map<Integer, BlobReadCursor>> cursors = new 
ConcurrentHashMap<>(16);
     
     public static FirebirdBlobReadCache getInstance() {
@@ -70,9 +77,10 @@ public final class FirebirdBlobReadCache {
      * @param connectionId connection id
      * @param blobHandle blob handle
      * @param content blob content
+     * @param streamBlob whether the BLOB is a stream BLOB
      */
-    public void registerBlob(final int connectionId, final int blobHandle, 
final byte[] content) {
-        getCursorMap(connectionId).put(blobHandle, new 
BlobReadCursor(content));
+    public void registerBlob(final int connectionId, final int blobHandle, 
final byte[] content, final boolean streamBlob) {
+        getCursorMap(connectionId).put(blobHandle, new BlobReadCursor(content, 
streamBlob));
     }
     
     /**
@@ -84,31 +92,55 @@ public final class FirebirdBlobReadCache {
      * @return optional segment data
      */
     public Optional<BlobSegment> readSegment(final int connectionId, final int 
blobHandle, final int maximumLength) {
-        Map<Integer, BlobReadCursor> cursorMap = getCursorMap(connectionId);
-        BlobReadCursor cursor = cursorMap.get(blobHandle);
+        BlobReadCursor cursor = getCursorMap(connectionId).get(blobHandle);
         if (null == cursor || 0 == cursor.getRemainingSize()) {
             return Optional.empty();
         }
         int segmentLength = Math.min(maximumLength, cursor.getRemainingSize());
         byte[] data = Arrays.copyOfRange(cursor.content, cursor.offset, 
cursor.offset + segmentLength);
         cursor.offset += segmentLength;
-        boolean complete = 0 == cursor.getRemainingSize();
-        if (complete) {
-            cursorMap.remove(blobHandle);
-        }
-        return Optional.of(new BlobSegment(data, complete));
+        return Optional.of(new BlobSegment(data, 0 == 
cursor.getRemainingSize()));
+    }
+    
+    /**
+     * Get total BLOB size by handle.
+     *
+     * @param connectionId connection id
+     * @param blobHandle blob handle
+     * @return optional total BLOB size
+     */
+    public OptionalInt getTotalSize(final int connectionId, final int 
blobHandle) {
+        BlobReadCursor cursor = getCursorMap(connectionId).get(blobHandle);
+        return null == cursor ? OptionalInt.empty() : 
OptionalInt.of(cursor.content.length);
     }
     
     /**
-     * Get remaining BLOB size by handle.
+     * Judge whether an opened BLOB is a stream BLOB.
      *
      * @param connectionId connection id
      * @param blobHandle blob handle
-     * @return optional remaining BLOB size
+     * @return optional stream BLOB state
      */
-    public OptionalInt getRemainingSize(final int connectionId, final int 
blobHandle) {
+    public Optional<Boolean> isStreamBlob(final int connectionId, final int 
blobHandle) {
         BlobReadCursor cursor = getCursorMap(connectionId).get(blobHandle);
-        return null == cursor ? OptionalInt.empty() : 
OptionalInt.of(cursor.getRemainingSize());
+        return null == cursor ? Optional.empty() : 
Optional.of(cursor.streamBlob);
+    }
+    
+    /**
+     * Position an opened BLOB for the following reads.
+     *
+     * <p>Mode 1 positions relative to the current position and mode 2 
relative to the end of the BLOB,
+     * every other mode positions from the start. The resulting position is 
limited to the BLOB content.</p>
+     *
+     * @param connectionId connection id
+     * @param blobHandle blob handle
+     * @param seekMode seek mode
+     * @param offset offset to position by
+     * @return optional resulting position
+     */
+    public OptionalInt seek(final int connectionId, final int blobHandle, 
final int seekMode, final int offset) {
+        BlobReadCursor cursor = getCursorMap(connectionId).get(blobHandle);
+        return null == cursor ? OptionalInt.empty() : 
OptionalInt.of(cursor.seek(seekMode, offset));
     }
     
     /**
@@ -131,11 +163,26 @@ public final class FirebirdBlobReadCache {
         
         private final byte[] content;
         
+        private final boolean streamBlob;
+        
         private int offset;
         
         private int getRemainingSize() {
             return content.length - offset;
         }
+        
+        private int seek(final int seekMode, final int seekOffset) {
+            int position = getSeekPosition(seekMode, seekOffset);
+            offset = Math.min(Math.max(position, 0), content.length);
+            return offset;
+        }
+        
+        private int getSeekPosition(final int seekMode, final int seekOffset) {
+            if (SEEK_MODE_RELATIVE == seekMode) {
+                return offset + seekOffset;
+            }
+            return SEEK_MODE_FROM_TAIL == seekMode ? content.length + 
seekOffset : seekOffset;
+        }
     }
     
     /**
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWrite.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWrite.java
index e586c83d39a..7ff4fc98494 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWrite.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWrite.java
@@ -31,6 +31,9 @@ public final class FirebirdBlobWrite {
     @Getter
     private final long blobId;
     
+    @Getter
+    private final boolean streamBlob;
+    
     private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
     
     @Getter
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCache.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCache.java
index bd8084293f9..39993633d3d 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCache.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCache.java
@@ -75,9 +75,10 @@ public final class FirebirdBlobWriteCache {
      * @param connectionId connection id
      * @param blobHandle blob handle
      * @param blobId blob id
+     * @param streamBlob whether the BLOB is a stream BLOB
      */
-    public void registerBlob(final int connectionId, final int blobHandle, 
final long blobId) {
-        FirebirdBlobWrite write = new FirebirdBlobWrite(blobHandle, blobId);
+    public void registerBlob(final int connectionId, final int blobHandle, 
final long blobId, final boolean streamBlob) {
+        FirebirdBlobWrite write = new FirebirdBlobWrite(blobHandle, blobId, 
streamBlob);
         getHandleMap(connectionId).put(blobHandle, write);
         getIdMap(connectionId).put(blobId, write);
     }
@@ -171,6 +172,18 @@ public final class FirebirdBlobWriteCache {
         return null != write && write.isClosed();
     }
     
+    /**
+     * Judge whether a buffered BLOB is a stream BLOB.
+     *
+     * @param connectionId connection id
+     * @param blobId blob id
+     * @return whether the BLOB is a stream BLOB
+     */
+    public boolean isStreamBlob(final int connectionId, final long blobId) {
+        FirebirdBlobWrite write = getIdMap(connectionId).get(blobId);
+        return null != write && write.isStreamBlob();
+    }
+    
     /**
      * Remove write by blob id.
      *
diff --git 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutor.java
 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutor.java
index e4e87455096..42e206c2c8f 100644
--- 
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutor.java
+++ 
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutor.java
@@ -44,16 +44,21 @@ public final class FirebirdBlobInfoExecutor implements 
CommandExecutor {
     
     @Override
     public Collection<DatabasePacket> execute() {
-        return Collections.singleton(new 
FirebirdGenericResponsePacket().setData(new 
FirebirdBlobInfoReturnPacket(packet.getInfoItems(), getBlobLength())));
-    }
-    
-    private int getBlobLength() {
         int connectionId = connectionSession.getConnectionId();
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().resolveBlobHandle(connectionId, 
packet.getHandle());
-        OptionalInt remainingSize = 
FirebirdBlobReadCache.getInstance().getRemainingSize(connectionId, blobHandle);
-        if (remainingSize.isPresent()) {
-            return remainingSize.getAsInt();
+        FirebirdBlobInfoReturnPacket returnPacket = new 
FirebirdBlobInfoReturnPacket(packet.getInfoItems(), getBlobLength(connectionId, 
blobHandle), isStreamBlob(connectionId, blobHandle));
+        return Collections.singleton(new 
FirebirdGenericResponsePacket().setData(returnPacket));
+    }
+    
+    private int getBlobLength(final int connectionId, final int blobHandle) {
+        OptionalInt totalSize = 
FirebirdBlobReadCache.getInstance().getTotalSize(connectionId, blobHandle);
+        if (totalSize.isPresent()) {
+            return totalSize.getAsInt();
         }
         return 
FirebirdBlobWriteCache.getInstance().getBlobSizeByHandle(connectionId, 
blobHandle).orElse(0);
     }
+    
+    private boolean isStreamBlob(final int connectionId, final int blobHandle) 
{
+        return FirebirdBlobReadCache.getInstance().isStreamBlob(connectionId, 
blobHandle).orElse(false);
+    }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdBatchBlobSegmentsCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdBatchBlobSegmentsCommandExecutorTest.java
index b0f8e11eae9..00b4b0632e2 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdBatchBlobSegmentsCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdBatchBlobSegmentsCommandExecutorTest.java
@@ -73,7 +73,7 @@ class FirebirdBatchBlobSegmentsCommandExecutorTest {
     void assertExecute() {
         int blobHandle = 4;
         long blobId = 12L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         when(packet.getSegments()).thenReturn(Arrays.asList(new byte[]{1, 2}, 
new byte[]{3, 4, 5}));
         FirebirdBatchBlobSegmentsCommandExecutor executor = new 
FirebirdBatchBlobSegmentsCommandExecutor(packet, connectionSession);
@@ -89,7 +89,7 @@ class FirebirdBatchBlobSegmentsCommandExecutorTest {
     void assertExecuteWithDeferredPlaceholderHandle() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
         long blobId = 21L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         when(packet.getBlobHandle()).thenReturn(0xFFFF);
         when(packet.getSegments()).thenReturn(Arrays.asList(new byte[]{1}, new 
byte[]{2, 3}));
         FirebirdBatchBlobSegmentsCommandExecutor executor = new 
FirebirdBatchBlobSegmentsCommandExecutor(packet, connectionSession);
@@ -109,7 +109,7 @@ class FirebirdBatchBlobSegmentsCommandExecutorTest {
     void assertExecuteAfterClose() {
         int blobHandle = 4;
         long blobId = 12L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         FirebirdBlobWriteCache.getInstance().closeWrite(CONNECTION_ID, 
blobHandle);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdBatchBlobSegmentsCommandExecutor executor = new 
FirebirdBatchBlobSegmentsCommandExecutor(packet, connectionSession);
@@ -119,7 +119,7 @@ class FirebirdBatchBlobSegmentsCommandExecutorTest {
     @Test
     void assertExecuteWithReadHandle() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2}, false);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdBatchBlobSegmentsCommandExecutor executor = new 
FirebirdBatchBlobSegmentsCommandExecutor(packet, connectionSession);
         assertThrows(CannotUpdateOldBlobException.class, executor::execute);
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCancelBlobCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCancelBlobCommandExecutorTest.java
index 547e23e8eb6..d781bfd5e05 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCancelBlobCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCancelBlobCommandExecutorTest.java
@@ -72,7 +72,7 @@ class FirebirdCancelBlobCommandExecutorTest {
     void assertExecute() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
         long blobId = 9L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdCancelBlobCommandExecutor executor = new 
FirebirdCancelBlobCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actual = executor.execute();
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCloseBlobCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCloseBlobCommandExecutorTest.java
index 5b4c741ca30..5baaf494df5 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCloseBlobCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCloseBlobCommandExecutorTest.java
@@ -74,7 +74,7 @@ class FirebirdCloseBlobCommandExecutorTest {
     void assertExecute() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
         long blobId = 5L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdCloseBlobCommandExecutor executor = new 
FirebirdCloseBlobCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actual = executor.execute();
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutorTest.java
index e08fb937d54..1187dc0bac9 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdCreateBlobCommandExecutorTest.java
@@ -38,6 +38,7 @@ import java.util.Collection;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
@@ -78,4 +79,12 @@ class FirebirdCreateBlobCommandExecutorTest {
         assertThat(((FirebirdGenericResponsePacket) response).getHandle(), 
is(1));
         assertThat(((FirebirdGenericResponsePacket) response).getId(), is(1L));
     }
+    
+    @Test
+    void assertExecuteWithStreamBlob() {
+        when(packet.isStreamBlob()).thenReturn(true);
+        FirebirdCreateBlobCommandExecutor executor = new 
FirebirdCreateBlobCommandExecutor(packet, connectionSession);
+        FirebirdGenericResponsePacket actual = (FirebirdGenericResponsePacket) 
executor.execute().iterator().next();
+        
assertTrue(FirebirdBlobWriteCache.getInstance().isStreamBlob(CONNECTION_ID, 
actual.getId()));
+    }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutorTest.java
index e21a8fedd6a..4ed1904c342 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutorTest.java
@@ -39,7 +39,6 @@ import java.util.Collection;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.isA;
-import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.Mockito.when;
 
@@ -73,7 +72,7 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
     @Test
     void assertExecuteWithDeferredPlaceholderHandleExpectsLastOpenedBlob() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{7, 8, 9});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{7, 8, 9}, false);
         when(packet.getBlobHandle()).thenReturn(0xFFFF);
         when(packet.getSegmentLength()).thenReturn(16386);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
@@ -95,7 +94,7 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
     
     @Test
     void assertExecuteWithEmptyBlobExpectsEof() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[0]);
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[0], false);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actualPackets = executor.execute();
         FirebirdGenericResponsePacket actualGenericPacket = 
(FirebirdGenericResponsePacket) actualPackets.iterator().next();
@@ -105,19 +104,18 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
     
     @Test
     void assertExecuteWithRequestedLengthBelowPrefixExpectsPartial() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{9, 8});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{9, 8}, false);
         when(packet.getSegmentLength()).thenReturn(1);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actualPackets = executor.execute();
         FirebirdGenericResponsePacket actualGenericPacket = 
(FirebirdGenericResponsePacket) actualPackets.iterator().next();
         assertThat(actualGenericPacket.getHandle(), is(1));
         assertThat(getResponseSegment(actualGenericPacket).length, is(0));
-        
assertThat(FirebirdBlobReadCache.getInstance().getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).getAsInt(), is(2));
     }
     
     @Test
     void assertExecuteWithPartialSegmentLeftExpectsPartial() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3}, false);
         when(packet.getSegmentLength()).thenReturn(4);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actualPackets = executor.execute();
@@ -125,38 +123,35 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
         assertThat(actualGenericPacket.getHandle(), is(1));
         assertThat(actualGenericPacket.getData(), 
isA(FirebirdGetBlobSegmentResponsePacket.class));
         assertThat(getResponseSegment(actualGenericPacket), is(new byte[]{1, 
2}));
-        
assertThat(FirebirdBlobReadCache.getInstance().getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).getAsInt(), is(1));
     }
     
     @Test
     void assertExecuteWithFullyConsumedSegmentExpectsComplete() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{4, 5});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{4, 5}, false);
         when(packet.getSegmentLength()).thenReturn(4);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actualPackets = executor.execute();
         FirebirdGenericResponsePacket actualGenericPacket = 
(FirebirdGenericResponsePacket) actualPackets.iterator().next();
         assertThat(actualGenericPacket.getHandle(), is(0));
         assertThat(getResponseSegment(actualGenericPacket), is(new byte[]{4, 
5}));
-        
assertFalse(FirebirdBlobReadCache.getInstance().getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
     }
     
     @Test
     void 
assertExecuteWithRequestedLengthOverMaxSegmentDataLengthExpectsCappedSegment() {
         byte[] content = new byte[0xFFFF + 1];
         content[0xFFFF] = 42;
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, content);
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, content, false);
         when(packet.getSegmentLength()).thenReturn(0xFFFF + 100);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actualPackets = executor.execute();
         FirebirdGenericResponsePacket actualGenericPacket = 
(FirebirdGenericResponsePacket) actualPackets.iterator().next();
         assertThat(actualGenericPacket.getHandle(), is(1));
         assertThat(getResponseSegment(actualGenericPacket).length, is(0xFFFF));
-        
assertThat(FirebirdBlobReadCache.getInstance().getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).getAsInt(), is(1));
     }
     
     @Test
     void assertExecuteConsecutivelyReturnsAllDataOnce() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3, 4, 5});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3, 4, 5}, false);
         when(packet.getSegmentLength()).thenReturn(4);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         FirebirdGenericResponsePacket firstPacket = 
(FirebirdGenericResponsePacket) executor.execute().iterator().next();
@@ -173,12 +168,12 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
     @Test
     void assertExecuteWithOtherConnectionBlobExpectsEof() {
         FirebirdBlobReadCache.getInstance().registerConnection(2);
-        FirebirdBlobReadCache.getInstance().registerBlob(2, BLOB_HANDLE, new 
byte[]{1, 2, 3});
+        FirebirdBlobReadCache.getInstance().registerBlob(2, BLOB_HANDLE, new 
byte[]{1, 2, 3}, false);
         FirebirdGetBlobSegmentCommandExecutor executor = new 
FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
         Collection<DatabasePacket> actualPackets = executor.execute();
         FirebirdGenericResponsePacket actualGenericPacket = 
(FirebirdGenericResponsePacket) actualPackets.iterator().next();
         assertThat(actualGenericPacket.getHandle(), is(2));
-        assertThat(FirebirdBlobReadCache.getInstance().getRemainingSize(2, 
BLOB_HANDLE).getAsInt(), is(3));
+        assertThat(FirebirdBlobReadCache.getInstance().readSegment(2, 
BLOB_HANDLE, 3).get().getData(), is(new byte[]{1, 2, 3}));
         FirebirdBlobReadCache.getInstance().unregisterConnection(2);
     }
     
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutorTest.java
index b25e5471ea8..1f67f8567ec 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdOpenBlobCommandExecutorTest.java
@@ -120,7 +120,7 @@ class FirebirdOpenBlobCommandExecutorTest {
     
     @Test
     void assertExecuteAfterCreatedBlobIsClosed() {
-        FirebirdGenericResponsePacket createdResponse = 
createBlobWithSegment(new byte[]{1, 2, 3});
+        FirebirdGenericResponsePacket createdResponse = 
createBlobWithSegment(new byte[]{1, 2, 3}, false);
         FirebirdCloseBlobCommandPacket closePacket = 
mock(FirebirdCloseBlobCommandPacket.class);
         
when(closePacket.getBlobHandle()).thenReturn(createdResponse.getHandle());
         new FirebirdCloseBlobCommandExecutor(closePacket, 
connectionSession).execute();
@@ -135,12 +135,36 @@ class FirebirdOpenBlobCommandExecutorTest {
     
     @Test
     void assertExecuteWhenCreatedBlobIsNotClosed() {
-        FirebirdGenericResponsePacket createdResponse = 
createBlobWithSegment(new byte[]{1, 2, 3});
+        FirebirdGenericResponsePacket createdResponse = 
createBlobWithSegment(new byte[]{1, 2, 3}, false);
         when(packet.getBlobId()).thenReturn(createdResponse.getId());
         FirebirdOpenBlobCommandExecutor executor = new 
FirebirdOpenBlobCommandExecutor(packet, connectionSession);
         assertThrows(InvalidSegstrIdException.class, executor::execute);
     }
     
+    @Test
+    void assertExecuteWithResultBlobId() {
+        when(packet.getBlobId()).thenReturn(registerBlobContent(new byte[]{1, 
2}));
+        FirebirdGenericResponsePacket actual = (FirebirdGenericResponsePacket) 
new FirebirdOpenBlobCommandExecutor(packet, 
connectionSession).execute().iterator().next();
+        
assertThat(FirebirdBlobReadCache.getInstance().isStreamBlob(CONNECTION_ID, 
actual.getHandle()), is(Optional.of(true)));
+    }
+    
+    @Test
+    void assertExecuteWithZeroBlobIdOpensSegmentedBlob() {
+        FirebirdGenericResponsePacket actual = (FirebirdGenericResponsePacket) 
new FirebirdOpenBlobCommandExecutor(packet, 
connectionSession).execute().iterator().next();
+        
assertThat(FirebirdBlobReadCache.getInstance().isStreamBlob(CONNECTION_ID, 
actual.getHandle()), is(Optional.of(false)));
+    }
+    
+    @Test
+    void assertExecuteRestoresCreatedStreamBlobType() {
+        FirebirdGenericResponsePacket createdResponse = 
createBlobWithSegment(new byte[]{1, 2, 3}, true);
+        FirebirdCloseBlobCommandPacket closePacket = 
mock(FirebirdCloseBlobCommandPacket.class);
+        
when(closePacket.getBlobHandle()).thenReturn(createdResponse.getHandle());
+        new FirebirdCloseBlobCommandExecutor(closePacket, 
connectionSession).execute();
+        when(packet.getBlobId()).thenReturn(createdResponse.getId());
+        FirebirdGenericResponsePacket actual = (FirebirdGenericResponsePacket) 
new FirebirdOpenBlobCommandExecutor(packet, 
connectionSession).execute().iterator().next();
+        
assertThat(FirebirdBlobReadCache.getInstance().isStreamBlob(CONNECTION_ID, 
actual.getHandle()), is(Optional.of(true)));
+    }
+    
     @Test
     void assertExecuteWithUnknownResultBlobId() {
         when(packet.getBlobId()).thenReturn(-99L);
@@ -148,9 +172,10 @@ class FirebirdOpenBlobCommandExecutorTest {
         assertThrows(InvalidSegstrIdException.class, executor::execute);
     }
     
-    private FirebirdGenericResponsePacket createBlobWithSegment(final byte[] 
segment) {
-        FirebirdGenericResponsePacket result = (FirebirdGenericResponsePacket) 
new FirebirdCreateBlobCommandExecutor(
-                mock(FirebirdCreateBlobCommandPacket.class), 
connectionSession).execute().iterator().next();
+    private FirebirdGenericResponsePacket createBlobWithSegment(final byte[] 
segment, final boolean streamBlob) {
+        FirebirdCreateBlobCommandPacket createPacket = 
mock(FirebirdCreateBlobCommandPacket.class);
+        when(createPacket.isStreamBlob()).thenReturn(streamBlob);
+        FirebirdGenericResponsePacket result = (FirebirdGenericResponsePacket) 
new FirebirdCreateBlobCommandExecutor(createPacket, 
connectionSession).execute().iterator().next();
         FirebirdPutBlobSegmentCommandPacket putPacket = 
mock(FirebirdPutBlobSegmentCommandPacket.class);
         when(putPacket.getBlobHandle()).thenReturn(result.getHandle());
         when(putPacket.getSegment()).thenReturn(segment);
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdPutBlobSegmentCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdPutBlobSegmentCommandExecutorTest.java
index 9062f313bb5..9eaba43d542 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdPutBlobSegmentCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdPutBlobSegmentCommandExecutorTest.java
@@ -79,7 +79,7 @@ class FirebirdPutBlobSegmentCommandExecutorTest {
         int blobHandle = 4;
         long blobId = 12L;
         byte[] segment = new byte[]{1, 2};
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         when(packet.getSegment()).thenReturn(segment);
         FirebirdPutBlobSegmentCommandExecutor executor = new 
FirebirdPutBlobSegmentCommandExecutor(packet, connectionSession);
@@ -97,7 +97,7 @@ class FirebirdPutBlobSegmentCommandExecutorTest {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
         long blobId = 21L;
         byte[] segment = new byte[]{1, 2, 3};
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         when(packet.getBlobHandle()).thenReturn(0xFFFF);
         when(packet.getSegment()).thenReturn(segment);
         FirebirdPutBlobSegmentCommandExecutor executor = new 
FirebirdPutBlobSegmentCommandExecutor(packet, connectionSession);
@@ -118,7 +118,7 @@ class FirebirdPutBlobSegmentCommandExecutorTest {
     void assertExecuteAfterClose() {
         int blobHandle = 4;
         long blobId = 12L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         FirebirdBlobWriteCache.getInstance().closeWrite(CONNECTION_ID, 
blobHandle);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdPutBlobSegmentCommandExecutor executor = new 
FirebirdPutBlobSegmentCommandExecutor(packet, connectionSession);
@@ -128,7 +128,7 @@ class FirebirdPutBlobSegmentCommandExecutorTest {
     @Test
     void assertExecuteWithReadHandle() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2}, false);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdPutBlobSegmentCommandExecutor executor = new 
FirebirdPutBlobSegmentCommandExecutor(packet, connectionSession);
         assertThrows(CannotUpdateOldBlobException.class, executor::execute);
@@ -137,7 +137,7 @@ class FirebirdPutBlobSegmentCommandExecutorTest {
     @Test
     void assertExecuteWithFullyReadHandle() {
         int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2}, false);
         FirebirdBlobReadCache.getInstance().readSegment(CONNECTION_ID, 
blobHandle, 2);
         when(packet.getBlobHandle()).thenReturn(blobHandle);
         FirebirdPutBlobSegmentCommandExecutor executor = new 
FirebirdPutBlobSegmentCommandExecutor(packet, connectionSession);
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
index 015af357624..bd2634ff46f 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdSeekBlobCommandExecutorTest.java
@@ -17,13 +17,24 @@
 
 package org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob;
 
-import 
org.apache.shardingsphere.database.exception.core.exception.protocol.DatabaseProtocolException;
+import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrHandleException;
+import 
org.apache.shardingsphere.database.exception.firebird.exception.protocol.InvalidSegstrTypeException;
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.blob.FirebirdSeekBlobCommandPacket;
+import 
org.apache.shardingsphere.database.protocol.firebird.packet.generic.FirebirdGenericResponsePacket;
+import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
+import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import 
org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob.cache.FirebirdBlobReadCache;
+import 
org.apache.shardingsphere.proxy.frontend.firebird.command.query.blob.generator.FirebirdBlobHandleGenerator;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.util.Collection;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -32,14 +43,85 @@ import static org.mockito.Mockito.when;
 @ExtendWith(MockitoExtension.class)
 class FirebirdSeekBlobCommandExecutorTest {
     
+    private static final int CONNECTION_ID = 1;
+    
+    private static final int BLOB_HANDLE = 7;
+    
+    private static final int SEEK_MODE_FROM_TAIL = 2;
+    
     @Mock
     private FirebirdSeekBlobCommandPacket packet;
     
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private ConnectionSession connectionSession;
+    
+    @BeforeEach
+    void setUp() {
+        
FirebirdBlobHandleGenerator.getInstance().registerConnection(CONNECTION_ID);
+        FirebirdBlobReadCache.getInstance().registerConnection(CONNECTION_ID);
+        when(connectionSession.getConnectionId()).thenReturn(CONNECTION_ID);
+    }
+    
+    @AfterEach
+    void tearDown() {
+        
FirebirdBlobHandleGenerator.getInstance().unregisterConnection(CONNECTION_ID);
+        
FirebirdBlobReadCache.getInstance().unregisterConnection(CONNECTION_ID);
+    }
+    
+    @Test
+    void assertExecuteWithStreamBlob() {
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3, 4}, true);
+        when(packet.getBlobHandle()).thenReturn(BLOB_HANDLE);
+        when(packet.getOffset()).thenReturn(3);
+        Collection<DatabasePacket> actualPackets = new 
FirebirdSeekBlobCommandExecutor(packet, connectionSession).execute();
+        assertThat(((FirebirdGenericResponsePacket) 
actualPackets.iterator().next()).getId(), is(3L));
+        
assertThat(FirebirdBlobReadCache.getInstance().readSegment(CONNECTION_ID, 
BLOB_HANDLE, 4).get().getData(), is(new byte[]{4}));
+    }
+    
+    @Test
+    void assertExecuteWithOffsetBeyondBlobContent() {
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2}, true);
+        when(packet.getBlobHandle()).thenReturn(BLOB_HANDLE);
+        when(packet.getOffset()).thenReturn(9);
+        Collection<DatabasePacket> actualPackets = new 
FirebirdSeekBlobCommandExecutor(packet, connectionSession).execute();
+        assertThat(((FirebirdGenericResponsePacket) 
actualPackets.iterator().next()).getId(), is(2L));
+    }
+    
+    @Test
+    void assertExecuteWithDeferredPlaceholderHandle() {
+        int blobHandle = 
FirebirdBlobHandleGenerator.getInstance().nextBlobHandle(CONNECTION_ID);
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, new byte[]{1, 2, 3}, true);
+        when(packet.getBlobHandle()).thenReturn(0xFFFF);
+        when(packet.getSeekMode()).thenReturn(SEEK_MODE_FROM_TAIL);
+        when(packet.getOffset()).thenReturn(-1);
+        Collection<DatabasePacket> actualPackets = new 
FirebirdSeekBlobCommandExecutor(packet, connectionSession).execute();
+        assertThat(((FirebirdGenericResponsePacket) 
actualPackets.iterator().next()).getId(), is(2L));
+    }
+    
+    @Test
+    void assertExecuteWithFullyReadBlob() {
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2}, true);
+        FirebirdBlobReadCache.getInstance().readSegment(CONNECTION_ID, 
BLOB_HANDLE, 2);
+        when(packet.getBlobHandle()).thenReturn(BLOB_HANDLE);
+        Collection<DatabasePacket> actualPackets = new 
FirebirdSeekBlobCommandExecutor(packet, connectionSession).execute();
+        assertThat(((FirebirdGenericResponsePacket) 
actualPackets.iterator().next()).getId(), is(0L));
+        
assertThat(FirebirdBlobReadCache.getInstance().readSegment(CONNECTION_ID, 
BLOB_HANDLE, 2).get().getData(), is(new byte[]{1, 2}));
+    }
+    
+    @Test
+    void assertExecuteWithSegmentedBlob() {
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2}, false);
+        when(packet.getBlobHandle()).thenReturn(BLOB_HANDLE);
+        FirebirdSeekBlobCommandExecutor executor = new 
FirebirdSeekBlobCommandExecutor(packet, connectionSession);
+        InvalidSegstrTypeException actual = 
assertThrows(InvalidSegstrTypeException.class, executor::execute);
+        assertThat(actual.getBlobHandle(), is(BLOB_HANDLE));
+    }
+    
     @Test
-    void assertExecuteThrowsUnsupported() {
-        when(packet.getBlobHandle()).thenReturn(42);
-        FirebirdSeekBlobCommandExecutor executor = new 
FirebirdSeekBlobCommandExecutor(packet);
-        DatabaseProtocolException actual = 
assertThrows(DatabaseProtocolException.class, executor::execute);
-        assertThat(actual.getMessage(), is("SEEK BLOB is not supported at the 
moment, blob handle: 42"));
+    void assertExecuteWithUnknownHandle() {
+        when(packet.getBlobHandle()).thenReturn(BLOB_HANDLE);
+        FirebirdSeekBlobCommandExecutor executor = new 
FirebirdSeekBlobCommandExecutor(packet, connectionSession);
+        InvalidSegstrHandleException actual = 
assertThrows(InvalidSegstrHandleException.class, executor::execute);
+        assertThat(actual.getBlobHandle(), is(BLOB_HANDLE));
     }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCacheTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCacheTest.java
index db6a74f02a9..7699880955b 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCacheTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobReadCacheTest.java
@@ -41,6 +41,12 @@ class FirebirdBlobReadCacheTest {
     
     private static final int BLOB_HANDLE = 3;
     
+    private static final int SEEK_MODE_FROM_HEAD = 0;
+    
+    private static final int SEEK_MODE_RELATIVE = 1;
+    
+    private static final int SEEK_MODE_FROM_TAIL = 2;
+    
     @AfterEach
     void tearDown() {
         CACHE.unregisterConnection(CONNECTION_ID);
@@ -50,16 +56,16 @@ class FirebirdBlobReadCacheTest {
     @Test
     void assertUnregisterConnectionRemovesBlobs() {
         CACHE.registerConnection(CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1});
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1}, true);
         CACHE.unregisterConnection(CONNECTION_ID);
-        assertFalse(CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
+        assertFalse(CACHE.getTotalSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
     }
     
     @Test
     void assertRegisterBlobKeepsContentReference() {
         CACHE.registerConnection(CONNECTION_ID);
         byte[] content = new byte[]{1, 2};
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, content);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, content, true);
         content[0] = 9;
         Optional<BlobSegment> actual = CACHE.readSegment(CONNECTION_ID, 
BLOB_HANDLE, content.length);
         assertTrue(actual.isPresent());
@@ -76,64 +82,161 @@ class FirebirdBlobReadCacheTest {
     @Test
     void assertReadEmptyBlob() {
         CACHE.registerConnection(CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[0]);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[0], true);
         assertFalse(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
1).isPresent());
-        OptionalInt remainingSize = CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE);
-        assertTrue(remainingSize.isPresent());
-        assertThat(remainingSize.getAsInt(), is(0));
     }
     
     @Test
     void assertReadPartialSegmentAdvancesCursor() {
         CACHE.registerConnection(CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3});
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3}, 
true);
         Optional<BlobSegment> actual = CACHE.readSegment(CONNECTION_ID, 
BLOB_HANDLE, 2);
         assertTrue(actual.isPresent());
         assertThat(actual.get().getData(), is(new byte[]{1, 2}));
         assertFalse(actual.get().isComplete());
-        assertThat(CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).getAsInt(), is(1));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
2).get().getData(), is(new byte[]{3}));
     }
     
     @Test
-    void assertReadCompleteSegmentRemovesCursor() {
+    void assertReadCompleteSegmentKeepsCursor() {
         CACHE.registerConnection(CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2});
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2}, true);
         Optional<BlobSegment> actual = CACHE.readSegment(CONNECTION_ID, 
BLOB_HANDLE, 2);
         assertTrue(actual.isPresent());
         assertThat(actual.get().getData(), is(new byte[]{1, 2}));
         assertTrue(actual.get().isComplete());
-        assertFalse(CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
+        assertFalse(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
2).isPresent());
+        assertTrue(CACHE.getTotalSize(CONNECTION_ID, BLOB_HANDLE).isPresent());
     }
     
     @Test
     void assertReadZeroLengthDoesNotAdvanceCursor() {
         CACHE.registerConnection(CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2});
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2}, true);
         Optional<BlobSegment> actual = CACHE.readSegment(CONNECTION_ID, 
BLOB_HANDLE, 0);
         assertTrue(actual.isPresent());
         assertThat(actual.get().getData(), is(new byte[0]));
         assertFalse(actual.get().isComplete());
-        assertThat(CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).getAsInt(), is(2));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
2).get().getData(), is(new byte[]{1, 2}));
+    }
+    
+    @Test
+    void assertGetTotalSize() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3}, 
true);
+        CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 2);
+        OptionalInt actual = CACHE.getTotalSize(CONNECTION_ID, BLOB_HANDLE);
+        assertTrue(actual.isPresent());
+        assertThat(actual.getAsInt(), is(3));
+    }
+    
+    @Test
+    void assertGetTotalSizeOfMissingBlob() {
+        CACHE.registerConnection(CONNECTION_ID);
+        assertFalse(CACHE.getTotalSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
+    }
+    
+    @Test
+    void assertIsStreamBlob() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1}, true);
+        assertThat(CACHE.isStreamBlob(CONNECTION_ID, BLOB_HANDLE), 
is(Optional.of(true)));
+    }
+    
+    @Test
+    void assertIsStreamBlobWithSegmentedBlob() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1}, false);
+        assertThat(CACHE.isStreamBlob(CONNECTION_ID, BLOB_HANDLE), 
is(Optional.of(false)));
+    }
+    
+    @Test
+    void assertIsStreamBlobWithMissingBlob() {
+        CACHE.registerConnection(CONNECTION_ID);
+        assertThat(CACHE.isStreamBlob(CONNECTION_ID, BLOB_HANDLE), 
is(Optional.empty()));
+    }
+    
+    @Test
+    void assertSeekFromHead() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3, 4}, 
true);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_FROM_HEAD, 
2).getAsInt(), is(2));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
4).get().getData(), is(new byte[]{3, 4}));
+    }
+    
+    @Test
+    void assertSeekRelativeToCurrentPosition() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3, 4}, 
true);
+        CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 1);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_RELATIVE, 
2).getAsInt(), is(3));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
4).get().getData(), is(new byte[]{4}));
+    }
+    
+    @Test
+    void assertSeekFromTail() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3, 4}, 
true);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_FROM_TAIL, 
-1).getAsInt(), is(3));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
4).get().getData(), is(new byte[]{4}));
+    }
+    
+    @Test
+    void assertSeekBeyondContent() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2}, true);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_FROM_HEAD, 
5).getAsInt(), is(2));
+        assertFalse(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
2).isPresent());
+    }
+    
+    @Test
+    void assertSeekBeforeContent() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2}, true);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_FROM_TAIL, 
-5).getAsInt(), is(0));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
2).get().getData(), is(new byte[]{1, 2}));
+    }
+    
+    @Test
+    void assertSeekWithUnknownModePositionsFromHead() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3}, 
true);
+        CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 2);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, 3, 1).getAsInt(), 
is(1));
+    }
+    
+    @Test
+    void assertSeekAfterFullReadAllowsReadingAgain() {
+        CACHE.registerConnection(CONNECTION_ID);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2}, true);
+        CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 2);
+        assertThat(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_FROM_HEAD, 
0).getAsInt(), is(0));
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 
2).get().getData(), is(new byte[]{1, 2}));
+    }
+    
+    @Test
+    void assertSeekMissingBlob() {
+        CACHE.registerConnection(CONNECTION_ID);
+        assertFalse(CACHE.seek(CONNECTION_ID, BLOB_HANDLE, 
SEEK_MODE_FROM_HEAD, 0).isPresent());
     }
     
     @Test
     void assertRemoveBlob() {
         CACHE.registerConnection(CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1});
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1}, true);
         CACHE.removeBlob(CONNECTION_ID, BLOB_HANDLE);
-        assertFalse(CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
+        assertFalse(CACHE.getTotalSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
     }
     
     @Test
     void assertConnectionsAndHandlesAreIsolated() {
         CACHE.registerConnection(CONNECTION_ID);
         CACHE.registerConnection(OTHER_CONNECTION_ID);
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1});
-        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE + 1, new byte[]{2, 3});
-        CACHE.registerBlob(OTHER_CONNECTION_ID, BLOB_HANDLE, new byte[]{4, 5, 
6});
-        CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE, 1);
-        assertFalse(CACHE.getRemainingSize(CONNECTION_ID, 
BLOB_HANDLE).isPresent());
-        assertThat(CACHE.getRemainingSize(CONNECTION_ID, BLOB_HANDLE + 
1).getAsInt(), is(2));
-        assertThat(CACHE.getRemainingSize(OTHER_CONNECTION_ID, 
BLOB_HANDLE).getAsInt(), is(3));
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1}, true);
+        CACHE.registerBlob(CONNECTION_ID, BLOB_HANDLE + 1, new byte[]{2, 3}, 
true);
+        CACHE.registerBlob(OTHER_CONNECTION_ID, BLOB_HANDLE, new byte[]{4, 5, 
6}, true);
+        CACHE.seek(CONNECTION_ID, BLOB_HANDLE, SEEK_MODE_FROM_TAIL, 0);
+        assertThat(CACHE.readSegment(CONNECTION_ID, BLOB_HANDLE + 1, 
2).get().getData(), is(new byte[]{2, 3}));
+        assertThat(CACHE.readSegment(OTHER_CONNECTION_ID, BLOB_HANDLE, 
3).get().getData(), is(new byte[]{4, 5, 6}));
     }
 }
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCacheTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCacheTest.java
index 9c54f68a56c..d04dd0c6c81 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCacheTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/cache/FirebirdBlobWriteCacheTest.java
@@ -62,7 +62,7 @@ class FirebirdBlobWriteCacheTest {
     
     @Test
     void assertRegisterBlobCreatesMappingsWhenConnectionMissing() {
-        CACHE.registerBlob(2, 3, 4L);
+        CACHE.registerBlob(2, 3, 4L, false);
         OptionalLong actualBlobId = CACHE.getBlobId(2, 3);
         assertTrue(actualBlobId.isPresent());
         assertThat(actualBlobId.getAsLong(), is(4L));
@@ -82,7 +82,7 @@ class FirebirdBlobWriteCacheTest {
         CACHE.registerConnection(connectionId);
         boolean actualClosedWhenMissing = CACHE.isClosed(connectionId, blobId);
         assertFalse(actualClosedWhenMissing);
-        CACHE.registerBlob(connectionId, blobHandle, blobId);
+        CACHE.registerBlob(connectionId, blobHandle, blobId, false);
         OptionalInt actualSizeAfterAppend = CACHE.appendSegment(connectionId, 
blobHandle, new byte[]{1, 2, 3});
         assertTrue(actualSizeAfterAppend.isPresent());
         assertThat(actualSizeAfterAppend.getAsInt(), is(3));
@@ -107,9 +107,9 @@ class FirebirdBlobWriteCacheTest {
         long oldBlobId = 9L;
         long expectedBlobId = 10L;
         CACHE.registerConnection(connectionId);
-        CACHE.registerBlob(connectionId, blobHandle, oldBlobId);
+        CACHE.registerBlob(connectionId, blobHandle, oldBlobId, false);
         CACHE.closeWrite(connectionId, blobHandle);
-        CACHE.registerBlob(connectionId, blobHandle, expectedBlobId);
+        CACHE.registerBlob(connectionId, blobHandle, expectedBlobId, false);
         CACHE.removeWrite(connectionId, oldBlobId);
         OptionalLong actualBlobId = CACHE.getBlobId(connectionId, blobHandle);
         assertTrue(actualBlobId.isPresent());
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutorTest.java
index 95692641a87..9e188fdb4dc 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/info/FirebirdBlobInfoExecutorTest.java
@@ -18,6 +18,7 @@
 package org.apache.shardingsphere.proxy.frontend.firebird.command.query.info;
 
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.info.FirebirdInfoPacket;
+import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.info.type.blob.FirebirdBlobInfoPacketType;
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.command.query.info.type.blob.FirebirdBlobInfoReturnPacket;
 import 
org.apache.shardingsphere.database.protocol.firebird.packet.generic.FirebirdGenericResponsePacket;
 import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
@@ -37,6 +38,7 @@ import java.util.Collections;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
@@ -71,22 +73,30 @@ class FirebirdBlobInfoExecutorTest {
     
     @Test
     void assertExecuteWithReadBlob() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3}, false);
         Collection<DatabasePacket> actual = new 
FirebirdBlobInfoExecutor(packet, connectionSession).execute();
         assertThat(getBlobLength(actual), is(3));
     }
     
     @Test
     void assertExecuteWithPartiallyReadBlob() {
-        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3});
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3}, false);
         FirebirdBlobReadCache.getInstance().readSegment(CONNECTION_ID, 
BLOB_HANDLE, 1);
         Collection<DatabasePacket> actual = new 
FirebirdBlobInfoExecutor(packet, connectionSession).execute();
-        assertThat(getBlobLength(actual), is(2));
+        assertThat(getBlobLength(actual), is(3));
+    }
+    
+    @Test
+    void assertExecuteWithStreamBlob() {
+        FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3}, true);
+        
when(packet.getInfoItems()).thenReturn(Collections.singletonList(FirebirdBlobInfoPacketType.TYPE));
+        Collection<DatabasePacket> actual = new 
FirebirdBlobInfoExecutor(packet, connectionSession).execute();
+        assertTrue(((FirebirdBlobInfoReturnPacket) 
((FirebirdGenericResponsePacket) 
actual.iterator().next()).getData()).isStreamBlob());
     }
     
     @Test
     void assertExecuteWithWriteBlob() {
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, 3L);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
BLOB_HANDLE, 3L, false);
         FirebirdBlobWriteCache.getInstance().appendSegment(CONNECTION_ID, 
BLOB_HANDLE, new byte[]{1, 2, 3, 4});
         Collection<DatabasePacket> actual = new 
FirebirdBlobInfoExecutor(packet, connectionSession).execute();
         assertThat(getBlobLength(actual), is(4));
diff --git 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/execute/FirebirdExecuteStatementCommandExecutorTest.java
 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/execute/FirebirdExecuteStatementCommandExecutorTest.java
index 83203c18a9b..f157dfb6188 100644
--- 
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/execute/FirebirdExecuteStatementCommandExecutorTest.java
+++ 
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/execute/FirebirdExecuteStatementCommandExecutorTest.java
@@ -219,7 +219,7 @@ class FirebirdExecuteStatementCommandExecutorTest {
     void assertRejectUnclosedBlobParameter() {
         int blobHandle = 7;
         long blobId = 11L;
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         FirebirdBlobWriteCache.getInstance().appendSegment(CONNECTION_ID, 
blobHandle, new byte[]{1, 2});
         when(packet.getStatementId()).thenReturn(2);
         
when(packet.getParameterTypes()).thenReturn(Collections.singletonList(FirebirdBinaryColumnType.BLOB));
@@ -315,7 +315,7 @@ class FirebirdExecuteStatementCommandExecutorTest {
         int blobHandle = 13;
         long blobId = 17L;
         byte[] expectedBytes = new byte[]{3, 4};
-        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId);
+        FirebirdBlobWriteCache.getInstance().registerBlob(CONNECTION_ID, 
blobHandle, blobId, false);
         FirebirdBlobWriteCache.getInstance().appendSegment(CONNECTION_ID, 
blobHandle, expectedBytes);
         FirebirdBlobWriteCache.getInstance().closeWrite(CONNECTION_ID, 
blobHandle);
         List<Object> params = new 
ArrayList<>(Collections.singletonList(blobId));

Reply via email to