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 dd8e43d1684 Fix BLOB result set handling in MySQL Proxy (#39340)
dd8e43d1684 is described below

commit dd8e43d1684fd9d42621e2201c29fb7ae357221a
Author: Liang Zhang <[email protected]>
AuthorDate: Wed Aug 5 01:22:12 2026 +0800

    Fix BLOB result set handling in MySQL Proxy (#39340)
    
    * Prevent negative round-robin index on counter overflow
    
    * Fix BLOB result set handling in MySQL Proxy
---
 .../MySQLByteLenencBinaryProtocolValue.java        | 13 ++++++
 .../query/text/MySQLTextResultSetRowPacket.java    | 13 ++++++
 .../MySQLByteLenencBinaryProtocolValueTest.java    | 46 ++++++++++++++++++++++
 .../text/MySQLTextResultSetRowPacketTest.java      | 43 ++++++++++++++++++++
 4 files changed, 115 insertions(+)

diff --git 
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValue.java
 
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValue.java
index 6ac9afdd530..cbf10d57d44 100644
--- 
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValue.java
+++ 
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValue.java
@@ -17,12 +17,15 @@
 
 package 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.binary.execute.protocol;
 
+import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
 import 
org.apache.shardingsphere.database.protocol.mysql.payload.MySQLPacketPayload;
 import org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Reader;
+import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.SQLException;
 
@@ -40,6 +43,8 @@ public final class MySQLByteLenencBinaryProtocolValue 
implements MySQLBinaryProt
     public void write(final MySQLPacketPayload payload, final Object value) {
         if (value instanceof byte[]) {
             payload.writeBytesLenenc((byte[]) value);
+        } else if (value instanceof Blob) {
+            payload.writeBytesLenenc(readBlob((Blob) value));
         } else if (value instanceof Clob) {
             payload.writeStringLenenc(readClob((Clob) value));
         } else {
@@ -47,6 +52,14 @@ public final class MySQLByteLenencBinaryProtocolValue 
implements MySQLBinaryProt
         }
     }
     
+    private byte[] readBlob(final Blob value) {
+        try (InputStream inputStream = value.getBinaryStream()) {
+            return ByteStreams.toByteArray(inputStream);
+        } catch (final IOException | SQLException ex) {
+            throw new UnknownSQLException(ex);
+        }
+    }
+    
     private String readClob(final Clob value) {
         try (Reader reader = value.getCharacterStream()) {
             return CharStreams.toString(reader);
diff --git 
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacket.java
 
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacket.java
index dff852d38af..a0a3155e807 100644
--- 
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacket.java
+++ 
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacket.java
@@ -17,6 +17,7 @@
 
 package 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text;
 
+import com.google.common.io.ByteStreams;
 import com.google.common.io.CharStreams;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
@@ -26,8 +27,10 @@ import 
org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
 import org.apache.shardingsphere.infra.util.datetime.DateTimeFormatterFactory;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Reader;
 import java.math.BigDecimal;
+import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.SQLException;
 import java.sql.Time;
@@ -83,6 +86,8 @@ public final class MySQLTextResultSetRowPacket extends 
MySQLPacket {
             payload.writeStringLenenc(formatLocalTime((LocalTime) data));
         } else if (data instanceof Time) {
             payload.writeStringLenenc(formatTime((Time) data));
+        } else if (data instanceof Blob) {
+            payload.writeBytesLenenc(readBlob((Blob) data));
         } else if (data instanceof Clob) {
             payload.writeStringLenenc(readClob((Clob) data));
         } else {
@@ -90,6 +95,14 @@ public final class MySQLTextResultSetRowPacket extends 
MySQLPacket {
         }
     }
     
+    private byte[] readBlob(final Blob value) {
+        try (InputStream inputStream = value.getBinaryStream()) {
+            return ByteStreams.toByteArray(inputStream);
+        } catch (final IOException | SQLException ex) {
+            throw new UnknownSQLException(ex);
+        }
+    }
+    
     private String readClob(final Clob value) {
         try (Reader reader = value.getCharacterStream()) {
             return CharStreams.toString(reader);
diff --git 
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValueTest.java
 
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValueTest.java
index 989cb0176b8..c322c16f424 100644
--- 
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValueTest.java
+++ 
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/protocol/MySQLByteLenencBinaryProtocolValueTest.java
@@ -24,10 +24,13 @@ import 
org.apache.shardingsphere.database.protocol.mysql.payload.MySQLPacketPayl
 import org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
 import org.junit.jupiter.api.Test;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.nio.charset.StandardCharsets;
+import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.SQLException;
 
@@ -69,6 +72,49 @@ class MySQLByteLenencBinaryProtocolValueTest {
         assertThat(new MySQLPacketPayload(byteBuf, 
StandardCharsets.UTF_8).readStringLenenc(), is("value"));
     }
     
+    @Test
+    void assertWriteBlob() throws SQLException, IOException {
+        byte[] expected = {0x00, (byte) 0x80, (byte) 0xff, 0x41};
+        InputStream inputStream = spy(new ByteArrayInputStream(expected));
+        Blob blob = mock(Blob.class);
+        when(blob.getBinaryStream()).thenReturn(inputStream);
+        ByteBuf byteBuf = Unpooled.buffer();
+        MySQLPacketPayload payload = new MySQLPacketPayload(byteBuf, 
StandardCharsets.UTF_8);
+        new MySQLByteLenencBinaryProtocolValue().write(payload, blob);
+        assertThat(new MySQLPacketPayload(byteBuf, 
StandardCharsets.UTF_8).readStringLenencByBytes(), is(expected));
+        verify(inputStream).close();
+    }
+    
+    @Test
+    void assertWriteBlobWithIOException() throws SQLException, IOException {
+        IOException expectedCause = new IOException("read error");
+        InputStream inputStream = spy(new InputStream() {
+            
+            @Override
+            public int read() throws IOException {
+                throw expectedCause;
+            }
+        });
+        Blob blob = mock(Blob.class);
+        when(blob.getBinaryStream()).thenReturn(inputStream);
+        ByteBuf byteBuf = Unpooled.buffer();
+        MySQLPacketPayload payload = new MySQLPacketPayload(byteBuf, 
StandardCharsets.UTF_8);
+        UnknownSQLException actual = assertThrows(UnknownSQLException.class, 
() -> new MySQLByteLenencBinaryProtocolValue().write(payload, blob));
+        assertThat(actual.getCause(), is(expectedCause));
+        assertThat(byteBuf.writerIndex(), is(0));
+        verify(inputStream).close();
+    }
+    
+    @Test
+    void assertWriteBlobWithSQLException() throws SQLException {
+        SQLException expectedCause = new SQLException("sql error");
+        Blob blob = mock(Blob.class);
+        when(blob.getBinaryStream()).thenThrow(expectedCause);
+        MySQLPacketPayload payload = new MySQLPacketPayload(Unpooled.buffer(), 
StandardCharsets.UTF_8);
+        UnknownSQLException actual = assertThrows(UnknownSQLException.class, 
() -> new MySQLByteLenencBinaryProtocolValue().write(payload, blob));
+        assertThat(actual.getCause(), is(expectedCause));
+    }
+    
     @Test
     void assertWriteClob() throws SQLException, IOException {
         String expected = "ASCII|δΈ­ζ–‡|πŸ™‚|Γ©|π„ž";
diff --git 
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacketTest.java
 
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacketTest.java
index 1f3539fbebc..20a8398e3e7 100644
--- 
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacketTest.java
+++ 
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/MySQLTextResultSetRowPacketTest.java
@@ -28,10 +28,13 @@ import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.math.BigDecimal;
+import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.SQLException;
 import java.sql.Time;
@@ -46,6 +49,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
@@ -110,6 +114,45 @@ class MySQLTextResultSetRowPacketTest {
         verify(payload).writeStringLenenc(expectedValue);
     }
     
+    @Test
+    void assertWriteBlob() throws SQLException, IOException {
+        byte[] expected = {0x00, (byte) 0x80, (byte) 0xff, 0x41};
+        InputStream inputStream = spy(new ByteArrayInputStream(expected));
+        Blob blob = mock(Blob.class);
+        when(blob.getBinaryStream()).thenReturn(inputStream);
+        new 
MySQLTextResultSetRowPacket(Collections.singletonList(blob)).write((PacketPayload)
 payload);
+        verify(payload).writeBytesLenenc(argThat(actual -> 
Arrays.equals(actual, expected)));
+        verify(inputStream).close();
+    }
+    
+    @Test
+    void assertWriteBlobWithIOException() throws SQLException, IOException {
+        IOException expectedCause = new IOException("read error");
+        InputStream inputStream = spy(new InputStream() {
+            
+            @Override
+            public int read() throws IOException {
+                throw expectedCause;
+            }
+        });
+        Blob blob = mock(Blob.class);
+        when(blob.getBinaryStream()).thenReturn(inputStream);
+        MySQLTextResultSetRowPacket packet = new 
MySQLTextResultSetRowPacket(Collections.singletonList(blob));
+        UnknownSQLException actual = assertThrows(UnknownSQLException.class, 
() -> packet.write((PacketPayload) payload));
+        assertThat(actual.getCause(), is(expectedCause));
+        verify(inputStream).close();
+        verify(payload, never()).writeBytesLenenc(any());
+    }
+    
+    @Test
+    void assertWriteBlobWithSQLException() throws SQLException {
+        SQLException expectedCause = new SQLException("sql error");
+        Blob blob = mock(Blob.class);
+        when(blob.getBinaryStream()).thenThrow(expectedCause);
+        UnknownSQLException actual = assertThrows(UnknownSQLException.class, 
() -> new 
MySQLTextResultSetRowPacket(Collections.singletonList(blob)).write((PacketPayload)
 payload));
+        assertThat(actual.getCause(), is(expectedCause));
+    }
+    
     @Test
     void assertWriteClob() throws SQLException, IOException {
         String expected = "ASCII|δΈ­ζ–‡|πŸ™‚|Γ©|π„ž";

Reply via email to