You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/08/13 12:38:09 UTC
(shardingsphere) branch master updated: Refactor MySQL COM_QUERY original SQL byte handling (#39444)
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 113a5739477 Refactor MySQL COM_QUERY original SQL byte handling (#39444)
113a5739477 is described below
commit 113a5739477a69a0f082e968ab4c13aa0fc8064f
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Thu Aug 13 20:38:02 2026 +0800
Refactor MySQL COM_QUERY original SQL byte handling (#39444)
* Refine gen-ut quality gates
- keep semantic decisions out of mechanical rule scans
- harden coverage and task-scope verification
- remove unsafe gate-result reuse and add regression tests
* Refine gen-ut quality gates
- keep semantic decisions out of mechanical rule scans
- harden coverage and task-scope verification
- remove unsafe gate-result reuse and add regression tests
* Refine gen-ut quality gates
- keep semantic decisions out of mechanical rule scans
- harden coverage and task-scope verification
- remove unsafe gate-result reuse and add regression tests
* Refine gen-ut quality gates
- keep semantic decisions out of mechanical rule scans
- harden coverage and task-scope verification
- remove unsafe gate-result reuse and add regression tests
* Refactor MySQL COM_QUERY original SQL byte handling
---
.../query/text/query/MySQLComQueryPacket.java | 20 +++------------
.../query/text/query/MySQLComQueryPacketTest.java | 11 +-------
.../query/MySQLComQueryBackendHandlerFactory.java | 16 +++++++++++-
.../MySQLComQueryBackendHandlerFactoryTest.java | 30 +++++++++++++++++++---
4 files changed, 46 insertions(+), 31 deletions(-)
diff --git a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacket.java b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacket.java
index f60e753954c..c06f63d00e1 100644
--- a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacket.java
+++ b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacket.java
@@ -34,10 +34,6 @@ import java.util.Optional;
*/
public final class MySQLComQueryPacket extends MySQLCommandPacket implements SQLReceivedPacket {
- private static final String BINARY_INTRODUCER = "_binary";
-
- private static final char MALFORMED_INPUT_REPLACEMENT = (char) 0xFFFD;
-
private final String sql;
private final byte[] originalSQLBytes;
@@ -58,17 +54,7 @@ public final class MySQLComQueryPacket extends MySQLCommandPacket implements SQL
String originSQL = new String(sqlBytes, payload.getCharset());
hintValueContext = SQLHintUtils.extractHint(originSQL);
sql = SQLHintUtils.removeHint(originSQL);
- originalSQLBytes = requiresBinaryLiteralInspection(originSQL) ? sqlBytes : null;
- }
-
- private static boolean requiresBinaryLiteralInspection(final String sql) {
- for (int i = 0; i < sql.length(); i++) {
- char current = sql.charAt(i);
- if (MALFORMED_INPUT_REPLACEMENT == current || '_' == current && sql.regionMatches(true, i, BINARY_INTRODUCER, 0, BINARY_INTRODUCER.length())) {
- return true;
- }
- }
- return false;
+ originalSQLBytes = sqlBytes;
}
@Override
@@ -82,9 +68,9 @@ public final class MySQLComQueryPacket extends MySQLCommandPacket implements SQL
}
/**
- * Find original SQL bytes retained for binary literal inspection.
+ * Find original SQL bytes received from packet payload.
*
- * @return original SQL bytes when binary literal inspection is required
+ * @return original SQL bytes if constructed from packet payload
*/
public Optional<byte[]> findOriginalSQLBytes() {
return Optional.ofNullable(originalSQLBytes);
diff --git a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacketTest.java b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacketTest.java
index 70ea2448140..baa2b461b07 100644
--- a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacketTest.java
+++ b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/command/query/text/query/MySQLComQueryPacketTest.java
@@ -60,16 +60,7 @@ class MySQLComQueryPacketTest {
MySQLComQueryPacket actual = new MySQLComQueryPacket(payload);
assertThat(actual.getSQL(), is(expectedSQL));
assertThat(actual.getHintValueContext().isWriteRouteOnly(), is(expectedWriteRouteOnly));
- assertFalse(actual.findOriginalSQLBytes().isPresent());
- }
-
- @Test
- void assertRetainBinaryLiteralOriginalSQLBytes() {
- byte[] sql = "SELECT _binary'foo'".getBytes(StandardCharsets.UTF_8);
- when(payload.readStringEOFByBytes()).thenReturn(sql);
- when(payload.getCharset()).thenReturn(StandardCharsets.UTF_8);
- MySQLComQueryPacket actual = new MySQLComQueryPacket(payload);
- assertArrayEquals(sql, actual.findOriginalSQLBytes().get());
+ assertArrayEquals(inputSQL.getBytes(StandardCharsets.UTF_8), actual.findOriginalSQLBytes().get());
}
@Test
diff --git a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactory.java b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactory.java
index 6f0fd34004d..e97fdf9cb91 100644
--- a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactory.java
+++ b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactory.java
@@ -54,6 +54,10 @@ import java.util.Optional;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
final class MySQLComQueryBackendHandlerFactory {
+ private static final String BINARY_INTRODUCER = "_binary";
+
+ private static final char MALFORMED_INPUT_REPLACEMENT = (char) 0xFFFD;
+
static ProxyBackendHandler newInstance(final MySQLComQueryPacket packet, final ConnectionSession connectionSession) throws SQLException {
DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "MySQL");
String sql = packet.getSQL();
@@ -70,7 +74,7 @@ final class MySQLComQueryBackendHandlerFactory {
private static Optional<ProxyBackendHandler> tryCreateBinaryLiteralHandler(final DatabaseType databaseType, final String sql, final MySQLComQueryPacket packet,
final ConnectionSession connectionSession) throws SQLException {
Optional<byte[]> originalSQLBytes = packet.findOriginalSQLBytes();
- if (!originalSQLBytes.isPresent() || 1 != MultiSQLSplitter.split(sql).size()) {
+ if (!originalSQLBytes.isPresent() || !requiresBinaryLiteralInspection(sql) || 1 != MultiSQLSplitter.split(sql).size()) {
return Optional.empty();
}
ExtractionResult extractionResult = MySQLComQueryBinaryParameterExtractor.extract(
@@ -96,6 +100,16 @@ final class MySQLComQueryBackendHandlerFactory {
return Optional.of(ProxyBackendHandlerFactory.newInstance(databaseType, queryContext, connectionSession, true));
}
+ private static boolean requiresBinaryLiteralInspection(final String sql) {
+ for (int i = 0; i < sql.length(); i++) {
+ char current = sql.charAt(i);
+ if (MALFORMED_INPUT_REPLACEMENT == current || '_' == current && sql.regionMatches(true, i, BINARY_INTRODUCER, 0, BINARY_INTRODUCER.length())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private static boolean areMultiStatements(final ConnectionSession connectionSession, final SQLStatement sqlStatement, final String sql) {
return isMultiStatementsEnabled(connectionSession)
&& isSuitableMultiStatementsSQLStatement(sqlStatement)
diff --git a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java
index 1e9259bef69..fa6ad265962 100644
--- a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java
+++ b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java
@@ -98,7 +98,7 @@ class MySQLComQueryBackendHandlerFactoryTest {
@BeforeEach
void setUp() throws SQLException {
when(packet.getSQL()).thenReturn("");
- when(packet.findOriginalSQLBytes()).thenReturn(Optional.empty());
+ when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(new byte[0]));
when(packet.getHintValueContext()).thenReturn(new HintValueContext());
when(ProxyBackendHandlerFactory.newInstance(eq(databaseType), anyString(), any(SQLStatement.class), eq(connectionSession), any())).thenReturn(proxyBackendHandler);
when(ProxyBackendHandlerFactory.newInstance(eq(databaseType), any(QueryContext.class), eq(connectionSession), anyBoolean())).thenReturn(proxyBackendHandler);
@@ -107,7 +107,17 @@ class MySQLComQueryBackendHandlerFactoryTest {
@Test
void assertCreateStandardSQLHandler() throws SQLException {
+ String sql = "SELECT 1";
+ when(packet.getSQL()).thenReturn(sql);
+ when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql.getBytes(StandardCharsets.UTF_8)));
+ ProxyBackendHandler actual = MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+ assertThat(actual, is(proxyBackendHandler));
+ }
+
+ @Test
+ void assertCreateStandardSQLHandlerWithoutOriginalSQLBytes() throws SQLException {
when(packet.getSQL()).thenReturn("SELECT 1");
+ when(packet.findOriginalSQLBytes()).thenReturn(Optional.empty());
ProxyBackendHandler actual = MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
assertThat(actual, is(proxyBackendHandler));
}
@@ -115,7 +125,9 @@ class MySQLComQueryBackendHandlerFactoryTest {
@Test
void assertCreateMultiStatementsHandlerForUpdate() throws SQLException {
enableMultiStatements();
- when(packet.getSQL()).thenReturn("UPDATE t SET v=v+1 WHERE id=1;UPDATE t SET v=v+1 WHERE id=2");
+ String sql = "UPDATE t SET v=v+1 WHERE id=1;UPDATE t SET v=v+1 WHERE id=2";
+ when(packet.getSQL()).thenReturn(sql);
+ when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql.getBytes(StandardCharsets.UTF_8)));
ProxyBackendHandler actual = MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
assertThat(actual, isA(MySQLMultiStatementsProxyBackendHandler.class));
}
@@ -123,7 +135,9 @@ class MySQLComQueryBackendHandlerFactoryTest {
@Test
void assertCreateMultiStatementsHandlerForInsertOnDuplicateKey() throws SQLException {
enableMultiStatements();
- when(packet.getSQL()).thenReturn("INSERT INTO t (id, v) VALUES(1,1) ON DUPLICATE KEY UPDATE v=2;INSERT INTO t (id, v) VALUES(2,1) ON DUPLICATE KEY UPDATE v=3");
+ String sql = "INSERT INTO t (id, v) VALUES(1,1) ON DUPLICATE KEY UPDATE v=2;INSERT INTO t (id, v) VALUES(2,1) ON DUPLICATE KEY UPDATE v=3";
+ when(packet.getSQL()).thenReturn(sql);
+ when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql.getBytes(StandardCharsets.UTF_8)));
ProxyBackendHandler actual = MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
assertThat(actual, isA(MySQLMultiStatementsProxyBackendHandler.class));
}
@@ -151,6 +165,16 @@ class MySQLComQueryBackendHandlerFactoryTest {
assertArrayEquals(new byte[]{(byte) 0xFF}, blob.getBytes(1, (int) blob.length()));
}
+ @Test
+ void assertCreateStandardHandlerForValidBinaryLiteral() throws SQLException {
+ String sql = "SELECT _binary'foo'";
+ when(packet.getSQL()).thenReturn(sql);
+ when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql.getBytes(StandardCharsets.UTF_8)));
+ when(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get()).thenReturn(StandardCharsets.UTF_8);
+ ProxyBackendHandler actual = MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+ assertThat(actual, is(proxyBackendHandler));
+ }
+
@Test
void assertCreateMultiStatementsHandlerForMalformedBinaryLiteral() throws SQLException {
enableMultiStatements();