Uploaded image for project: 'MariaDB Connector/J'
  1. MariaDB Connector/J
  2. CONJ-168

Rewritten batch insert can fail if the first value isn't a parameter

    XMLWordPrintable

Details

    • Bug
    • Status: Closed (View Workflow)
    • Major
    • Resolution: Fixed
    • 1.1.9
    • 1.2.2
    • Other
    • None
    • Sprint connector/j 1.3.0

    Description

      When performing a batch insert with the rewriteBatchedStatements=true option, the insert can fail if the first value being inserted isn't a parameter. The following test case demonstrates the problem:

      @Test
      public void testInsertWithLeadingConstantValue() throws Exception {
          Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true", "root", "root");
          Statement stmt = null;
          PreparedStatement insertStmt = null;
          try {
              stmt = conn.createStatement();
              stmt.executeUpdate("CREATE TABLE IF NOT EXISTS test_table(col1 VARCHAR(32), col2 VARCHAR(32))");
              
              insertStmt = conn.prepareStatement("INSERT INTO test_table (col1, col2) values('some value', ?)");
              insertStmt.setString(1, "a");
              insertStmt.addBatch();
              insertStmt.setString(1, "b");
              insertStmt.addBatch();
              insertStmt.executeBatch();
              conn.commit();
          }
          finally {
              if (insertStmt != null) {
                  insertStmt.close();
              }
              if (stmt != null) {
                  stmt.close();
              }
              conn.close();
          }
      }

      The insert attempt results in the following SQL being executed:

      INSERT INTO test_table (col1, col2) values('some value', 'a'),('b')

      The SQL fails with the following error:

      java.sql.BatchUpdateException: Column count doesn't match value count at row 2
      	at org.mariadb.jdbc.MySQLStatement.executeBatch(MySQLStatement.java:1291)
      	at org.mariadb.jdbc.TestBatchInsert.testInsertWithLeadingConstantValue(TestBatchInsert.java:24)
      	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      	at java.lang.reflect.Method.invoke(Method.java:606)
      	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
      	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
      	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
      	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
      	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
      	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
      	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
      	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
      	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
      	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
      	at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
      	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
      	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
      	at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
      	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
      	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
      	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
      Caused by: java.sql.SQLException: Column count doesn't match value count at row 2
      	at org.mariadb.jdbc.internal.SQLExceptionMapper.get(SQLExceptionMapper.java:149)
      	at org.mariadb.jdbc.internal.SQLExceptionMapper.throwException(SQLExceptionMapper.java:106)
      	at org.mariadb.jdbc.MySQLStatement.executeQueryEpilog(MySQLStatement.java:268)
      	at org.mariadb.jdbc.MySQLStatement.execute(MySQLStatement.java:335)
      	at org.mariadb.jdbc.MySQLStatement.executeBatch(MySQLStatement.java:1271)
      	... 25 more
      Caused by: org.mariadb.jdbc.internal.common.QueryException: Column count doesn't match value count at row 2
      	at org.mariadb.jdbc.internal.mysql.MySQLProtocol.getResult(MySQLProtocol.java:995)
      	at org.mariadb.jdbc.internal.mysql.MySQLProtocol.executeQuery(MySQLProtocol.java:1050)
      	at org.mariadb.jdbc.MySQLStatement.execute(MySQLStatement.java:328)
      	... 26 more

      This seems to be because the initial part of the values clause up to the first parameter isn't being included in the rewritten SQL. The following patch addresses this issue:

      diff --git a/src/main/java/org/mariadb/jdbc/internal/common/query/MySQLParameterizedQuery.java b/src/main/java/org/mariadb/jdbc/internal/common/query/MySQLParameterizedQuery.java
      index 364a185..8f7dd5a 100644
      --- a/src/main/java/org/mariadb/jdbc/internal/common/query/MySQLParameterizedQuery.java
      +++ b/src/main/java/org/mariadb/jdbc/internal/common/query/MySQLParameterizedQuery.java
      @@ -139,6 +139,9 @@
                   throw new AssertionError("Invalid query, queryParts was empty");
               }
               os.write(",(".getBytes());
      +        if (queryPartsArray[0].length > rewriteOffset) {
      +        	os.write(queryPartsArray[0], rewriteOffset + 1, queryPartsArray[0].length - rewriteOffset - 1);
      +        }
               for(int i = 1; i<queryPartsArray.length; i++) {
                   parameters[i-1].writeTo(os);
                   if(queryPartsArray[i].length != 0)

      Attachments

        Activity

          People

            diego dupin Diego Dupin
            bbehan Brendan Behan
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.