This is an automated email from the ASF dual-hosted git repository.
RaigorJiang 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 5f28295f952 Fix MySQL STRING parameter decoding for numeric columns
(#40232)
5f28295f952 is described below
commit 5f28295f952d446f86694ed3967e41cec3b870ed
Author: Raigor <[email protected]>
AuthorDate: Mon Oct 19 11:16:52 2026 +0800
Fix MySQL STRING parameter decoding for numeric columns (#40232)
---
.../binary/execute/MySQLComStmtExecutePacket.java | 27 ++++--
.../execute/MySQLComStmtExecutePacketTest.java | 98 ++++++++++++++++++----
2 files changed, 103 insertions(+), 22 deletions(-)
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 ae6ac371c1d..07e08c6696e 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
@@ -149,17 +149,34 @@ public final class MySQLComStmtExecutePacket extends
MySQLCommandPacket {
}
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)) {
+ if (!(value instanceof byte[]) || MySQLBinaryColumnType.STRING !=
parameterType || !isCharacterOrNumericColumnType(parameterColumnTypes,
paramIndex)) {
return value;
}
return new String((byte[]) value, payload.getCharset());
}
- private boolean isCharacterColumnType(final List<MySQLBinaryColumnType>
parameterColumnTypes, final int paramIndex) {
- if (paramIndex >= parameterColumnTypes.size()) {
+ private boolean isCharacterOrNumericColumnType(final
List<MySQLBinaryColumnType> parameterColumnTypes, final int paramIndex) {
+ MySQLBinaryColumnType columnType =
getParameterColumnType(parameterColumnTypes, paramIndex);
+ if (null == columnType) {
return false;
}
- MySQLBinaryColumnType columnType =
parameterColumnTypes.get(paramIndex);
- return MySQLBinaryColumnType.STRING == columnType ||
MySQLBinaryColumnType.VAR_STRING == columnType || MySQLBinaryColumnType.VARCHAR
== columnType;
+ switch (columnType) {
+ case STRING:
+ case VAR_STRING:
+ case VARCHAR:
+ return true;
+ case TINY:
+ case SHORT:
+ case LONG:
+ case LONGLONG:
+ case INT24:
+ case FLOAT:
+ case DOUBLE:
+ case DECIMAL:
+ case NEWDECIMAL:
+ return true;
+ default:
+ return false;
+ }
}
}
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 ca174473977..029e4baef3a 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
@@ -17,6 +17,7 @@
package
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.binary.execute;
+import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLBinaryColumnType;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLNewParametersBoundFlag;
@@ -28,6 +29,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.sql.SQLException;
@@ -160,28 +162,90 @@ class MySQLComStmtExecutePacketTest {
@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, new byte[]{0x61});
- 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(), parameterColumnTypes).get(0);
- assertThat(actualValue instanceof String, is(expectedString));
- if (expectedString) {
- assertThat(actualValue, is("a"));
- return;
+ void assertReadParametersWithStringDecoding(final String name, final
MySQLBinaryColumnType columnType, final String expected, final Charset charset)
throws SQLException {
+ ByteBuf buffer =
Unpooled.wrappedBuffer(createPacketData(MySQLBinaryColumnType.STRING,
expected.getBytes(charset)));
+ try {
+ MySQLComStmtExecutePacket packet = new
MySQLComStmtExecutePacket(new MySQLPacketPayload(buffer, charset), 1);
+ Object actual =
packet.readParameters(packet.getNewParameterTypes(), Collections.emptySet(),
Collections.singletonList(columnType)).get(0);
+ assertThat(actual, isA(String.class));
+ assertThat(actual, is(expected));
+ } finally {
+ buffer.release();
}
- assertThat((byte[]) actualValue, is(new byte[]{0x61}));
}
private static Stream<Arguments> stringDecodingArguments() {
return Stream.of(
- 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("missing-column-type",
MySQLBinaryColumnType.STRING, Collections.emptyList(), false),
- Arguments.of("blob-parameter-type",
MySQLBinaryColumnType.BLOB,
Collections.singletonList(MySQLBinaryColumnType.VAR_STRING), false));
+ Arguments.of("string-column", MySQLBinaryColumnType.STRING,
"0", StandardCharsets.UTF_8),
+ Arguments.of("var-string-column",
MySQLBinaryColumnType.VAR_STRING, "0", StandardCharsets.UTF_8),
+ Arguments.of("varchar-column", MySQLBinaryColumnType.VARCHAR,
"0", StandardCharsets.UTF_8),
+ Arguments.of("tiny-column", MySQLBinaryColumnType.TINY, "48",
StandardCharsets.UTF_8),
+ Arguments.of("short-column", MySQLBinaryColumnType.SHORT,
"48", StandardCharsets.UTF_8),
+ Arguments.of("long-column", MySQLBinaryColumnType.LONG, "48",
StandardCharsets.UTF_8),
+ Arguments.of("longlong-column",
MySQLBinaryColumnType.LONGLONG, "48", StandardCharsets.UTF_8),
+ Arguments.of("int24-column", MySQLBinaryColumnType.INT24,
"48", StandardCharsets.UTF_8),
+ Arguments.of("float-column", MySQLBinaryColumnType.FLOAT,
"48", StandardCharsets.UTF_8),
+ Arguments.of("double-column", MySQLBinaryColumnType.DOUBLE,
"48", StandardCharsets.UTF_8),
+ Arguments.of("decimal-column", MySQLBinaryColumnType.DECIMAL,
"48", StandardCharsets.UTF_8),
+ Arguments.of("newdecimal-column",
MySQLBinaryColumnType.NEWDECIMAL, "48", StandardCharsets.UTF_8),
+ Arguments.of("negative-number",
MySQLBinaryColumnType.LONGLONG, "-48", StandardCharsets.UTF_8),
+ Arguments.of("fractional-number",
MySQLBinaryColumnType.NEWDECIMAL, "48.123456", StandardCharsets.UTF_8),
+ Arguments.of("unsigned-bigint-boundary",
MySQLBinaryColumnType.LONGLONG, "18446744073709551615", StandardCharsets.UTF_8),
+ Arguments.of("invalid-number", MySQLBinaryColumnType.LONGLONG,
"foo_invalid", StandardCharsets.UTF_8),
+ Arguments.of("empty-string", MySQLBinaryColumnType.LONG, "",
StandardCharsets.UTF_8),
+ Arguments.of("numeric-column-charset",
MySQLBinaryColumnType.LONGLONG, "é", StandardCharsets.ISO_8859_1),
+ Arguments.of("character-column-charset",
MySQLBinaryColumnType.VAR_STRING, "foo_中文", StandardCharsets.UTF_8));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("withoutStringDecodingArguments")
+ void assertReadParametersWithoutStringDecoding(final String name, final
MySQLBinaryColumnType paramType, final List<MySQLBinaryColumnType> columnTypes)
throws SQLException {
+ byte[] expected = {0x34, 0x38};
+ ByteBuf buffer = Unpooled.wrappedBuffer(createPacketData(paramType,
expected));
+ try {
+ MySQLComStmtExecutePacket packet = new
MySQLComStmtExecutePacket(new MySQLPacketPayload(buffer,
StandardCharsets.UTF_8), 1);
+ Object actual =
packet.readParameters(packet.getNewParameterTypes(), Collections.emptySet(),
columnTypes).get(0);
+ assertThat(actual, isA(byte[].class));
+ assertThat((byte[]) actual, is(expected));
+ } finally {
+ buffer.release();
+ }
+ }
+
+ private static Stream<Arguments> withoutStringDecodingArguments() {
+ return Stream.of(
+ Arguments.of("missing-column-type",
MySQLBinaryColumnType.STRING, Collections.emptyList()),
+ Arguments.of("unknown-column-type",
MySQLBinaryColumnType.STRING,
Collections.singletonList(MySQLBinaryColumnType.NULL)),
+ Arguments.of("date-column", MySQLBinaryColumnType.STRING,
Collections.singletonList(MySQLBinaryColumnType.DATE)),
+ Arguments.of("blob-parameter-character-column",
MySQLBinaryColumnType.BLOB,
Collections.singletonList(MySQLBinaryColumnType.VAR_STRING)),
+ Arguments.of("blob-parameter-numeric-column",
MySQLBinaryColumnType.BLOB,
Collections.singletonList(MySQLBinaryColumnType.LONGLONG)));
+ }
+
+ @Test
+ void assertReadParametersWithNullStringBoundToNumericColumn() throws
SQLException {
+ byte[] data = {1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, (byte) 0xfe, 0};
+ ByteBuf buffer = Unpooled.wrappedBuffer(data);
+ try {
+ MySQLComStmtExecutePacket packet = new
MySQLComStmtExecutePacket(new MySQLPacketPayload(buffer,
StandardCharsets.UTF_8), 1);
+ assertNull(packet.readParameters(packet.getNewParameterTypes(),
Collections.emptySet(),
Collections.singletonList(MySQLBinaryColumnType.LONGLONG)).get(0));
+ } finally {
+ buffer.release();
+ }
+ }
+
+ @Test
+ void assertReadParametersWithReusedStringTypeBoundToNumericColumn() throws
SQLException {
+ byte[] data = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0x34, 0x38};
+ ByteBuf buffer = Unpooled.wrappedBuffer(data);
+ try {
+ MySQLComStmtExecutePacket packet = new
MySQLComStmtExecutePacket(new MySQLPacketPayload(buffer,
StandardCharsets.UTF_8), 1);
+ List<MySQLPreparedStatementParameterType> paramTypes =
Collections.singletonList(new
MySQLPreparedStatementParameterType(MySQLBinaryColumnType.STRING, 0));
+ Object actual = packet.readParameters(paramTypes,
Collections.emptySet(),
Collections.singletonList(MySQLBinaryColumnType.LONGLONG)).get(0);
+ assertThat(actual, isA(String.class));
+ assertThat(actual, is("48"));
+ } finally {
+ buffer.release();
+ }
}
private static Stream<Arguments> withoutParameterArguments() {