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|δΈζ|π|Γ©|π";