Skip to content

Commit 3207854

Browse files
committed
Fix for Bug#77183 (21181501), INSERT..VALUE..lead to invalidation of batch insert.
1 parent e824d25 commit 3207854

File tree

4 files changed

+74
-7
lines changed

4 files changed

+74
-7
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
Version 5.1.49
55

6+
- Fix for Bug#77183 (21181501), INSERT..VALUE..lead to invalidation of batch insert.
7+
68
- Fix for Bug#23143279, CLIENT HANG WHEN LOADBALANCESTRATEGY IS BESTRESPONSETIME.
79

810
- WL#14007, Remove third-party libraries from sources and bundles.

src/com/mysql/jdbc/LocalizedErrorMessages.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ ConnectionProperties.replicationConnectionGroup=Logical group of replication con
461461
ConnectionProperties.allowMasterDownConnections=By default, a replication-aware connection will fail to connect when configured master hosts are all unavailable at initial connection. Setting this property to 'true' allows to establish the initial connection, by failing over to the slave servers, in read-only state. It won't prevent subsequent failures when switching back to the master hosts i.e. by setting the replication connection to read/write state.
462462
ConnectionProperties.allowSlaveDownConnections=By default, a replication-aware connection will fail to connect when configured slave hosts are all unavailable at initial connection. Setting this property to 'true' allows to establish the initial connection. It won't prevent failures when switching to slaves i.e. by setting the replication connection to read-only state. The property 'readFromMasterWhenNoSlaves' should be used for this purpose.
463463
ConnectionProperties.readFromMasterWhenNoSlaves=Replication-aware connections distribute load by using the master hosts when in read/write state and by using the slave hosts when in read-only state. If, when setting the connection to read-only state, none of the slave hosts are available, an SQLExeception is thrown back. Setting this property to 'true' allows to fail over to the master hosts, while setting the connection state to read-only, when no slave hosts are available at switch instant.
464-
ConnectionProperties.allowMultiQueries=Allow the use of ';' to delimit multiple queries during one statement (true/false), defaults to 'false', and does not affect the addBatch() and executeBatch() methods, which instead rely on rewriteBatchStatements.
464+
ConnectionProperties.allowMultiQueries=Allow the use of ';' to delimit multiple queries during one statement (true/false), defaults to 'false', and does not affect the addBatch() and executeBatch() methods, which instead rely on rewriteBatchedStatements.
465465
ConnectionProperties.allowNANandINF=Should the driver allow NaN or +/- INF values in PreparedStatement.setDouble()?
466466
ConnectionProperties.allowUrlInLoadLocal=Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements?
467467
ConnectionProperties.alwaysSendSetIsolation=Should the driver always communicate with the database when Connection.setTransactionIsolation() is called? If set to false, the driver will only communicate with the database when the requested transaction isolation is different than the whichever is newer, the last value that was set via Connection.setTransactionIsolation(), or the value that was read from the server when the connection was established. Note that useLocalSessionState=true will force the same behavior as alwaysSendSetIsolation=false, regardless of how alwaysSendSetIsolation is set.
@@ -569,7 +569,7 @@ ConnectionProperties.resourceId=A globally unique name that identifies the resou
569569
ConnectionProperties.resultSetSizeThreshold=If 'useUsageAdvisor' is true, how many rows should a result set contain before the driver warns that it is suspiciously large?
570570
ConnectionProperties.retainStatementAfterResultSetClose=Should the driver retain the Statement reference in a ResultSet after ResultSet.close() has been called. This is not JDBC-compliant after JDBC-4.0.
571571
ConnectionProperties.retriesAllDown=When using loadbalancing or failover, the number of times the driver should cycle through available hosts, attempting to connect. Between cycles, the driver will pause for 250ms if no servers are available.
572-
ConnectionProperties.rewriteBatchedStatements=Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream(), the driver won't be able to determine the optimum number of parameters per batch and you might receive an error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements. Please be aware using rewriteBatchedStatements=true with INSERT .. ON DUPLICATE KEY UPDATE that for rewritten statement server returns only one value as sum of all affected (or found) rows in batch and it isn't possible to map it correctly to initial statements; in this case driver returns 0 as a result of each batch statement if total count was 0, and the Statement.SUCCESS_NO_INFO as a result of each batch statement if total count was > 0.
572+
ConnectionProperties.rewriteBatchedStatements=Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, if you don't specify stream lengths when using PreparedStatement.set*Stream(), the driver won't be able to determine the optimum number of parameters per batch and you might receive an error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements. Please be aware using rewriteBatchedStatements=true with INSERT .. ON DUPLICATE KEY UPDATE that for rewritten statement server returns only one value as sum of all affected (or found) rows in batch and it isn't possible to map it correctly to initial statements; in this case driver returns 0 as a result of each batch statement if total count was 0, and the Statement.SUCCESS_NO_INFO as a result of each batch statement if total count was > 0.
573573
ConnectionProperties.rollbackOnPooledClose=Should the driver issue a rollback() when the logical connection in a pool is closed?
574574
ConnectionProperties.roundRobinLoadBalance=When autoReconnect is enabled, and failoverReadonly is false, should we pick hosts to connect to on a round-robin basis?
575575
ConnectionProperties.runningCTS13=Enables workarounds for bugs in Sun's JDBC compliance testsuite version 1.3

src/com/mysql/jdbc/PreparedStatement.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -409,22 +409,23 @@ private String extractValuesClause(String sql, String quoteCharStr) throws SQLEx
409409
int indexOfValues = -1;
410410
int valuesSearchStart = this.statementStartPos;
411411

