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 = ?";