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 6997e3926a0 Fix MySQL COM_QUERY binary string literal corruption 
(#39433)
6997e3926a0 is described below

commit 6997e3926a0303decf8c02d28676d28929254ff2
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Aug 13 16:30:37 2026 +0800

    Fix MySQL COM_QUERY binary string literal corruption (#39433)
    
    * Refactor MySQL COM_QUERY backend handler creation
    
    - move SQL parsing and handler selection into a dedicated factory
    - keep the packet executor focused on response processing
    - separate factory and executor unit tests
    
    * Refactor MySQL COM_QUERY backend handler creation
    
    - move SQL parsing and handler selection into a dedicated factory
    - keep the packet executor focused on response processing
    - separate factory and executor unit tests
    
    * Refactor MySQL COM_QUERY backend handler creation
    
    - move SQL parsing and handler selection into a dedicated factory
    - keep the packet executor focused on response processing
    - separate factory and executor unit tests
    
    * Refactor MySQL COM_QUERY backend handler creation
    
    - move SQL parsing and handler selection into a dedicated factory
    - keep the packet executor focused on response processing
    - separate factory and executor unit tests
---
 RELEASE-NOTES.md                                   |   1 +
 .../query/text/query/MySQLComQueryPacket.java      |  32 +-
 .../query/text/query/MySQLComQueryPacketTest.java  |  26 +-
 .../query/MySQLComQueryBackendHandlerFactory.java  | 117 +++++++
 .../MySQLComQueryBinaryParameterExtractor.java     | 350 +++++++++++++++++++++
 .../text/query/MySQLComQueryPacketExecutor.java    |  35 +--
 .../command/MySQLCommandPacketFactoryTest.java     |   5 +-
 ...=> MySQLComQueryBackendHandlerFactoryTest.java} | 177 ++++++-----
 .../MySQLComQueryBinaryParameterExtractorTest.java | 128 ++++++++
 .../query/MySQLComQueryPacketExecutorTest.java     | 101 +-----
 10 files changed, 760 insertions(+), 212 deletions(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 5e0af8310b6..39655a7baba 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -54,6 +54,7 @@
 1. Proxy: Fix incorrect openGauss composite column type OIDs in simple and 
extended query row descriptions - 
[#39253](https://github.com/apache/shardingsphere/pull/39253)
 1. Proxy: Fix MySQL CLOB and NCLOB result set handling - 
[#39338](https://github.com/apache/shardingsphere/pull/39338)
 1. Proxy: Fix MySQL BLOB result set handling in text and binary protocols - 
[#39340](https://github.com/apache/shardingsphere/pull/39340)
+1. Proxy: Fix MySQL COM_QUERY binary string literal corruption - 
[#39433](https://github.com/apache/shardingsphere/pull/39433)
 1. JDBC & Proxy: Remove default MySQL prepared statement query properties when 
creating data sources - 
[#38593](https://github.com/apache/shardingsphere/pull/38593)
 1. Mode: Fix rule metadata not removed from memory after dropping rules in 
Etcd cluster mode - 
[#38561](https://github.com/apache/shardingsphere/pull/38561)
 1. Agent: Fix wrong target class name in StaticMethodAdviceExecutor error logs 
- [#39077](https://github.com/apache/shardingsphere/pull/39077)
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 46dfbfc0f5a..f60e753954c 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
@@ -25,6 +25,8 @@ import 
org.apache.shardingsphere.database.protocol.packet.sql.SQLReceivedPacket;
 import org.apache.shardingsphere.infra.hint.HintValueContext;
 import org.apache.shardingsphere.infra.hint.SQLHintUtils;
 
+import java.util.Optional;
+
 /**
  * COM_QUERY command packet for MySQL.
  *
@@ -32,8 +34,14 @@ import org.apache.shardingsphere.infra.hint.SQLHintUtils;
  */
 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;
+    
     @Getter
     private final HintValueContext hintValueContext;
     
@@ -41,13 +49,26 @@ public final class MySQLComQueryPacket extends 
MySQLCommandPacket implements SQL
         super(MySQLCommandPacketType.COM_QUERY);
         hintValueContext = SQLHintUtils.extractHint(sql);
         this.sql = SQLHintUtils.removeHint(sql);
+        originalSQLBytes = null;
     }
     
     public MySQLComQueryPacket(final MySQLPacketPayload payload) {
         super(MySQLCommandPacketType.COM_QUERY);
-        String originSQL = payload.readStringEOF();
+        byte[] sqlBytes = payload.readStringEOFByBytes();
+        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;
     }
     
     @Override
@@ -59,4 +80,13 @@ public final class MySQLComQueryPacket extends 
MySQLCommandPacket implements SQL
     public String getSQL() {
         return sql;
     }
+    
+    /**
+     * Find original SQL bytes retained for binary literal inspection.
+     *
+     * @return original SQL bytes when binary literal inspection is required
+     */
+    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 5fea6f5f085..70ea2448140 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
@@ -27,10 +27,13 @@ import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.nio.charset.StandardCharsets;
 import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -46,15 +49,36 @@ class MySQLComQueryPacketTest {
         MySQLComQueryPacket actual = new MySQLComQueryPacket(inputSQL);
         assertThat(actual.getSQL(), is(expectedSQL));
         assertThat(actual.getHintValueContext().isWriteRouteOnly(), 
is(expectedWriteRouteOnly));
+        assertFalse(actual.findOriginalSQLBytes().isPresent());
     }
     
     @ParameterizedTest(name = "{0}")
     @MethodSource("newWithPayloadArguments")
     void assertNewWithPayload(final String name, final String inputSQL, final 
String expectedSQL, final boolean expectedWriteRouteOnly) {
-        when(payload.readStringEOF()).thenReturn(inputSQL);
+        
when(payload.readStringEOFByBytes()).thenReturn(inputSQL.getBytes(StandardCharsets.UTF_8));
+        when(payload.getCharset()).thenReturn(StandardCharsets.UTF_8);
         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());
+    }
+    
+    @Test
+    void assertRetainMalformedOriginalSQLBytes() {
+        byte[] sql = {'S', 'E', 'L', 'E', 'C', 'T', ' ', '\'', (byte) 0xFF, 
'\''};
+        when(payload.readStringEOFByBytes()).thenReturn(sql);
+        when(payload.getCharset()).thenReturn(StandardCharsets.UTF_8);
+        MySQLComQueryPacket actual = new MySQLComQueryPacket(payload);
+        assertArrayEquals(sql, 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
new file mode 100644
index 00000000000..6f0fd34004d
--- /dev/null
+++ 
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactory.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.sql.DialectSQLParsingException;
+import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
+import 
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
+import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.admin.MySQLComSetOptionPacket;
+import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.query.MySQLComQueryPacket;
+import org.apache.shardingsphere.infra.binder.context.aware.ParameterAware;
+import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
+import org.apache.shardingsphere.infra.binder.engine.SQLBindEngine;
+import org.apache.shardingsphere.infra.hint.SQLHintUtils;
+import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
+import org.apache.shardingsphere.infra.session.query.QueryContext;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
+import org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
+import 
org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandlerFactory;
+import org.apache.shardingsphere.proxy.backend.handler.ProxySQLComQueryParser;
+import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query.MySQLComQueryBinaryParameterExtractor.ExtractionResult;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DeleteStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.UpdateStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.util.MultiSQLSplitter;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Backend handler factory for MySQL COM_QUERY.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+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();
+        SQLStatement sqlStatement = ProxySQLComQueryParser.parse(sql, 
databaseType, connectionSession);
+        Optional<ProxyBackendHandler> binaryLiteralHandler = 
tryCreateBinaryLiteralHandler(databaseType, sql, packet, connectionSession);
+        if (binaryLiteralHandler.isPresent()) {
+            return binaryLiteralHandler.get();
+        }
+        return areMultiStatements(connectionSession, sqlStatement, sql)
+                ? new 
MySQLMultiStatementsProxyBackendHandler(connectionSession, sqlStatement, sql)
+                : ProxyBackendHandlerFactory.newInstance(databaseType, sql, 
sqlStatement, connectionSession, packet.getHintValueContext());
+    }
+    
+    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()) {
+            return Optional.empty();
+        }
+        ExtractionResult extractionResult = 
MySQLComQueryBinaryParameterExtractor.extract(
+                originalSQLBytes.get(), 
connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get());
+        if (extractionResult.getBinaryLiteralValues().isEmpty()) {
+            return Optional.empty();
+        }
+        String parameterizedSQL = 
SQLHintUtils.removeHint(extractionResult.getSql());
+        List<Object> binaryLiteralValues = 
extractionResult.getBinaryLiteralValues();
+        SQLStatement parameterizedSQLStatement;
+        try {
+            parameterizedSQLStatement = 
ProxySQLComQueryParser.parse(parameterizedSQL, databaseType, connectionSession);
+        } catch (final DialectSQLParsingException ignored) {
+            return Optional.empty();
+        }
+        ShardingSphereMetaData metaData = 
ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData();
+        SQLStatementContext sqlStatementContext = new SQLBindEngine(metaData, 
connectionSession.getCurrentDatabaseName(), 
packet.getHintValueContext()).bind(parameterizedSQLStatement);
+        if (sqlStatementContext instanceof ParameterAware) {
+            ((ParameterAware) 
sqlStatementContext).bindParameters(binaryLiteralValues);
+        }
+        QueryContext queryContext = new QueryContext(
+                sqlStatementContext, parameterizedSQL, binaryLiteralValues, 
packet.getHintValueContext(), connectionSession.getConnectionContext(), 
metaData, true);
+        return 
Optional.of(ProxyBackendHandlerFactory.newInstance(databaseType, queryContext, 
connectionSession, true));
+    }
+    
+    private static boolean areMultiStatements(final ConnectionSession 
connectionSession, final SQLStatement sqlStatement, final String sql) {
+        return isMultiStatementsEnabled(connectionSession)
+                && isSuitableMultiStatementsSQLStatement(sqlStatement)
+                && MultiSQLSplitter.hasSameTypeMultiStatements(sqlStatement, 
MultiSQLSplitter.split(sql));
+    }
+    
+    private static boolean isMultiStatementsEnabled(final ConnectionSession 
connectionSession) {
+        return 
connectionSession.getAttributeMap().hasAttr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)
+                && MySQLComSetOptionPacket.MYSQL_OPTION_MULTI_STATEMENTS_ON == 
connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get();
+    }
+    
+    private static boolean isSuitableMultiStatementsSQLStatement(final 
SQLStatement sqlStatement) {
+        return containsInsertOnDuplicateKey(sqlStatement) || sqlStatement 
instanceof UpdateStatement || sqlStatement instanceof DeleteStatement;
+    }
+    
+    private static boolean containsInsertOnDuplicateKey(final SQLStatement 
sqlStatement) {
+        return sqlStatement instanceof InsertStatement && ((InsertStatement) 
sqlStatement).getOnDuplicateKeyColumns().isPresent();
+    }
+}
diff --git 
a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBinaryParameterExtractor.java
 
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBinaryParameterExtractor.java
new file mode 100644
index 00000000000..157e979fedd
--- /dev/null
+++ 
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBinaryParameterExtractor.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import javax.sql.rowset.serial.SerialBlob;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.CoderResult;
+import java.nio.charset.StandardCharsets;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Binary parameter extractor for MySQL COM_QUERY.
+ *
+ * <p>Rewrite unambiguous single-quoted literals containing malformed bytes to 
placeholders and preserve their values as {@link SerialBlob} parameters.
+ * Backslash escapes use MySQL's default escaping rules; literals with 
grammar-dependent semantics are left unchanged.</p>
+ */
+final class MySQLComQueryBinaryParameterExtractor {
+    
+    private static final byte[] BINARY_INTRODUCER = 
"_binary".getBytes(StandardCharsets.US_ASCII);
+    
+    private final byte[] sql;
+    
+    private final Charset charset;
+    
+    private final CharsetDecoder decoder;
+    
+    private final ByteBuffer input;
+    
+    private final CharBuffer output;
+    
+    private MySQLComQueryBinaryParameterExtractor(final byte[] sql, final 
Charset charset) {
+        this.sql = sql;
+        this.charset = charset;
+        decoder = 
charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT);
+        input = ByteBuffer.wrap(sql);
+        output = CharBuffer.allocate(2);
+    }
+    
+    static ExtractionResult extract(final byte[] sql, final Charset charset) 
throws SQLException {
+        return new MySQLComQueryBinaryParameterExtractor(sql, 
charset).extract();
+    }
+    
+    private ExtractionResult extract() throws SQLException {
+        Collection<QuotedLiteral> parameterLiterals = findParameterLiterals();
+        if (parameterLiterals.isEmpty()) {
+            return new ExtractionResult(new String(sql, charset), 
Collections.emptyList());
+        }
+        ByteArrayOutputStream sql = new ByteArrayOutputStream(this.sql.length);
+        List<Object> binaryLiteralValues = new 
ArrayList<>(parameterLiterals.size());
+        int offset = 0;
+        for (QuotedLiteral each : parameterLiterals) {
+            int replacementStart = findBinaryIntroducerStart(each.startIndex);
+            replacementStart = 0 <= replacementStart ? replacementStart : 
each.startIndex;
+            sql.write(this.sql, offset, replacementStart - offset);
+            sql.write('?');
+            binaryLiteralValues.add(new SerialBlob(unescape(each.startIndex + 
1, each.endIndex)));
+            offset = each.endIndex + 1;
+        }
+        sql.write(this.sql, offset, this.sql.length - offset);
+        return new ExtractionResult(new String(sql.toByteArray(), charset), 
binaryLiteralValues);
+    }
+    
+    private Collection<QuotedLiteral> findParameterLiterals() {
+        Collection<QuotedLiteral> result = new LinkedList<>();
+        QuotedLiteral previousLiteral = null;
+        boolean previousLiteralIsParameter = false;
+        for (int i = 0; i < sql.length;) {
+            if ('#' == sql[i]) {
+                i = skipLineComment(i + 1);
+            } else if (isDashComment(i)) {
+                i = skipLineComment(i + 2);
+            } else if (isBlockComment(i)) {
+                i = skipBlockComment(i + 2);
+            } else if ('`' == sql[i] || '"' == sql[i] || '\'' == sql[i]) {
+                QuotedLiteral literal = findQuotedLiteral(i, sql[i]);
+                if ('\'' == sql[i] && !literal.isClosed()) {
+                    return Collections.emptyList();
+                }
+                boolean parameterLiteral = isParameterLiteral(sql[i], literal);
+                if (null != previousLiteral && (previousLiteralIsParameter || 
parameterLiteral) && areAdjacent(previousLiteral, literal)) {
+                    return Collections.emptyList();
+                }
+                if (parameterLiteral) {
+                    result.add(literal);
+                }
+                previousLiteral = literal;
+                previousLiteralIsParameter = parameterLiteral;
+                i = literal.isClosed() ? literal.endIndex + 1 : sql.length;
+            } else {
+                int characterLength = readCharacterLength(i);
+                i += 0 < characterLength ? characterLength : 1;
+            }
+        }
+        return result;
+    }
+    
+    private boolean isParameterLiteral(final byte quote, final QuotedLiteral 
literal) {
+        return '\'' == quote && literal.containsMalformedBytes;
+    }
+    
+    private QuotedLiteral findQuotedLiteral(final int startIndex, final byte 
quote) {
+        boolean containsMalformedBytes = false;
+        for (int i = startIndex + 1; i < sql.length;) {
+            int characterLength = readCharacterLength(i);
+            if (0 > characterLength) {
+                containsMalformedBytes = true;
+                i++;
+            } else if (1 < characterLength) {
+                i += characterLength;
+            } else if ('\\' == sql[i]) {
+                int escapedCharacterLength = i + 1 < sql.length ? 
readCharacterLength(i + 1) : 0;
+                if (0 > escapedCharacterLength) {
+                    containsMalformedBytes = true;
+                }
+                i += 1 + Math.max(1, escapedCharacterLength);
+            } else if (quote != sql[i]) {
+                i++;
+            } else if (i + 1 < sql.length && quote == sql[i + 1]) {
+                i += 2;
+            } else {
+                return new QuotedLiteral(startIndex, i, 
containsMalformedBytes);
+            }
+        }
+        return new QuotedLiteral(startIndex, -1, containsMalformedBytes);
+    }
+    
+    private boolean areAdjacent(final QuotedLiteral previousLiteral, final 
QuotedLiteral currentLiteral) {
+        return containsOnlyTrivia(previousLiteral.endIndex + 1, 
findLiteralTokenStart(currentLiteral.startIndex));
+    }
+    
+    private int findLiteralTokenStart(final int quoteIndex) {
+        int prefixEnd = quoteIndex;
+        while (0 < prefixEnd && isWhitespace(sql[prefixEnd - 1])) {
+            prefixEnd--;
+        }
+        int prefixStart = prefixEnd;
+        while (0 < prefixStart && isIdentifierCharacter(prefixStart - 1)) {
+            prefixStart--;
+        }
+        boolean hasCharacterSetIntroducer = prefixStart < prefixEnd && ('_' == 
sql[prefixStart] || prefixEnd - prefixStart == 1 && 'n' == 
toLowerCase(sql[prefixStart]));
+        return hasCharacterSetIntroducer ? prefixStart : quoteIndex;
+    }
+    
+    private boolean containsOnlyTrivia(final int startIndex, final int 
endIndex) {
+        for (int i = startIndex; i < endIndex;) {
+            if (isWhitespace(sql[i])) {
+                i++;
+            } else if ('#' == sql[i]) {
+                i = skipLineComment(i + 1);
+            } else if (isDashComment(i)) {
+                i = skipLineComment(i + 2);
+            } else if (isBlockComment(i)) {
+                i = skipBlockComment(i + 2);
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    private int readCharacterLength(final int index) {
+        if (0 <= sql[index]) {
+            return 1;
+        }
+        decoder.reset();
+        input.limit(sql.length);
+        input.position(index);
+        output.clear();
+        for (int limit = index + 1; limit <= sql.length; limit++) {
+            input.limit(limit);
+            CoderResult decodeResult = decoder.decode(input, output, false);
+            if (decodeResult.isError()) {
+                return -1;
+            }
+            if (0 < output.position()) {
+                return input.position() - index;
+            }
+        }
+        CoderResult decodeResult = decoder.decode(input, output, true);
+        return decodeResult.isError() || 0 == output.position() ? -1 : 
input.position() - index;
+    }
+    
+    private int findBinaryIntroducerStart(final int quoteIndex) {
+        int introducerEnd = quoteIndex;
+        while (0 < introducerEnd && isWhitespace(sql[introducerEnd - 1])) {
+            introducerEnd--;
+        }
+        int introducerStart = introducerEnd - BINARY_INTRODUCER.length;
+        if (0 > introducerStart || isIdentifierCharacter(introducerStart - 1)) 
{
+            return -1;
+        }
+        for (int i = 0; i < BINARY_INTRODUCER.length; i++) {
+            if (toLowerCase(sql[introducerStart + i]) != BINARY_INTRODUCER[i]) 
{
+                return -1;
+            }
+        }
+        return introducerStart;
+    }
+    
+    private boolean isIdentifierCharacter(final int index) {
+        if (0 > index) {
+            return false;
+        }
+        int value = Byte.toUnsignedInt(sql[index]);
+        byte lowerCaseValue = toLowerCase(sql[index]);
+        return 0x80 <= value || '_' == value || '$' == value || '0' <= value 
&& '9' >= value || 'a' <= lowerCaseValue && 'z' >= lowerCaseValue;
+    }
+    
+    private static byte toLowerCase(final byte value) {
+        return 'A' <= value && 'Z' >= value ? (byte) (value + 'a' - 'A') : 
value;
+    }
+    
+    private static boolean isWhitespace(final byte value) {
+        return ' ' == value || '\t' == value || '\n' == value || '\r' == value 
|| '\f' == value;
+    }
+    
+    private boolean isDashComment(final int index) {
+        return index + 2 < sql.length && '-' == sql[index] && '-' == sql[index 
+ 1] && Byte.toUnsignedInt(sql[index + 2]) <= ' ';
+    }
+    
+    private boolean isBlockComment(final int index) {
+        return index + 1 < sql.length && '/' == sql[index] && '*' == sql[index 
+ 1];
+    }
+    
+    private int skipLineComment(final int startIndex) {
+        int result = startIndex;
+        while (result < sql.length && '\n' != sql[result] && '\r' != 
sql[result]) {
+            result++;
+        }
+        return result;
+    }
+    
+    private int skipBlockComment(final int startIndex) {
+        for (int i = startIndex; i + 1 < sql.length; i++) {
+            if ('*' == sql[i] && '/' == sql[i + 1]) {
+                return i + 2;
+            }
+        }
+        return sql.length;
+    }
+    
+    private byte[] unescape(final int startIndex, final int endIndex) {
+        ByteArrayOutputStream result = new ByteArrayOutputStream(endIndex - 
startIndex);
+        for (int i = startIndex; i < endIndex;) {
+            int characterLength = readCharacterLength(i);
+            if (1 < characterLength) {
+                result.write(sql, i, characterLength);
+                i += characterLength;
+            } else if ('\'' == sql[i] && i + 1 < endIndex && '\'' == sql[i + 
1]) {
+                result.write('\'');
+                i += 2;
+            } else if ('\\' != sql[i] || i + 1 >= endIndex) {
+                result.write(sql[i]);
+                i++;
+            } else {
+                int escapedCharacterLength = readCharacterLength(i + 1);
+                if (1 < escapedCharacterLength) {
+                    result.write(sql, i + 1, escapedCharacterLength);
+                    i += escapedCharacterLength + 1;
+                } else {
+                    writeEscapedByte(result, sql[i + 1]);
+                    i += 2;
+                }
+            }
+        }
+        return result.toByteArray();
+    }
+    
+    private static void writeEscapedByte(final ByteArrayOutputStream output, 
final byte value) {
+        switch (value) {
+            case '0':
+                output.write(0);
+                break;
+            case 'b':
+                output.write('\b');
+                break;
+            case 'n':
+                output.write('\n');
+                break;
+            case 'r':
+                output.write('\r');
+                break;
+            case 't':
+                output.write('\t');
+                break;
+            case 'Z':
+                output.write(0x1A);
+                break;
+            case '%':
+            case '_':
+                output.write('\\');
+                output.write(value);
+                break;
+            default:
+                output.write(value);
+                break;
+        }
+    }
+    
+    @RequiredArgsConstructor
+    @Getter
+    static final class ExtractionResult {
+        
+        private final String sql;
+        
+        private final List<Object> binaryLiteralValues;
+    }
+    
+    @RequiredArgsConstructor
+    private static final class QuotedLiteral {
+        
+        private final int startIndex;
+        
+        private final int endIndex;
+        
+        private final boolean containsMalformedBytes;
+        
+        private boolean isClosed() {
+            return 0 <= endIndex;
+        }
+    }
+}
diff --git 
a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutor.java
 
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutor.java
index 5c52e5fe1be..9769d580174 100644
--- 
a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutor.java
+++ 
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutor.java
@@ -18,17 +18,12 @@
 package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
 
 import lombok.Getter;
-import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
 import org.apache.shardingsphere.database.protocol.mysql.packet.MySQLPacket;
-import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.admin.MySQLComSetOptionPacket;
 import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.MySQLTextResultSetRowPacket;
 import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.query.MySQLComQueryPacket;
 import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
-import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
-import 
org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandlerFactory;
-import org.apache.shardingsphere.proxy.backend.handler.ProxySQLComQueryParser;
 import org.apache.shardingsphere.proxy.backend.response.header.ResponseHeader;
 import 
org.apache.shardingsphere.proxy.backend.response.header.query.QueryResponseHeader;
 import 
org.apache.shardingsphere.proxy.backend.response.header.update.MultiStatementsUpdateResponseHeader;
@@ -38,11 +33,6 @@ import 
org.apache.shardingsphere.proxy.frontend.command.executor.QueryCommandExe
 import org.apache.shardingsphere.proxy.frontend.command.executor.ResponseType;
 import 
org.apache.shardingsphere.proxy.frontend.mysql.command.ServerStatusFlagCalculator;
 import 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.builder.ResponsePacketBuilder;
-import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
-import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DeleteStatement;
-import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
-import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.UpdateStatement;
-import 
org.apache.shardingsphere.sql.parser.statement.core.util.MultiSQLSplitter;
 
 import java.sql.SQLException;
 import java.util.Collection;
@@ -64,33 +54,10 @@ public final class MySQLComQueryPacketExecutor implements 
QueryCommandExecutor {
     
     public MySQLComQueryPacketExecutor(final MySQLComQueryPacket packet, final 
ConnectionSession connectionSession) throws SQLException {
         this.connectionSession = connectionSession;
-        DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "MySQL");
-        SQLStatement sqlStatement = 
ProxySQLComQueryParser.parse(packet.getSQL(), databaseType, connectionSession);
-        proxyBackendHandler = areMultiStatements(connectionSession, 
sqlStatement, packet.getSQL())
-                ? new 
MySQLMultiStatementsProxyBackendHandler(connectionSession, sqlStatement, 
packet.getSQL())
-                : ProxyBackendHandlerFactory.newInstance(databaseType, 
packet.getSQL(), sqlStatement, connectionSession, packet.getHintValueContext());
+        proxyBackendHandler = 
MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
         characterSet = 
connectionSession.getAttributeMap().attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).get().getId();
     }
     
-    private boolean areMultiStatements(final ConnectionSession 
connectionSession, final SQLStatement sqlStatement, final String sql) {
-        return isMultiStatementsEnabled(connectionSession)
-                && isSuitableMultiStatementsSQLStatement(sqlStatement)
-                && MultiSQLSplitter.hasSameTypeMultiStatements(sqlStatement, 
MultiSQLSplitter.split(sql));
-    }
-    
-    private boolean isMultiStatementsEnabled(final ConnectionSession 
connectionSession) {
-        return 
connectionSession.getAttributeMap().hasAttr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)
-                && MySQLComSetOptionPacket.MYSQL_OPTION_MULTI_STATEMENTS_ON == 
connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get();
-    }
-    
-    private boolean isSuitableMultiStatementsSQLStatement(final SQLStatement 
sqlStatement) {
-        return containsInsertOnDuplicateKey(sqlStatement) || sqlStatement 
instanceof UpdateStatement || sqlStatement instanceof DeleteStatement;
-    }
-    
-    private boolean containsInsertOnDuplicateKey(final SQLStatement 
sqlStatement) {
-        return sqlStatement instanceof InsertStatement && ((InsertStatement) 
sqlStatement).getOnDuplicateKeyColumns().isPresent();
-    }
-    
     @Override
     public Collection<DatabasePacket> execute() throws SQLException {
         ResponseHeader responseHeader = proxyBackendHandler.execute();
diff --git 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/MySQLCommandPacketFactoryTest.java
 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/MySQLCommandPacketFactoryTest.java
index 9339532b2de..0fce0268df9 100644
--- 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/MySQLCommandPacketFactoryTest.java
+++ 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/MySQLCommandPacketFactoryTest.java
@@ -48,6 +48,8 @@ import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.nio.charset.StandardCharsets;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.isA;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -81,7 +83,8 @@ class MySQLCommandPacketFactoryTest {
     
     @Test
     void assertNewInstanceWithComQueryPacket() {
-        when(payload.readStringEOF()).thenReturn("SHOW TABLES");
+        when(payload.readStringEOFByBytes()).thenReturn("SHOW 
TABLES".getBytes(StandardCharsets.UTF_8));
+        when(payload.getCharset()).thenReturn(StandardCharsets.UTF_8);
         
assertThat(MySQLCommandPacketFactory.newInstance(MySQLCommandPacketType.COM_QUERY,
 payload, connectionSession), isA(MySQLComQueryPacket.class));
     }
     
diff --git 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java
similarity index 53%
copy from 
proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
copy to 
proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java
index f727c87ab58..1e9259bef69 100644
--- 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
+++ 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBackendHandlerFactoryTest.java
@@ -18,20 +18,20 @@
 package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
 
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
-import 
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCharacterSets;
+import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
 import 
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
-import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.MySQLTextResultSetRowPacket;
+import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.admin.MySQLComSetOptionPacket;
 import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.query.MySQLComQueryPacket;
-import 
org.apache.shardingsphere.database.protocol.mysql.packet.generic.MySQLOKPacket;
-import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
 import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
 import org.apache.shardingsphere.infra.config.props.ConfigurationPropertyKey;
+import org.apache.shardingsphere.infra.hint.HintValueContext;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import 
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
 import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
 import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
 import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
 import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
+import org.apache.shardingsphere.infra.session.query.QueryContext;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
@@ -39,50 +39,50 @@ import org.apache.shardingsphere.parser.rule.SQLParserRule;
 import 
org.apache.shardingsphere.parser.rule.builder.DefaultSQLParserRuleConfigurationBuilder;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
-import 
org.apache.shardingsphere.proxy.backend.response.header.query.QueryHeader;
-import 
org.apache.shardingsphere.proxy.backend.response.header.query.QueryResponseHeader;
-import 
org.apache.shardingsphere.proxy.backend.response.header.update.MultiStatementsUpdateResponseHeader;
-import 
org.apache.shardingsphere.proxy.backend.response.header.update.UpdateResponseHeader;
+import 
org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandlerFactory;
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
-import org.apache.shardingsphere.proxy.frontend.command.executor.ResponseType;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 import org.apache.shardingsphere.sqltranslator.rule.SQLTranslatorRule;
 import 
org.apache.shardingsphere.sqltranslator.rule.builder.DefaultSQLTranslatorRuleConfigurationBuilder;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
+import 
org.apache.shardingsphere.test.infra.framework.extension.mock.ConstructionMockSettings;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.internal.configuration.plugins.Plugins;
 import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.plugins.MemberAccessor;
 import org.mockito.quality.Strictness;
 
+import java.nio.charset.StandardCharsets;
+import java.sql.Blob;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.Iterator;
+import java.util.Optional;
 import java.util.Properties;
 
-import static org.hamcrest.Matchers.is;
 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.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(AutoMockExtension.class)
-@StaticMockSettings(ProxyContext.class)
+@StaticMockSettings({ProxyContext.class, ProxyBackendHandlerFactory.class})
+@ConstructionMockSettings(MySQLMultiStatementsProxyBackendHandler.class)
 @MockitoSettings(strictness = Strictness.LENIENT)
-class MySQLComQueryPacketExecutorTest {
+class MySQLComQueryBackendHandlerFactoryTest {
     
     private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "MySQL");
     
@@ -96,69 +96,96 @@ class MySQLComQueryPacketExecutorTest {
     private ConnectionSession connectionSession;
     
     @BeforeEach
-    void setUp() {
+    void setUp() throws SQLException {
         when(packet.getSQL()).thenReturn("");
-        
when(connectionSession.getAttributeMap().attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).get()).thenReturn(MySQLCharacterSets.UTF8MB4_GENERAL_CI);
+        when(packet.findOriginalSQLBytes()).thenReturn(Optional.empty());
+        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);
+        mockProxyContext();
     }
     
     @Test
-    void assertIsQueryResponse() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
-        MySQLComQueryPacketExecutor mysqlComQueryPacketExecutor = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 mysqlComQueryPacketExecutor, proxyBackendHandler);
-        QueryHeader queryHeader = mock(QueryHeader.class);
-        when(queryHeader.getColumnTypeName()).thenReturn("VARCHAR");
-        when(proxyBackendHandler.execute()).thenReturn(new 
QueryResponseHeader(Collections.singletonList(queryHeader)));
-        mysqlComQueryPacketExecutor.execute();
-        assertThat(mysqlComQueryPacketExecutor.getResponseType(), 
is(ResponseType.QUERY));
+    void assertCreateStandardSQLHandler() throws SQLException {
+        when(packet.getSQL()).thenReturn("SELECT 1");
+        ProxyBackendHandler actual = 
MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+        assertThat(actual, is(proxyBackendHandler));
     }
     
     @Test
-    void assertIsUpdateResponse() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
-        MySQLComQueryPacketExecutor mysqlComQueryPacketExecutor = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 mysqlComQueryPacketExecutor, proxyBackendHandler);
-        when(proxyBackendHandler.execute()).thenReturn(new 
UpdateResponseHeader(mock(SQLStatement.class)));
-        mysqlComQueryPacketExecutor.execute();
-        assertThat(mysqlComQueryPacketExecutor.getResponseType(), 
is(ResponseType.UPDATE));
+    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");
+        ProxyBackendHandler actual = 
MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+        assertThat(actual, isA(MySQLMultiStatementsProxyBackendHandler.class));
     }
     
     @Test
-    void assertExecuteMultiUpdateStatements() throws SQLException, 
NoSuchFieldException, IllegalAccessException {
+    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");
+        ProxyBackendHandler actual = 
MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+        assertThat(actual, isA(MySQLMultiStatementsProxyBackendHandler.class));
+    }
+    
+    private void enableMultiStatements() {
         
when(connectionSession.getAttributeMap().hasAttr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(true);
-        
when(connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get()).thenReturn(0);
-        when(connectionSession.getCurrentDatabaseName()).thenReturn("foo_db");
-        when(packet.getSQL()).thenReturn("UPDATE t SET v=v+1 WHERE id=1;UPDATE 
t SET v=v+1 WHERE id=2;UPDATE t SET v=v+1 WHERE id=3");
-        ContextManager contextManager = mock(ContextManager.class);
-        MetaDataContexts metaDataContexts = mockMetaDataContexts();
-        
when(contextManager.getMetaDataContexts()).thenReturn(metaDataContexts);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
-        MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
-        when(proxyBackendHandler.execute()).thenReturn(new 
UpdateResponseHeader(mock(SQLStatement.class)));
-        Collection<DatabasePacket> actualPackets = actual.execute();
-        assertThat(actualPackets.size(), is(1));
-        assertThat(actualPackets.iterator().next(), isA(MySQLOKPacket.class));
+        
when(connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get()).thenReturn(MySQLComSetOptionPacket.MYSQL_OPTION_MULTI_STATEMENTS_ON);
     }
     
     @Test
-    void assertExecuteMultiInsertOnDuplicateKeyStatements() throws 
SQLException, NoSuchFieldException, IllegalAccessException {
-        
when(connectionSession.getAttributeMap().hasAttr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(true);
-        
when(connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get()).thenReturn(0);
+    void assertCreatePreparedHandlerForBinaryLiteral() throws SQLException {
+        when(packet.getSQL()).thenReturn("INSERT INTO t (id, v) VALUES (1, 
_binary'�')");
+        when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql(
+                "/* SHARDINGSPHERE_HINT: WRITE_ROUTE_ONLY=true */ INSERT INTO 
t (id, v) VALUES (1, _binary'", (byte) 0xFF, "')")));
+        
when(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get()).thenReturn(StandardCharsets.UTF_8);
         when(connectionSession.getCurrentDatabaseName()).thenReturn("foo_db");
-        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");
-        ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
+        
when(connectionSession.getConnectionContext().getCurrentDatabaseName()).thenReturn(Optional.of("foo_db"));
+        ArgumentCaptor<QueryContext> queryContextCaptor = 
ArgumentCaptor.forClass(QueryContext.class);
+        when(ProxyBackendHandlerFactory.newInstance(eq(databaseType), 
queryContextCaptor.capture(), eq(connectionSession), 
eq(true))).thenReturn(proxyBackendHandler);
+        MySQLComQueryBackendHandlerFactory.newInstance(packet, 
connectionSession);
+        QueryContext actual = queryContextCaptor.getValue();
+        assertThat(actual.getSql(), is("INSERT INTO t (id, v) VALUES (1, ?)"));
+        assertThat(actual.getParameters().size(), is(1));
+        Blob blob = (Blob) actual.getParameters().get(0);
+        assertArrayEquals(new byte[]{(byte) 0xFF}, blob.getBytes(1, (int) 
blob.length()));
+    }
+    
+    @Test
+    void assertCreateMultiStatementsHandlerForMalformedBinaryLiteral() throws 
SQLException {
+        enableMultiStatements();
+        when(packet.getSQL()).thenReturn("UPDATE t SET v = _binary'�' WHERE id 
= 1; UPDATE t SET v = _binary'bar' WHERE id = 2");
+        when(packet.findOriginalSQLBytes()).thenReturn(Optional.of(sql(
+                "UPDATE t SET v = _binary'", (byte) 0xFF, "' WHERE id = 1; 
UPDATE t SET v = _binary'bar' WHERE id = 2")));
+        
when(connectionSession.getAttributeMap().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get()).thenReturn(StandardCharsets.UTF_8);
+        ProxyBackendHandler actual = 
MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+        assertThat(actual, isA(MySQLMultiStatementsProxyBackendHandler.class));
+    }
+    
+    @Test
+    void assertCreateStandardHandlerForStructuralBinaryLiteral() throws 
SQLException {
+        when(packet.getSQL()).thenReturn("SHOW TABLES LIKE _binary'�'");
+        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 actual = 
MySQLComQueryBackendHandlerFactory.newInstance(packet, connectionSession);
+        assertThat(actual, is(proxyBackendHandler));
+    }
+    
+    private static byte[] sql(final String prefix, final byte content, final 
String suffix) {
+        byte[] prefixBytes = prefix.getBytes(StandardCharsets.UTF_8);
+        byte[] suffixBytes = suffix.getBytes(StandardCharsets.UTF_8);
+        byte[] result = new byte[prefixBytes.length + 1 + suffixBytes.length];
+        System.arraycopy(prefixBytes, 0, result, 0, prefixBytes.length);
+        result[prefixBytes.length] = content;
+        System.arraycopy(suffixBytes, 0, result, prefixBytes.length + 1, 
suffixBytes.length);
+        return result;
+    }
+    
+    private void mockProxyContext() {
+        ContextManager contextManager = mock(ContextManager.class);
         MetaDataContexts metaDataContexts = mockMetaDataContexts();
         
when(contextManager.getMetaDataContexts()).thenReturn(metaDataContexts);
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
-        MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        MemberAccessor accessor = Plugins.getMemberAccessor();
-        
accessor.set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
-        when(proxyBackendHandler.execute())
-                .thenReturn(new 
MultiStatementsUpdateResponseHeader(Arrays.asList(new 
UpdateResponseHeader(mock(SQLStatement.class)), new 
UpdateResponseHeader(mock(SQLStatement.class)))));
-        Collection<DatabasePacket> actualPackets = actual.execute();
-        assertThat(actualPackets.size(), is(2));
-        Iterator<DatabasePacket> iterator = actualPackets.iterator();
-        assertThat(iterator.next(), isA(MySQLOKPacket.class));
-        assertThat(iterator.next(), isA(MySQLOKPacket.class));
     }
     
     private MetaDataContexts mockMetaDataContexts() {
@@ -184,26 +211,4 @@ class MySQLComQueryPacketExecutorTest {
         return new ShardingSphereDatabase("foo_db", databaseType, new 
ResourceMetaData(Collections.emptyMap()), new 
RuleMetaData(Collections.emptyList()), Collections.singleton(schema),
                 new ConfigurationProperties(new Properties()));
     }
-    
-    @Test
-    void assertNext() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
-        MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
-        when(proxyBackendHandler.next()).thenReturn(true, false);
-        assertTrue(actual.next());
-        assertFalse(actual.next());
-    }
-    
-    @Test
-    void assertGetQueryRowPacket() throws SQLException {
-        assertThat(new MySQLComQueryPacketExecutor(packet, 
connectionSession).getQueryRowPacket(), isA(MySQLTextResultSetRowPacket.class));
-    }
-    
-    @Test
-    void assertClose() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
-        MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
-        actual.close();
-        verify(proxyBackendHandler).close();
-    }
 }
diff --git 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBinaryParameterExtractorTest.java
 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBinaryParameterExtractorTest.java
new file mode 100644
index 00000000000..ef5563d2c52
--- /dev/null
+++ 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryBinaryParameterExtractorTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.sql.Blob;
+import java.sql.SQLException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+class MySQLComQueryBinaryParameterExtractorTest {
+    
+    @Test
+    void assertExtractConnectorJBinaryLiteral() throws SQLException {
+        byte[] content = {'\\', '0', '\\', '\'', '\\', '\\', (byte) 0xFF};
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(
+                sql("INSERT INTO t VALUES (_binary'", content, "')"), 
StandardCharsets.UTF_8);
+        assertThat(actual.getSql(), is("INSERT INTO t VALUES (?)"));
+        assertArrayEquals(new byte[]{0, '\'', '\\', (byte) 0xFF}, 
getBlobBytes(actual.getBinaryLiteralValues().get(0)));
+    }
+    
+    @Test
+    void assertExtractBackslashEscapedMalformedLiteral() throws SQLException {
+        byte[] content = {'\\', 'b', '\\', 'n', '\\', 'r', '\\', 't', '\\', 
'Z', '\\', 'q', '\\', '%', '\\', '_', (byte) 0xFF};
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(
+                sql("INSERT INTO t VALUES ('", content, "')"), 
StandardCharsets.UTF_8);
+        assertThat(actual.getSql(), is("INSERT INTO t VALUES (?)"));
+        assertArrayEquals(new byte[]{'\b', '\n', '\r', '\t', 0x1A, 'q', '\\', 
'%', '\\', '_', (byte) 0xFF}, 
getBlobBytes(actual.getBinaryLiteralValues().get(0)));
+    }
+    
+    @Test
+    void assertExtractMalformedLiteral() throws SQLException {
+        byte[] content = {'f', 'o', '\'', '\'', (byte) 0xFF};
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(
+                sql("INSERT INTO t VALUES ('", content, "')"), 
StandardCharsets.UTF_8);
+        assertThat(actual.getSql(), is("INSERT INTO t VALUES (?)"));
+        assertArrayEquals(new byte[]{'f', 'o', '\'', (byte) 0xFF}, 
getBlobBytes(actual.getBinaryLiteralValues().get(0)));
+    }
+    
+    @Test
+    void assertExtractOnlyMalformedLiteral() throws SQLException {
+        byte[] sql = sql("SELECT _BiNaRy 'fo''o', '", new byte[]{(byte) 0xFF}, 
"'");
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(sql, StandardCharsets.UTF_8);
+        assertThat(actual.getSql(), is("SELECT _BiNaRy 'fo''o', ?"));
+        assertThat(actual.getBinaryLiteralValues().size(), is(1));
+        assertArrayEquals(new byte[]{(byte) 0xFF}, 
getBlobBytes(actual.getBinaryLiteralValues().get(0)));
+    }
+    
+    @Test
+    void assertSkipValidLiteral() throws SQLException {
+        String sql = "SELECT '😀', _binary'foo'";
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(sql.getBytes(StandardCharsets.UTF_8),
 StandardCharsets.UTF_8);
+        assertThat(actual.getBinaryLiteralValues(), empty());
+    }
+    
+    @Test
+    void assertIgnoreNonLiteralBinaryTokens() throws SQLException {
+        String sql = "SELECT '_binary', `_binary'ignored`, 
\"_binary'ignored'\", 1 /* _binary'x' */ -- _binary'y'\n";
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(sql.getBytes(StandardCharsets.UTF_8),
 StandardCharsets.UTF_8);
+        assertThat(actual.getBinaryLiteralValues(), empty());
+    }
+    
+    @Test
+    void assertExtractMalformedLiteralWithMultibyteTrailBackslash() throws 
SQLException {
+        Charset charset = Charset.forName("Shift_JIS");
+        byte[] content = {(byte) 0x83, '\\', (byte) 0xFF};
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(sql("SELECT _binary'", content, 
"'"), charset);
+        assertArrayEquals(content, 
getBlobBytes(actual.getBinaryLiteralValues().get(0)));
+    }
+    
+    @Test
+    void assertKeepMalformedSQLForParser() throws SQLException {
+        byte[] sql = "SELECT _binary'foo".getBytes(StandardCharsets.UTF_8);
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(sql, StandardCharsets.UTF_8);
+        assertThat(actual.getBinaryLiteralValues(), empty());
+    }
+    
+    @Test
+    void assertSkipDoubleQuotedLiteral() throws SQLException {
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(
+                sql("SELECT _binary\"", new byte[]{(byte) 0xFF}, "\""), 
StandardCharsets.UTF_8);
+        assertThat(actual.getBinaryLiteralValues(), empty());
+    }
+    
+    @Test
+    void assertSkipAdjacentLiteral() throws SQLException {
+        MySQLComQueryBinaryParameterExtractor.ExtractionResult actual = 
MySQLComQueryBinaryParameterExtractor.extract(
+                sql("SELECT _binary'", new byte[]{(byte) 0xFF}, "' 'foo'"), 
StandardCharsets.UTF_8);
+        assertThat(actual.getBinaryLiteralValues(), empty());
+    }
+    
+    private static byte[] sql(final String prefix, final byte[] content, final 
String suffix) {
+        byte[] prefixBytes = prefix.getBytes(StandardCharsets.US_ASCII);
+        byte[] suffixBytes = suffix.getBytes(StandardCharsets.US_ASCII);
+        ByteArrayOutputStream result = new 
ByteArrayOutputStream(prefixBytes.length + content.length + suffixBytes.length);
+        result.write(prefixBytes, 0, prefixBytes.length);
+        result.write(content, 0, content.length);
+        result.write(suffixBytes, 0, suffixBytes.length);
+        return result.toByteArray();
+    }
+    
+    private static byte[] getBlobBytes(final Object value) throws SQLException 
{
+        Blob blob = (Blob) value;
+        return blob.getBytes(1, (int) blob.length());
+    }
+}
diff --git 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
index f727c87ab58..b401129d71f 100644
--- 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
+++ 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLComQueryPacketExecutorTest.java
@@ -17,28 +17,14 @@
 
 package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
 
-import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCharacterSets;
 import 
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
 import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.MySQLTextResultSetRowPacket;
 import 
org.apache.shardingsphere.database.protocol.mysql.packet.command.query.text.query.MySQLComQueryPacket;
 import 
org.apache.shardingsphere.database.protocol.mysql.packet.generic.MySQLOKPacket;
 import org.apache.shardingsphere.database.protocol.packet.DatabasePacket;
-import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
-import org.apache.shardingsphere.infra.config.props.ConfigurationPropertyKey;
-import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
-import 
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
-import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
-import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
-import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
-import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
-import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
-import org.apache.shardingsphere.mode.manager.ContextManager;
-import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
-import org.apache.shardingsphere.parser.rule.SQLParserRule;
-import 
org.apache.shardingsphere.parser.rule.builder.DefaultSQLParserRuleConfigurationBuilder;
-import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
+import org.apache.shardingsphere.proxy.backend.response.data.QueryResponseRow;
 import 
org.apache.shardingsphere.proxy.backend.response.header.query.QueryHeader;
 import 
org.apache.shardingsphere.proxy.backend.response.header.query.QueryResponseHeader;
 import 
org.apache.shardingsphere.proxy.backend.response.header.update.MultiStatementsUpdateResponseHeader;
@@ -46,9 +32,6 @@ import 
org.apache.shardingsphere.proxy.backend.response.header.update.UpdateResp
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import org.apache.shardingsphere.proxy.frontend.command.executor.ResponseType;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
-import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
-import org.apache.shardingsphere.sqltranslator.rule.SQLTranslatorRule;
-import 
org.apache.shardingsphere.sqltranslator.rule.builder.DefaultSQLTranslatorRuleConfigurationBuilder;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
 import org.junit.jupiter.api.BeforeEach;
@@ -56,36 +39,29 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
-import org.mockito.internal.configuration.plugins.Plugins;
 import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.plugins.MemberAccessor;
 import org.mockito.quality.Strictness;
 
 import java.sql.SQLException;
-import java.sql.Types;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
-import java.util.Properties;
 
-import static org.hamcrest.Matchers.is;
 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.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(AutoMockExtension.class)
-@StaticMockSettings(ProxyContext.class)
+@StaticMockSettings(MySQLComQueryBackendHandlerFactory.class)
 @MockitoSettings(strictness = Strictness.LENIENT)
 class MySQLComQueryPacketExecutorTest {
     
-    private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "MySQL");
-    
     @Mock
     private ProxyBackendHandler proxyBackendHandler;
     
@@ -96,15 +72,15 @@ class MySQLComQueryPacketExecutorTest {
     private ConnectionSession connectionSession;
     
     @BeforeEach
-    void setUp() {
-        when(packet.getSQL()).thenReturn("");
+    void setUp() throws SQLException {
         
when(connectionSession.getAttributeMap().attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).get()).thenReturn(MySQLCharacterSets.UTF8MB4_GENERAL_CI);
+        when(MySQLComQueryBackendHandlerFactory.newInstance(packet, 
connectionSession)).thenReturn(proxyBackendHandler);
+        when(proxyBackendHandler.getRowData()).thenReturn(new 
QueryResponseRow(Collections.emptyList()));
     }
     
     @Test
-    void assertIsQueryResponse() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
+    void assertIsQueryResponse() throws SQLException {
         MySQLComQueryPacketExecutor mysqlComQueryPacketExecutor = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 mysqlComQueryPacketExecutor, proxyBackendHandler);
         QueryHeader queryHeader = mock(QueryHeader.class);
         when(queryHeader.getColumnTypeName()).thenReturn("VARCHAR");
         when(proxyBackendHandler.execute()).thenReturn(new 
QueryResponseHeader(Collections.singletonList(queryHeader)));
@@ -113,45 +89,18 @@ class MySQLComQueryPacketExecutorTest {
     }
     
     @Test
-    void assertIsUpdateResponse() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
-        MySQLComQueryPacketExecutor mysqlComQueryPacketExecutor = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 mysqlComQueryPacketExecutor, proxyBackendHandler);
-        when(proxyBackendHandler.execute()).thenReturn(new 
UpdateResponseHeader(mock(SQLStatement.class)));
-        mysqlComQueryPacketExecutor.execute();
-        assertThat(mysqlComQueryPacketExecutor.getResponseType(), 
is(ResponseType.UPDATE));
-    }
-    
-    @Test
-    void assertExecuteMultiUpdateStatements() throws SQLException, 
NoSuchFieldException, IllegalAccessException {
-        
when(connectionSession.getAttributeMap().hasAttr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(true);
-        
when(connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get()).thenReturn(0);
-        when(connectionSession.getCurrentDatabaseName()).thenReturn("foo_db");
-        when(packet.getSQL()).thenReturn("UPDATE t SET v=v+1 WHERE id=1;UPDATE 
t SET v=v+1 WHERE id=2;UPDATE t SET v=v+1 WHERE id=3");
-        ContextManager contextManager = mock(ContextManager.class);
-        MetaDataContexts metaDataContexts = mockMetaDataContexts();
-        
when(contextManager.getMetaDataContexts()).thenReturn(metaDataContexts);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+    void assertExecuteUpdateResponse() throws SQLException {
         MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
         when(proxyBackendHandler.execute()).thenReturn(new 
UpdateResponseHeader(mock(SQLStatement.class)));
         Collection<DatabasePacket> actualPackets = actual.execute();
+        assertThat(actual.getResponseType(), is(ResponseType.UPDATE));
         assertThat(actualPackets.size(), is(1));
         assertThat(actualPackets.iterator().next(), isA(MySQLOKPacket.class));
     }
     
     @Test
-    void assertExecuteMultiInsertOnDuplicateKeyStatements() throws 
SQLException, NoSuchFieldException, IllegalAccessException {
-        
when(connectionSession.getAttributeMap().hasAttr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(true);
-        
when(connectionSession.getAttributeMap().attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY).get()).thenReturn(0);
-        when(connectionSession.getCurrentDatabaseName()).thenReturn("foo_db");
-        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");
-        ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
-        MetaDataContexts metaDataContexts = mockMetaDataContexts();
-        
when(contextManager.getMetaDataContexts()).thenReturn(metaDataContexts);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+    void assertExecuteMultiStatementsUpdateResponse() throws SQLException {
         MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        MemberAccessor accessor = Plugins.getMemberAccessor();
-        
accessor.set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
         when(proxyBackendHandler.execute())
                 .thenReturn(new 
MultiStatementsUpdateResponseHeader(Arrays.asList(new 
UpdateResponseHeader(mock(SQLStatement.class)), new 
UpdateResponseHeader(mock(SQLStatement.class)))));
         Collection<DatabasePacket> actualPackets = actual.execute();
@@ -161,34 +110,9 @@ class MySQLComQueryPacketExecutorTest {
         assertThat(iterator.next(), isA(MySQLOKPacket.class));
     }
     
-    private MetaDataContexts mockMetaDataContexts() {
-        MetaDataContexts result = mock(MetaDataContexts.class, 
RETURNS_DEEP_STUBS);
-        ShardingSphereDatabase database = createDatabase();
-        when(result.getMetaData().getDatabase("foo_db")).thenReturn(database);
-        when(result.getMetaData().getDatabase(new 
IdentifierValue("foo_db"))).thenReturn(database);
-        RuleMetaData globalRuleMetaData = new RuleMetaData(
-                Arrays.asList(new SQLParserRule(new 
DefaultSQLParserRuleConfigurationBuilder().build()), new SQLTranslatorRule(new 
DefaultSQLTranslatorRuleConfigurationBuilder().build())));
-        
when(result.getMetaData().getGlobalRuleMetaData()).thenReturn(globalRuleMetaData);
-        Properties props = new Properties();
-        
props.setProperty(ConfigurationPropertyKey.KERNEL_EXECUTOR_SIZE.getKey(), "1");
-        when(result.getMetaData().getProps()).thenReturn(new 
ConfigurationProperties(props));
-        when(result.getMetaData().containsDatabase("foo_db")).thenReturn(true);
-        when(result.getMetaData().containsDatabase(new 
IdentifierValue("foo_db"))).thenReturn(true);
-        return result;
-    }
-    
-    private ShardingSphereDatabase createDatabase() {
-        ShardingSphereTable table = new ShardingSphereTable("t", 
Arrays.asList(new ShardingSphereColumn("id", Types.BIGINT, true, false, false, 
false, true, false),
-                new ShardingSphereColumn("v", Types.INTEGER, false, false, 
false, false, true, false)), Collections.emptyList(), Collections.emptyList());
-        ShardingSphereSchema schema = new ShardingSphereSchema("foo_db", 
databaseType, Collections.singleton(table), Collections.emptyList());
-        return new ShardingSphereDatabase("foo_db", databaseType, new 
ResourceMetaData(Collections.emptyMap()), new 
RuleMetaData(Collections.emptyList()), Collections.singleton(schema),
-                new ConfigurationProperties(new Properties()));
-    }
-    
     @Test
-    void assertNext() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
+    void assertNext() throws SQLException {
         MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
         when(proxyBackendHandler.next()).thenReturn(true, false);
         assertTrue(actual.next());
         assertFalse(actual.next());
@@ -200,9 +124,8 @@ class MySQLComQueryPacketExecutorTest {
     }
     
     @Test
-    void assertClose() throws SQLException, NoSuchFieldException, 
IllegalAccessException {
+    void assertClose() throws SQLException {
         MySQLComQueryPacketExecutor actual = new 
MySQLComQueryPacketExecutor(packet, connectionSession);
-        
Plugins.getMemberAccessor().set(MySQLComQueryPacketExecutor.class.getDeclaredField("proxyBackendHandler"),
 actual, proxyBackendHandler);
         actual.close();
         verify(proxyBackendHandler).close();
     }

Reply via email to