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

zhangliang 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 c9906e96b3c Throw exception when placeholders exceed 65535 in MySQL 
prepared statement (#29296)
c9906e96b3c is described below

commit c9906e96b3c4a0657a58b48f6b7d45c9f9a4aed4
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Tue Dec 5 20:25:42 2023 +0800

    Throw exception when placeholders exceed 65535 in MySQL prepared statement 
(#29296)
    
    * Throw exception when placeholders exceed 65535 in MySQL prepared statement
    
    * Update license
---
 .../exception/TooManyPlaceholdersException.java    | 28 ++++++++++++++++++++++
 .../mysql/mapper/MySQLDialectExceptionMapper.java  |  8 +++++--
 .../exception/mysql/vendor/MySQLVendorError.java   |  2 ++
 .../prepare/MySQLComStmtPrepareExecutor.java       |  5 ++++
 .../prepare/MySQLComStmtPrepareExecutorTest.java   | 23 ++++++++++++++++++
 5 files changed, 64 insertions(+), 2 deletions(-)

diff --git 
a/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/exception/TooManyPlaceholdersException.java
 
b/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/exception/TooManyPlaceholdersException.java
new file mode 100644
index 00000000000..5115a9901e7
--- /dev/null
+++ 
b/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/exception/TooManyPlaceholdersException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.infra.exception.mysql.exception;
+
+import 
org.apache.shardingsphere.infra.exception.dialect.exception.SQLDialectException;
+
+/**
+ * Too many placeholders exception.
+ */
+public final class TooManyPlaceholdersException extends SQLDialectException {
+    
+    private static final long serialVersionUID = -220866708278903418L;
+}
diff --git 
a/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/mapper/MySQLDialectExceptionMapper.java
 
b/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/mapper/MySQLDialectExceptionMapper.java
index ecd3b7132ce..0b61b515265 100644
--- 
a/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/mapper/MySQLDialectExceptionMapper.java
+++ 
b/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/mapper/MySQLDialectExceptionMapper.java
@@ -17,6 +17,8 @@
 
 package org.apache.shardingsphere.infra.exception.mysql.mapper;
 
+import 
org.apache.shardingsphere.infra.exception.core.external.sql.type.generic.UnknownSQLException;
+import 
org.apache.shardingsphere.infra.exception.core.external.sql.vendor.VendorError;
 import 
org.apache.shardingsphere.infra.exception.dialect.exception.SQLDialectException;
 import 
org.apache.shardingsphere.infra.exception.dialect.exception.connection.TooManyConnectionsException;
 import 
org.apache.shardingsphere.infra.exception.dialect.exception.data.InsertColumnsAndValuesMismatchedException;
@@ -38,9 +40,8 @@ import 
org.apache.shardingsphere.infra.exception.mysql.exception.UnknownCharsetE
 import 
org.apache.shardingsphere.infra.exception.mysql.exception.UnknownCollationException;
 import 
org.apache.shardingsphere.infra.exception.mysql.exception.UnknownSystemVariableException;
 import 
org.apache.shardingsphere.infra.exception.mysql.exception.UnsupportedPreparedStatementException;
+import 
org.apache.shardingsphere.infra.exception.mysql.exception.TooManyPlaceholdersException;
 import org.apache.shardingsphere.infra.exception.mysql.vendor.MySQLVendorError;
-import 
org.apache.shardingsphere.infra.exception.core.external.sql.type.generic.UnknownSQLException;
-import 
org.apache.shardingsphere.infra.exception.core.external.sql.vendor.VendorError;
 
 import java.sql.SQLException;
 
@@ -83,6 +84,9 @@ public final class MySQLDialectExceptionMapper implements 
SQLDialectExceptionMap
         if (sqlDialectException instanceof 
UnsupportedPreparedStatementException) {
             return toSQLException(MySQLVendorError.ER_UNSUPPORTED_PS);
         }
+        if (sqlDialectException instanceof TooManyPlaceholdersException) {
+            return toSQLException(MySQLVendorError.ER_PS_MANY_PARAM);
+        }
         if (sqlDialectException instanceof UnknownCharsetException) {
             return toSQLException(MySQLVendorError.ER_UNKNOWN_CHARACTER_SET, 
((UnknownCharsetException) sqlDialectException).getCharset());
         }
diff --git 
a/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/vendor/MySQLVendorError.java
 
b/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/vendor/MySQLVendorError.java
index ace2d4f5f9a..67a9489bc81 100644
--- 
a/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/vendor/MySQLVendorError.java
+++ 
b/infra/exception/dialect/type/mysql/src/main/java/org/apache/shardingsphere/infra/exception/mysql/vendor/MySQLVendorError.java
@@ -70,6 +70,8 @@ public enum MySQLVendorError implements VendorError {
     
     ER_UNKNOWN_COLLATION(XOpenSQLState.GENERAL_ERROR, 1273, "Unknown 
collation: '%s'"),
     
+    ER_PS_MANY_PARAM(XOpenSQLState.GENERAL_ERROR, 1390, "Prepared statement 
contains too many placeholders"),
+    
     ER_ERROR_ON_MODIFYING_GTID_EXECUTED_TABLE(XOpenSQLState.GENERAL_ERROR, 
3176,
             "Please do not modify the %s table with an XA transaction. This is 
an internal system table used to store GTIDs for committed transactions. "
                     + "Although modifying it can lead to an inconsistent GTID 
state, if necessary you can modify it with a non-XA transaction.");
diff --git 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutor.java
 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutor.java
index 8e63f755af3..ee174ed6857 100644
--- 
a/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutor.java
+++ 
b/proxy/frontend/type/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutor.java
@@ -35,6 +35,8 @@ import 
org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatem
 import org.apache.shardingsphere.infra.binder.engine.SQLBindEngine;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
+import 
org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
+import 
org.apache.shardingsphere.infra.exception.mysql.exception.TooManyPlaceholdersException;
 import 
org.apache.shardingsphere.infra.exception.mysql.exception.UnsupportedPreparedStatementException;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import 
org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
@@ -66,6 +68,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
 @RequiredArgsConstructor
 public final class MySQLComStmtPrepareExecutor implements CommandExecutor {
     
+    private static final int MAX_PARAMETER_COUNT = 65535;
+    
     private final MySQLComStmtPreparePacket packet;
     
     private final ConnectionSession connectionSession;
@@ -101,6 +105,7 @@ public final class MySQLComStmtPrepareExecutor implements 
CommandExecutor {
         Collection<DatabasePacket> result = new LinkedList<>();
         Collection<Projection> projections = 
getProjections(sqlStatementContext);
         int parameterCount = 
sqlStatementContext.getSqlStatement().getParameterCount();
+        ShardingSpherePreconditions.checkState(parameterCount <= 
MAX_PARAMETER_COUNT, TooManyPlaceholdersException::new);
         result.add(new MySQLComStmtPrepareOKPacket(statementId, 
projections.size(), parameterCount, 0));
         int characterSet = 
connectionSession.getAttributeMap().attr(MySQLConstants.MYSQL_CHARACTER_SET_ATTRIBUTE_KEY).get().getId();
         int statusFlags = 
ServerStatusFlagCalculator.calculateFor(connectionSession);
diff --git 
a/proxy/frontend/type/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutorTest.java
 
b/proxy/frontend/type/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutorTest.java
index 6b1a36ed78d..dd011c7bee0 100644
--- 
a/proxy/frontend/type/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutorTest.java
+++ 
b/proxy/frontend/type/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/binary/prepare/MySQLComStmtPrepareExecutorTest.java
@@ -32,6 +32,7 @@ import 
org.apache.shardingsphere.infra.binder.context.statement.dml.InsertStatem
 import 
org.apache.shardingsphere.infra.binder.context.statement.dml.SelectStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.dml.UpdateStatementContext;
 import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
+import 
org.apache.shardingsphere.infra.exception.mysql.exception.TooManyPlaceholdersException;
 import 
org.apache.shardingsphere.infra.exception.mysql.exception.UnsupportedPreparedStatementException;
 import org.apache.shardingsphere.infra.hint.HintValueContext;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
@@ -165,6 +166,28 @@ class MySQLComStmtPrepareExecutorTest {
         return byteBuf.getUnsignedShortLE(17);
     }
     
+    @Test
+    void assertPrepareInsertStatementWithTooManyPlaceholders() {
+        String sql = createTooManyPlaceholdersSQL();
+        when(packet.getSQL()).thenReturn(sql);
+        when(packet.getHintValueContext()).thenReturn(new HintValueContext());
+        int connectionId = 2;
+        when(connectionSession.getConnectionId()).thenReturn(connectionId);
+        when(connectionSession.getDefaultDatabaseName()).thenReturn("foo_db");
+        
MySQLStatementIdGenerator.getInstance().registerConnection(connectionId);
+        ContextManager contextManager = mockContextManager();
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        assertThrows(TooManyPlaceholdersException.class, () -> new 
MySQLComStmtPrepareExecutor(packet, connectionSession).execute());
+    }
+    
+    private String createTooManyPlaceholdersSQL() {
+        StringBuilder builder = new StringBuilder("INSERT INTO USER (ID, NAME, 
AGE) VALUES (?, ?, ?)");
+        for (int index = 0; index < Short.MAX_VALUE; index++) {
+            builder.append(", (?, ?, ?)");
+        }
+        return builder.toString();
+    }
+    
     @Test
     void assertPrepareUpdateStatement() {
         String sql = "update user set name = ?, age = ? where id = ?";

Reply via email to