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/14 12:51:31 UTC

(shardingsphere) branch master updated: Refactor MySQL COM_QUERY binary literal handling (#39461)

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 4b8adc732ef Refactor MySQL COM_QUERY binary literal handling (#39461)
4b8adc732ef is described below

commit 4b8adc732ef10c3509fa9e8d9f5792b201e3a2fb
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Fri Aug 14 20:51:24 2026 +0800

    Refactor MySQL COM_QUERY binary literal handling (#39461)
---
 .../query/MySQLComQueryBackendHandlerFactory.java  | 53 +++++++++++++---------
 .../MySQLComQueryBackendHandlerFactoryTest.java    |  9 +++-
 2 files changed, 38 insertions(+), 24 deletions(-)

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 7137afa51c7..e7af928a454 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
@@ -46,6 +46,8 @@ import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.Up
 import org.apache.shardingsphere.sql.parser.statement.core.util.MultiSQLSplitter;
 
 import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
 import java.util.Optional;
 
 /**
@@ -61,18 +63,27 @@ final class MySQLComQueryBackendHandlerFactory {
     static ProxyBackendHandler newInstance(final MySQLComQueryPacket packet, final ConnectionSession connectionSession) throws SQLException {
         DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, "MySQL");
         String sql = packet.getSQL();
-        Optional<ProxyBackendHandler> binaryLiteralHandler = tryCreateBinaryLiteralHandler(databaseType, sql, packet, connectionSession);
-        if (binaryLiteralHandler.isPresent()) {
-            return binaryLiteralHandler.get();
+        List<Object> binaryLiteralValues = Collections.emptyList();
+        SQLStatement sqlStatement;
+        Optional<ExtractionResult> extractionResult = tryExtractBinaryLiteral(sql, packet, connectionSession);
+        if (extractionResult.isPresent() && 1 == MultiSQLSplitter.split(extractionResult.get().getSql()).size()) {
+            String parameterizedSQL = SQLHintUtils.removeHint(extractionResult.get().getSql());
+            try {
+                sqlStatement = ProxySQLComQueryParser.parse(parameterizedSQL, databaseType, connectionSession);
+                sql = parameterizedSQL;
+                binaryLiteralValues = extractionResult.get().getBinaryLiteralValues();
+            } catch (final DialectSQLParsingException ignored) {
+                sqlStatement = ProxySQLComQueryParser.parse(sql, databaseType, connectionSession);
+            }
+        } else {
+            sqlStatement = ProxySQLComQueryParser.parse(sql, databaseType, connectionSession);
         }
-        SQLStatement sqlStatement = ProxySQLComQueryParser.parse(sql, databaseType, connectionSession);
-        return areMultiStatements(connectionSession, sqlStatement, sql)
+        return binaryLiteralValues.isEmpty() && areMultiStatements(connectionSession, sqlStatement, sql)
                 ? new MySQLMultiStatementsProxyBackendHandler(connectionSession, sqlStatement, sql)
-                : ProxyBackendHandlerFactory.newInstance(databaseType, sql, sqlStatement, connectionSession, packet.getHintValueContext());
+                : createSingleStatementHandler(databaseType, sql, sqlStatement, binaryLiteralValues, packet, connectionSession);
     }
     
-    private static Optional<ProxyBackendHandler> tryCreateBinaryLiteralHandler(final DatabaseType databaseType, final String sql, final MySQLComQueryPacket packet,
-                                                                               final ConnectionSession connectionSession) throws SQLException {
+    private static Optional<ExtractionResult> tryExtractBinaryLiteral(final String sql, final MySQLComQueryPacket packet, final ConnectionSession connectionSession) throws SQLException {
         Optional<byte[]> originalSQLBytes = packet.findOriginalSQLBytes();
         if (!originalSQLBytes.isPresent() || !requiresBinaryLiteralInspection(sql)) {
             return Optional.empty();
@@ -80,24 +91,22 @@ final class MySQLComQueryBackendHandlerFactory {
         boolean noBackslashEscapes = MySQLSessionSQLMode.get(connectionSession.getAttributeMap()).isNoBackslashEscapes();
         ExtractionResult extractionResult = MySQLComQueryBinaryParameterExtractor.extract(
                 originalSQLBytes.get(), connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get(), noBackslashEscapes);
-        if (extractionResult.getBinaryLiteralValues().isEmpty() || 1 != MultiSQLSplitter.split(extractionResult.getSql()).size()) {
-            return Optional.empty();
-        }
-        String parameterizedSQL = SQLHintUtils.removeHint(extractionResult.getSql());
-        SQLStatement parameterizedSQLStatement;
-        try {
-            parameterizedSQLStatement = ProxySQLComQueryParser.parse(parameterizedSQL, databaseType, connectionSession);
-        } catch (final DialectSQLParsingException ignored) {
-            return Optional.empty();
+        return extractionResult.getBinaryLiteralValues().isEmpty() ? Optional.empty() : Optional.of(extractionResult);
+    }
+    
+    private static ProxyBackendHandler createSingleStatementHandler(final DatabaseType databaseType, final String sql, final SQLStatement sqlStatement,
+                                                                    final List<Object> binaryLiteralValues, final MySQLComQueryPacket packet,
+                                                                    final ConnectionSession connectionSession) throws SQLException {
+        if (binaryLiteralValues.isEmpty()) {
+            return ProxyBackendHandlerFactory.newInstance(databaseType, sql, sqlStatement, connectionSession, packet.getHintValueContext());
         }
         ShardingSphereMetaData metaData = ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData();
-        SQLStatementContext sqlStatementContext = new SQLBindEngine(metaData, connectionSession.getCurrentDatabaseName(), packet.getHintValueContext()).bind(parameterizedSQLStatement);
+        SQLStatementContext sqlStatementContext = new SQLBindEngine(metaData, connectionSession.getCurrentDatabaseName(), packet.getHintValueContext()).bind(sqlStatement);
         if (sqlStatementContext instanceof ParameterAware) {
-            ((ParameterAware) sqlStatementContext).bindParameters(extractionResult.getBinaryLiteralValues());
+            ((ParameterAware) sqlStatementContext).bindParameters(binaryLiteralValues);
         }
-        QueryContext queryContext = new QueryContext(
-                sqlStatementContext, parameterizedSQL, extractionResult.getBinaryLiteralValues(), packet.getHintValueContext(), connectionSession.getConnectionContext(), metaData, true);
-        return Optional.of(ProxyBackendHandlerFactory.newInstance(databaseType, queryContext, connectionSession, true));
+        QueryContext queryContext = new QueryContext(sqlStatementContext, sql, binaryLiteralValues, packet.getHintValueContext(), connectionSession.getConnectionContext(), metaData, true);
+        return ProxyBackendHandlerFactory.newInstance(databaseType, queryContext, connectionSession, true);
     }
     
     private static boolean requiresBinaryLiteralInspection(final String sql) {
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 ca357aae047..dcbe22470da 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
@@ -72,6 +72,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.isA;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
@@ -163,6 +164,7 @@ class MySQLComQueryBackendHandlerFactoryTest {
         QueryContext actual = queryContextCaptor.getValue();
         assertThat(actual.getSql(), is("INSERT INTO t (id, v) VALUES (1, ?)"));
         assertThat(actual.getParameters().size(), is(1));
+        assertTrue(actual.isUseCache());
         Blob blob = (Blob) actual.getParameters().get(0);
         assertArrayEquals(new byte[]{(byte) 0xFF}, blob.getBytes(1, (int) blob.length()));
     }
@@ -207,11 +209,14 @@ class MySQLComQueryBackendHandlerFactoryTest {
     
     @Test
     void assertCreateStandardHandlerForStructuralBinaryLiteral() throws SQLException {
-        when(packet.getSQL()).thenReturn("SHOW TABLES LIKE _binary'�'");
+        String sql = "SHOW TABLES LIKE _binary'�'";
+        when(packet.getSQL()).thenReturn(sql);
         when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql("SHOW TABLES LIKE _binary'", (byte) 0xFF, "'")));
         when(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get()).thenReturn(StandardCharsets.UTF_8);
+        ProxyBackendHandler expectedBackendHandler = mock(ProxyBackendHandler.class);
+        when(ProxyBackendHandlerFactory.newInstance(eq(databaseType), eq(sql), any(SQLStatement.class), eq(connectionSession), any())).thenReturn(expectedBackendHandler);
         ProxyBackendHandler actual = MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
-        assertThat(actual, is(proxyBackendHandler));
+        assertThat(actual, is(expectedBackendHandler));
     }
     
     private static byte[] sql(final String prefix, final byte content, final String suffix) {