Bug #46788 While using ON DUPLICATE KEY UPDATE on a batchRewriteStatements I get exception

Submitted: 18 Aug 2009 14:58 Modified: 16 Sep 2009 13:42
Reporter: Dan Dumitriu Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / J Severity:S2 (Serious)
Version:5.1.8 OS:Linux (Fedora 10)
Assigned to: Mark Matthews CPU Architecture:Any
Tags: exception, ON DUPLICATE KEY UPDATE, Parameter index out of range

[18 Aug 2009 14:58] Dan Dumitriu
Description:
I am using the ON DUPLICATE KEY UPDATE functionality together with the option of rewriteBatchedStatements set to true. when I try to execute the prepared statement I get the following exception:

the prepared statement looks like this:

insert into config_table (modified,id_) values (?,?) ON DUPLICATE KEY UPDATE modified=?

java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).
	at com.sag.etl.job.processors.JdbcInsertProcessor.flush(JdbcInsertProcessor.java:135)
......
Caused by: java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
	at com.mysql.jdbc.PreparedStatement.checkBounds(PreparedStatement.java:3657)
	at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:3641)
	at com.mysql.jdbc.PreparedStatement.setBytesNoEscapeNoQuotes(PreparedStatement.java:3391)
	at com.mysql.jdbc.PreparedStatement.setOneBatchedParameterSet(PreparedStatement.java:4203)
	at com.mysql.jdbc.PreparedStatement.executeBatchedInserts(PreparedStatement.java:1759)
	at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1441)
	at com.sag.etl.job.processors.JdbcInsertProcessor.flush(JdbcInsertProcessor.java:131)
	... 16 more

problem does not appear if I use the 5.1.7 connector or any connector which is older.
this seems like a newly introduced bug.

How to repeat:
create a table having a primary key id, and some value e.g. name.

thePreparedStmt = theConnection.prepareStatement("insert into config_table (modified,id_) values (?,?) ON DUPLICATE KEY UPDATE modified=?");

thePreparedStmt.setString(1, "theID");
thePreparedStmt.setString(2, "Hello_world_");
thePreparedStmt.setString(3, "Hello_world_");

when you create the connection set the properties:

        Properties myProperties = new Properties();
        myProperties.put("user", theUser);
        myProperties.put("password", thePassword);
        myProperties.put("cacheServerConfiguration", "true");
        
        myProperties.put("useLocalSessionState", "true");
        myProperties.put("elideSetAutoCommits", "true");
        myProperties.put("alwaysSendSetIsolation", "true");
        
        myProperties.put("rewriteBatchedStatements", "true");
        
        myProperties.put("cachePrepStmts", "true");
        myProperties.put("prepStmtCacheSize", "512");
        
        myProperties.put("maintainTimeStats", "false");
        myProperties.put("cacheResultSetMetadata", "true");
        
        myProperties.put("maintainTimeStats", "false");
        myProperties.put("cacheResultSetMetadata", "true");
        
        myProperties.put("useUnbufferedIO", "false");
        myProperties.put("useReadAheadInput", "false");

execute the insert statement:

thePreparedStmt.executeBatch();

you will get the above exception. please elt me know if you need more details on reproducing it

Suggested fix:
check how you rewrite the statement, I guess you rewrite it up where the ON DUPLICATE KEY UPDATE part of the statement begins....
I have no better suggestion
[18 Aug 2009 15:08] Mark Matthews
Versions prior to 5.1.8 wouldn't attempt to re-write a query of this form as a multi-value INSERT. 5.1.8 will attempt to re-write this query as a multi-value INSERT.

It seems we'll need a check that there are no parameters after the ON DUPLICATE KEY UPDATE clause, because it's not possible to re-write the query into a multi-value INSERT. Instead it should be sent as a multi-query (as it was prior to 5.1.8).
[18 Aug 2009 16:01] Dan Dumitriu
Hi,
Actually I was hopping I will get this fix and still aim for some performance improvement. As I found in a presentation (ConnectorJ Performance) by someone called Mark Matthews ;)

A Rewritten INSERT
 INSERT INTO foo VALUES (...),(...),(...) up to
max_allowed_packet
 Starting in 5.1.8, ON DUPLICATE KEY UPDATE
rewritten into multi-value as well!
 except for case where you use LAST_INSERT_ID()

I do not know if that is technically possible or not.
Dan
[18 Aug 2009 19:01] Mark Matthews
The issue is that the query can not be re-written to a form like:

INSERT INTO foo VALUES (...), (...), (...) ON DUPLICATE KEY UPDATE ... because after the update the query has parameters. There is no form of multi-value INSERT that works for that case. The entire batch could be re-written to a form like:

INSERT INTO foo VALUES (...) ON DUPLICATE KEY UPDATE bar=1; INSERT INTO foo VALUES (...) ON DUPLICATE KEY UPDATE bar=2; 

Which is what the fix will have to be.

You might consider if the query can be re-written to not require a parameter in the UPDATE clause, i.e. using the VALUES function, see http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
[26 Aug 2009 14:50] Mark Matthews
Fixed for 5.1.9.
[16 Sep 2009 13:42] Tony Bedford
An entry was added to the 5.1.9 changelog:

When using the ON DUPLICATE KEY UPDATE functionality together with the rewriteBatchedStatements option set to true, an exception was generated when trying to execute the prepared statement:

INSERT INTO config_table (modified,id_) VALUES (?,?) ON DUPLICATE KEY UPDATE modified=?

The exception generated was:

java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is
2).
	at com.sag.etl.job.processors.JdbcInsertProcessor.flush(JdbcInsertProcessor.java:135)
......
Caused by: java.sql.SQLException: Parameter index out of range (3 > number of parameters,
which is 2).
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
	at com.mysql.jdbc.PreparedStatement.checkBounds(PreparedStatement.java:3657)
	at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:3641)
	at
com.mysql.jdbc.PreparedStatement.setBytesNoEscapeNoQuotes(PreparedStatement.java:3391)
	at
com.mysql.jdbc.PreparedStatement.setOneBatchedParameterSet(PreparedStatement.java:4203)
	at com.mysql.jdbc.PreparedStatement.executeBatchedInserts(PreparedStatement.java:1759)
	at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1441)
	at com.sag.etl.job.processors.JdbcInsertProcessor.flush(JdbcInsertProcessor.java:131)
	... 16 more

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.054 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.