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));