You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2023/12/05 12:25:51 UTC
(shardingsphere) branch master updated: Throw exception when placeholders exceed 65535 in MySQL prepared statement (#29296)
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 <du...@apache.org>
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 = ?";