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 <[email protected]>
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());

Reply via email to