412+
// VALUE is a synonym for VALUES
412413
while (indexOfValues == -1) {
413414
if (quoteCharStr.length() > 0) {
414-
indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUES", quoteCharStr, quoteCharStr,
415+
indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUE", quoteCharStr, quoteCharStr,
415416
StringUtils.SEARCH_MODE__MRK_COM_WS);
416417
} else {
417-
indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUES");
418+
indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, sql, "VALUE");
418419
}
419420

420421
if (indexOfValues > 0) {
421-
/* check if the char immediately preceding VALUES may be part of the table name */
422+
/* check if the char immediately preceding VALUE[S] may be part of the table name */
422423
char c = sql.charAt(indexOfValues - 1);
423424
if (!(Character.isWhitespace(c) || c == ')' || c == '`')) {
424425
valuesSearchStart = indexOfValues + 6;
425426
indexOfValues = -1;
426427
} else {
427-
/* check if the char immediately following VALUES may be whitespace or open parenthesis */
428+
/* check if the char immediately following VALUE[S] may be whitespace or open parenthesis */
428429
c = sql.charAt(indexOfValues + 6);
429430
if (!(Character.isWhitespace(c) || c == '(')) {
430431
valuesSearchStart = indexOfValues + 6;
@@ -440,7 +441,9 @@ private String extractValuesClause(String sql, String quoteCharStr) throws SQLEx
440441
return null;
441442
}
442443

443-
int indexOfFirstParen = sql.indexOf('(', indexOfValues + 6);
444+
// VALUE vs VALUES length
445+
int valLength = sql.length() > indexOfValues + 5 && Character.toUpperCase(sql.charAt(indexOfValues + 5)) == 'S' ? 6 : 5;
446+
int indexOfFirstParen = sql.indexOf('(', indexOfValues + valLength);
444447

445448
if (indexOfFirstParen == -1) {
446449
return null;

src/testsuite/regression/StatementRegressionTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8917,6 +8917,68 @@ public Void call() throws Exception {
89178917
con.close();
89188918
}
89198919
} while (useSPS = !useSPS);
8920+
}
8921+
8922+
/**
8923+
* Test fix for Bug#77183 (21181501), INSERT..VALUE..lead to invalidation of batch insert.
8924+
*
8925+
* @throws Exception
8926+
*/
8927+
public void testBug77183() throws Exception {
8928+
createTable("testBug77183", "(c1 INT, c2 INT)");
8929+
8930+
boolean useSPS = false;
8931+
boolean rwBS = false;
8932+
boolean useRep = false;
8933+
boolean useValue = false;
8934+
8935+
do {
8936+
Properties props = new Properties();
8937+
props.setProperty("useServerPrepStmts", Boolean.toString(useSPS));
8938+
props.setProperty("rewriteBatchedStatements", Boolean.toString(rwBS));
8939+
props.setProperty("statementInterceptors", TestBug77183StatementInterceptor.class.getName());
8940+
8941+
Connection testConn = getConnectionWithProps(props);
8942+
8943+
this.pstmt = testConn.prepareStatement((useRep ? "REPLACE" : "INSERT") + " INTO testBug77183 " + (useValue ? "VALUE" : "VALUES") + " (?, ?)");
8944+
for (int i = 1; i <= 3; i++) {
8945+
this.pstmt.setInt(1, i);
8946+
this.pstmt.setInt(2, i);
8947+
this.pstmt.addBatch();
8948+
}
8949+
this.pstmt.executeBatch();
8950+
8951+
testConn.close();
8952+
} while ((useSPS = !useSPS) || (rwBS = !rwBS) || (useRep = !useRep) || (useValue = !useValue));
8953+
8954+
assertEquals(32, TestBug77183StatementInterceptor.countInterceptions);
8955+
}
8956+
8957+
public static class TestBug77183StatementInterceptor extends BaseStatementInterceptor {
8958+
public static int countInterceptions = 0;
8959+
8960+
@Override
8961+
public ResultSetInternalMethods preProcess(String sql, com.mysql.jdbc.Statement interceptedStatement, com.mysql.jdbc.Connection connection)
8962+
throws SQLException {
8963+
if (sql == null) {
8964+
sql = "";
8965+
}
8966+
if (sql.length() == 0 && interceptedStatement instanceof com.mysql.jdbc.PreparedStatement) {
8967+
sql = ((com.mysql.jdbc.PreparedStatement) interceptedStatement).asSql();
8968+
}
8969+
if (interceptedStatement instanceof PreparedStatement) {
8970+
countInterceptions++;
89208971

8972+
final boolean useSPS = connection.getUseServerPreparedStmts();
8973+
final boolean rwBS = connection.getRewriteBatchedStatements();
8974+
final String testCase = String.format("Case [SPS: %s, RwBS: %s, Query: %s]", useSPS ? "Y" : "N", rwBS ? "Y" : "N", sql);
8975+
final int numParamSets = sql.length() - sql.replace("(", "").length();
8976+
final int numPlaceholders = sql.length() - sql.replace("?", "").length();
8977+
8978+
assertEquals(testCase, rwBS ? 3 : 1, numParamSets);
8979+
assertEquals(testCase, useSPS ? rwBS ? 6 : 2 : 0, numPlaceholders);
8980+
}
8981+
return super.preProcess(sql, interceptedStatement, connection);
8982+
}
89218983
}
89228984
}

0 commit comments

Comments
 (0)