You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/10/10 16:06:24 UTC
(shardingsphere) branch master updated: Report Firebird end of BLOB together with the last segment data (#40102)
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 2ed6279659d Report Firebird end of BLOB together with the last segment data (#40102)
2ed6279659d is described below
commit 2ed6279659deee66f36c46f7360ab0f6368159aa
Author: Maxim Igoshin <16...@users.noreply.github.com>
AuthorDate: Sat Oct 10 19:06:17 2026 +0300
Report Firebird end of BLOB together with the last segment data (#40102)
The server reads segments into the client buffer while the buffer has room for another segment length and at least one byte, so the response with the last data of the BLOB has object 2 when the buffer is not filled, as the server does.
---
.../blob/FirebirdGetBlobSegmentCommandExecutor.java | 9 ++++++++-
.../FirebirdGetBlobSegmentCommandExecutorTest.java | 20 +++++++++++++++++++-
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutor.java b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutor.java
index bd646e2d905..1a2e3620375 100644
--- a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutor.java
+++ b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/blob/FirebirdGetBlobSegmentCommandExecutor.java
@@ -38,6 +38,9 @@ import java.util.Optional;
* <p>The requested length in the packet is the size of the client buffer receiving the length-prefixed
* segments, so the returned segment data is capped at the requested length minus the 2-byte segment length
* prefix, and at 65535 bytes representable by that prefix.</p>
+ *
+ * <p>Like Firebird, the last data of the BLOB comes with the end of BLOB state when the client buffer still has room
+ * for another segment length prefix and at least one byte after it.</p>
*/
@RequiredArgsConstructor
public final class FirebirdGetBlobSegmentCommandExecutor implements CommandExecutor {
@@ -65,7 +68,7 @@ public final class FirebirdGetBlobSegmentCommandExecutor implements CommandExecu
return Collections.singleton(new FirebirdGenericResponsePacket().setHandle(SEGMENT_STATE_EOF));
}
BlobSegment segment = blobSegment.get();
- int segmentState = segment.isComplete() ? SEGMENT_STATE_COMPLETE : SEGMENT_STATE_PARTIAL;
+ int segmentState = segment.isComplete() ? getLastSegmentState(segment.getData().length) : SEGMENT_STATE_PARTIAL;
FirebirdGetBlobSegmentResponsePacket responsePacket = new FirebirdGetBlobSegmentResponsePacket(segment.getData());
return Collections.singleton(new FirebirdGenericResponsePacket().setHandle(segmentState).setData(responsePacket));
}
@@ -73,4 +76,8 @@ public final class FirebirdGetBlobSegmentCommandExecutor implements CommandExecu
private int getMaxSegmentDataLength() {
return Math.min(Math.max(packet.getSegmentLength() - SEGMENT_LENGTH_PREFIX_SIZE, 0), MAX_SEGMENT_DATA_LENGTH);
}
+
+ private int getLastSegmentState(final int segmentDataLength) {
+ return packet.getSegmentLength() - SEGMENT_LENGTH_PREFIX_SIZE - segmentDataLength > SEGMENT_LENGTH_PREFIX_SIZE ? SEGMENT_STATE_EOF : SEGMENT_STATE_COMPLETE;
+ }
}
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 4ed1904c342..cfc8389236e 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
@@ -29,12 +29,16 @@ 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.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Collection;
+import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -78,7 +82,7 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
FirebirdGetBlobSegmentCommandExecutor executor = new FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession);
Collection<DatabasePacket> actualPackets = executor.execute();
FirebirdGenericResponsePacket actualGenericPacket = (FirebirdGenericResponsePacket) actualPackets.iterator().next();
- assertThat(actualGenericPacket.getHandle(), is(0));
+ assertThat(actualGenericPacket.getHandle(), is(2));
assertThat(getResponseSegment(actualGenericPacket), is(new byte[]{7, 8, 9}));
}
@@ -136,6 +140,16 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
assertThat(getResponseSegment(actualGenericPacket), is(new byte[]{4, 5}));
}
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("lastSegmentStateCases")
+ void assertExecuteWithLastSegment(final String name, final int requestedLength, final int expectedState) {
+ FirebirdBlobReadCache.getInstance().registerBlob(CONNECTION_ID, BLOB_HANDLE, new byte[]{1, 2, 3});
+ when(packet.getSegmentLength()).thenReturn(requestedLength);
+ FirebirdGenericResponsePacket actual = (FirebirdGenericResponsePacket) new FirebirdGetBlobSegmentCommandExecutor(packet, connectionSession).execute().iterator().next();
+ assertThat(actual.getHandle(), is(expectedState));
+ assertThat(getResponseSegment(actual), is(new byte[]{1, 2, 3}));
+ }
+
@Test
void assertExecuteWithRequestedLengthOverMaxSegmentDataLengthExpectsCappedSegment() {
byte[] content = new byte[0xFFFF + 1];
@@ -177,6 +191,10 @@ class FirebirdGetBlobSegmentCommandExecutorTest {
FirebirdBlobReadCache.getInstance().unregisterConnection(2);
}
+ private static Stream<Arguments> lastSegmentStateCases() {
+ return Stream.of(Arguments.of("buffer filled", 5, 0), Arguments.of("room for segment length only", 7, 0), Arguments.of("room for another segment", 8, 2));
+ }
+
@SneakyThrows(ReflectiveOperationException.class)
private byte[] getResponseSegment(final FirebirdGenericResponsePacket responsePacket) {
return (byte[]) Plugins.getMemberAccessor().get(FirebirdGetBlobSegmentResponsePacket.class.getDeclaredField("segment"), responsePacket.getData());