Skip to content

Commit

Permalink
Throw exception when placeholders exceed 65535 in MySQL prepared stat…
Browse files Browse the repository at this point in the history
…ement (#29296)

* Throw exception when placeholders exceed 65535 in MySQL prepared statement

* Update license
  • Loading branch information
strongduanmu authored Dec 5, 2023
1 parent 5c9b9f4 commit c9906e9
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -38,9 +40,8 @@
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;

Expand Down Expand Up @@ -83,6 +84,9 @@ public SQLException convert(final SQLDialectException sqlDialectException) {
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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
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;
Expand Down Expand Up @@ -66,6 +68,8 @@
@RequiredArgsConstructor
public final class MySQLComStmtPrepareExecutor implements CommandExecutor {

private static final int MAX_PARAMETER_COUNT = 65535;

private final MySQLComStmtPreparePacket packet;

private final ConnectionSession connectionSession;
Expand Down Expand Up @@ -101,6 +105,7 @@ private Collection<DatabasePacket> createPackets(final SQLStatementContext sqlSt
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -165,6 +166,28 @@ private int getColumnDefinitionFlag(final MySQLColumnDefinition41Packet packet)
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 = ?";
Expand Down

0 comments on commit c9906e9

Please sign in to comment.