This is an automated email from the ASF dual-hosted git repository.

jiangML 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 82d63200491 Fix incorrect BLOB data writing under MySQL protocol in 
Proxy (#39072)
82d63200491 is described below

commit 82d632004917020166f2b9be949ace1bc4f3de90
Author: Raigor <[email protected]>
AuthorDate: Fri Jul 10 22:52:09 2026 +0800

    Fix incorrect BLOB data writing under MySQL protocol in Proxy (#39072)
    
    * Fix incorrect BLOB data writing under MySQL protocol in Proxy
    
    * Update RELEASE-NOTES.md
---
 RELEASE-NOTES.md                                   |  1 +
 .../binary/execute/MySQLComStmtExecutePacket.java  | 24 ++++++++++++-
 .../execute/MySQLComStmtExecutePacketTest.java     | 40 +++++++++++++++++++---
 3 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 0e0cdefcf81..863215e80fa 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -38,6 +38,7 @@
 1. Sharding: Fix AUTO_INTERVAL sharding failure under JVM default locales that 
use comma decimal separators - 
[#38806](https://github.com/apache/shardingsphere/pull/38806)
 1. DistSQL: Fix case-sensitive storage unit matching in `SHOW RULES USED 
STORAGE UNIT` - [#38848](https://github.com/apache/shardingsphere/pull/38848)
 1. Sharding: Compute the Snowflake key generator epoch in UTC instead of the 
JVM default timezone - 
[#38932](https://github.com/apache/shardingsphere/pull/38932)
+1. Proxy: Fix MySQL BLOB data corruption when string-like prepared statement 
parameters target BLOB columns - 
[#39072](https://github.com/apache/shardingsphere/pull/39072)
 
 ### Enhancements
 
diff --git 
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacket.java
 
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacket.java
index b8c33d92bde..d8289e7ee04 100644
--- 
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacket.java
+++ 
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacket.java
@@ -111,13 +111,35 @@ public final class MySQLComStmtExecutePacket extends 
MySQLCommandPacket {
             MySQLBinaryProtocolValue binaryProtocolValue = 
MySQLBinaryProtocolValueFactory.getBinaryProtocolValue(parameterType);
             Object value = nullBitmap.isNullParameter(paramIndex)
                     ? null
-                    : binaryProtocolValue.read(payload, 
(parameterFlags.get(paramIndex) & 
MySQLColumnDefinitionFlag.UNSIGNED.getValue()) == 
MySQLColumnDefinitionFlag.UNSIGNED.getValue());
+                    : readParameterValue(binaryProtocolValue, 
parameterColumnTypes, paramIndex, parameterType, parameterFlags);
             value = decodeStringParameterValue(parameterColumnTypes, 
paramIndex, parameterType, value);
             result.add(value);
         }
         return result;
     }
     
+    private Object readParameterValue(final MySQLBinaryProtocolValue 
binaryProtocolValue, final List<MySQLBinaryColumnType> parameterColumnTypes, 
final int paramIndex,
+                                      final MySQLBinaryColumnType 
parameterType, final List<Integer> parameterFlags) throws SQLException {
+        if (isStringParameterType(parameterType) && 
isBinaryColumnType(parameterColumnTypes, paramIndex)) {
+            return payload.readStringLenencByBytes();
+        }
+        return binaryProtocolValue.read(payload, 
(parameterFlags.get(paramIndex) & 
MySQLColumnDefinitionFlag.UNSIGNED.getValue()) == 
MySQLColumnDefinitionFlag.UNSIGNED.getValue());
+    }
+    
+    private MySQLBinaryColumnType getParameterColumnType(final 
List<MySQLBinaryColumnType> parameterColumnTypes, final int paramIndex) {
+        return paramIndex < parameterColumnTypes.size() ? 
parameterColumnTypes.get(paramIndex) : null;
+    }
+    
+    private boolean isStringParameterType(final MySQLBinaryColumnType 
parameterType) {
+        return MySQLBinaryColumnType.STRING == parameterType || 
MySQLBinaryColumnType.VAR_STRING == parameterType || 
MySQLBinaryColumnType.VARCHAR == parameterType;
+    }
+    
+    private boolean isBinaryColumnType(final List<MySQLBinaryColumnType> 
parameterColumnTypes, final int paramIndex) {
+        MySQLBinaryColumnType columnType = 
getParameterColumnType(parameterColumnTypes, paramIndex);
+        return MySQLBinaryColumnType.LONG_BLOB == columnType || 
MySQLBinaryColumnType.MEDIUM_BLOB == columnType
+                || MySQLBinaryColumnType.BLOB == columnType || 
MySQLBinaryColumnType.TINY_BLOB == columnType;
+    }
+    
     private Object decodeStringParameterValue(final 
List<MySQLBinaryColumnType> parameterColumnTypes, final int paramIndex, final 
MySQLBinaryColumnType parameterType, final Object value) {
         if (!(value instanceof byte[]) || MySQLBinaryColumnType.STRING != 
parameterType || !isCharacterColumnType(parameterColumnTypes, paramIndex)) {
             return value;
diff --git 
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacketTest.java
 
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacketTest.java
index 35c53341bbf..c7910017c9b 100644
--- 
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacketTest.java
+++ 
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/binary/execute/MySQLComStmtExecutePacketTest.java
@@ -110,12 +110,41 @@ class MySQLComStmtExecutePacketTest {
         assertThat(actual.readParameters(parameterTypes, 
Collections.singleton(0), Collections.emptyList(), Collections.emptyList()), 
is(Collections.singletonList(null)));
     }
     
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("stringParameterTypesBoundToBlobColumnsArguments")
+    void assertReadParametersWithStringTypeBoundToBlobColumn(final String 
name, final MySQLBinaryColumnType parameterType,
+                                                             final 
MySQLBinaryColumnType parameterColumnType) throws SQLException {
+        byte[] data = createPacketData(parameterType, new byte[]{(byte) 0xac, 
(byte) 0xed, (byte) 0xff});
+        MySQLPacketPayload payload = new 
MySQLPacketPayload(Unpooled.wrappedBuffer(data), StandardCharsets.UTF_8);
+        MySQLComStmtExecutePacket actual = new 
MySQLComStmtExecutePacket(payload, 1);
+        List<MySQLPreparedStatementParameterType> parameterTypes = 
actual.getNewParameterTypes();
+        Object actualValue = actual.readParameters(parameterTypes, 
Collections.emptySet(), Collections.singletonList(0), 
Collections.singletonList(parameterColumnType)).get(0);
+        assertTrue(actualValue instanceof byte[]);
+        assertArrayEquals(new byte[]{(byte) 0xac, (byte) 0xed, (byte) 0xff}, 
(byte[]) actualValue);
+    }
+    
+    private static Stream<Arguments> 
stringParameterTypesBoundToBlobColumnsArguments() {
+        return Stream.of(
+                Arguments.of("string-parameter-bound-to-tiny-blob-column", 
MySQLBinaryColumnType.STRING, MySQLBinaryColumnType.TINY_BLOB),
+                Arguments.of("string-parameter-bound-to-blob-column", 
MySQLBinaryColumnType.STRING, MySQLBinaryColumnType.BLOB),
+                Arguments.of("string-parameter-bound-to-medium-blob-column", 
MySQLBinaryColumnType.STRING, MySQLBinaryColumnType.MEDIUM_BLOB),
+                Arguments.of("string-parameter-bound-to-long-blob-column", 
MySQLBinaryColumnType.STRING, MySQLBinaryColumnType.LONG_BLOB),
+                Arguments.of("var-string-parameter-bound-to-tiny-blob-column", 
MySQLBinaryColumnType.VAR_STRING, MySQLBinaryColumnType.TINY_BLOB),
+                Arguments.of("var-string-parameter-bound-to-blob-column", 
MySQLBinaryColumnType.VAR_STRING, MySQLBinaryColumnType.BLOB),
+                
Arguments.of("var-string-parameter-bound-to-medium-blob-column", 
MySQLBinaryColumnType.VAR_STRING, MySQLBinaryColumnType.MEDIUM_BLOB),
+                Arguments.of("var-string-parameter-bound-to-long-blob-column", 
MySQLBinaryColumnType.VAR_STRING, MySQLBinaryColumnType.LONG_BLOB),
+                Arguments.of("varchar-parameter-bound-to-tiny-blob-column", 
MySQLBinaryColumnType.VARCHAR, MySQLBinaryColumnType.TINY_BLOB),
+                Arguments.of("varchar-parameter-bound-to-blob-column", 
MySQLBinaryColumnType.VARCHAR, MySQLBinaryColumnType.BLOB),
+                Arguments.of("varchar-parameter-bound-to-medium-blob-column", 
MySQLBinaryColumnType.VARCHAR, MySQLBinaryColumnType.MEDIUM_BLOB),
+                Arguments.of("varchar-parameter-bound-to-long-blob-column", 
MySQLBinaryColumnType.VARCHAR, MySQLBinaryColumnType.LONG_BLOB));
+    }
+    
     @DisplayName("assertReadParametersWithStringDecoding")
     @ParameterizedTest(name = "{0}")
     @MethodSource("stringDecodingArguments")
     void assertReadParametersWithStringDecoding(final String name, final 
MySQLBinaryColumnType parameterType,
                                                 final 
List<MySQLBinaryColumnType> parameterColumnTypes, final boolean expectedString) 
throws SQLException {
-        byte[] data = createPacketData(parameterType);
+        byte[] data = createPacketData(parameterType, new byte[]{0x61});
         MySQLPacketPayload payload = new 
MySQLPacketPayload(Unpooled.wrappedBuffer(data), StandardCharsets.UTF_8);
         MySQLComStmtExecutePacket actual = new 
MySQLComStmtExecutePacket(payload, 1);
         List<MySQLPreparedStatementParameterType> parameterTypes = 
actual.getNewParameterTypes();
@@ -134,7 +163,6 @@ class MySQLComStmtExecutePacketTest {
                 Arguments.of("string-column", MySQLBinaryColumnType.STRING, 
Collections.singletonList(MySQLBinaryColumnType.STRING), true),
                 Arguments.of("var-string-column", 
MySQLBinaryColumnType.STRING, 
Collections.singletonList(MySQLBinaryColumnType.VAR_STRING), true),
                 Arguments.of("varchar-column", MySQLBinaryColumnType.STRING, 
Collections.singletonList(MySQLBinaryColumnType.VARCHAR), true),
-                Arguments.of("blob-column", MySQLBinaryColumnType.STRING, 
Collections.singletonList(MySQLBinaryColumnType.BLOB), false),
                 Arguments.of("missing-column-type", 
MySQLBinaryColumnType.STRING, Collections.emptyList(), false),
                 Arguments.of("blob-parameter-type", 
MySQLBinaryColumnType.BLOB, 
Collections.singletonList(MySQLBinaryColumnType.VAR_STRING), false));
     }
@@ -146,7 +174,7 @@ class MySQLComStmtExecutePacketTest {
                 Arguments.of("statement-id-256", new byte[]{0x00, 0x01, 0x00, 
0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, 256, 0));
     }
     
-    private byte[] createPacketData(final MySQLBinaryColumnType parameterType) 
{
+    private byte[] createPacketData(final MySQLBinaryColumnType parameterType, 
final byte[] value) {
         List<Byte> result = new ArrayList<>();
         byte[] fixedPrefix = {0x01, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 
0x00, 0x00, 0x01};
         for (byte each : fixedPrefix) {
@@ -154,8 +182,10 @@ class MySQLComStmtExecutePacketTest {
         }
         result.add((byte) parameterType.getValue());
         result.add((byte) 0x00);
-        result.add((byte) 0x01);
-        result.add((byte) 0x61);
+        result.add((byte) value.length);
+        for (byte each : value) {
+            result.add(each);
+        }
         byte[] bytes = new byte[result.size()];
         for (int i = 0; i < result.size(); i++) {
             bytes[i] = result.get(i);

Reply via email to