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