From: Date: January 4 2007 7:22pm Subject: Connector/J commit: r6254 - in branches/branch_5_0/connector-j: . src/com/mysql/jdbc src/testsuite/regression List-Archive: http://lists.mysql.com/commits/17637 X-Bug: 25025 Message-Id: <200701041822.l04IMgH2020633@bk-internal.mysql.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modified: branches/branch_5_0/connector-j/CHANGES branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java Log: Fixed BUG#25025 - Client-side prepared statement parser gets confused by in-line (/* ... */) comments and therefore can't rewrite batched statements or reliably detect type of statements when they're used. Modified: branches/branch_5_0/connector-j/CHANGES =================================================================== --- branches/branch_5_0/connector-j/CHANGES 2007-01-04 01:03:01 UTC (rev 6253) +++ branches/branch_5_0/connector-j/CHANGES 2007-01-04 18:22:41 UTC (rev 6254) @@ -39,7 +39,11 @@ INFORMATION_SCHEMA didn't have references to current connections, sometimes leading to NullPointerExceptions when intropsecting them via ResultSetMetaData. - + + - Fixed BUG#25025 - Client-side prepared statement parser gets confused by + in-line (/* ... */) comments and therefore can't rewrite batched statements + or reliably detect type of statements when they're used. + 10-20-06 - Version 5.0.4 - Fixed BUG#21379 - column names don't match metadata in cases Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java =================================================================== --- branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java 2007-01-04 01:03:01 UTC (rev 6253) +++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java 2007-01-04 18:22:41 UTC (rev 6254) @@ -135,6 +135,8 @@ long lastUsed = 0; int statementLength = 0; + + int statementStartPos = 0; byte[][] staticSql = null; @@ -177,7 +179,22 @@ boolean noBackslashEscapes = connection.isNoBackslashEscapesSet(); - for (i = 0; i < this.statementLength; ++i) { + // we're not trying to be real pedantic here, but we'd like to + // skip comments at the beginning of statements, as frameworks + // such as Hibernate use them to aid in debugging + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) { + statementStartPos = sql.indexOf("*/"); + + if (statementStartPos == -1) { + statementStartPos = 0; + } else { + statementStartPos += 2; + } + } + + + for (i = statementStartPos; i < this.statementLength; ++i) { char c = sql.charAt(i); if ((this.firstStmtChar == 0) && !Character.isWhitespace(c)) { @@ -401,7 +418,22 @@ private String batchedValuesClause; + /** Where does the statement text actually start? */ + + private int statementAfterCommentsPos; + /** + * have we checked whether we can rewrite this statement as a multi-value + * insert? + */ + + private boolean hasCheckedForRewrite = false; + + /** Can we actually rewrite this statement as a multi-value insert? */ + + private boolean canRewrite = false; + + /** * Constructor used by server-side prepared statements * * @param conn @@ -844,8 +876,9 @@ if (!this.batchHasPlainStatements && this.connection.getRewriteBatchedStatements()) { - if (StringUtils.startsWithIgnoreCaseAndWs(this.originalSql, - "INSERT")) { + + + if (canRewriteAsMultivalueInsertStatement()) { return executeBatchedInserts(); } } @@ -857,6 +890,16 @@ } } + public synchronized boolean canRewriteAsMultivalueInsertStatement() { + if (!this.hasCheckedForRewrite) { + this.canRewrite = StringUtils.startsWithIgnoreCaseAndWs( + this.originalSql, "INSERT", this.statementAfterCommentsPos); + this.hasCheckedForRewrite = true; + } + + return this.canRewrite; + } + /** * Rewrites the already prepared statement into a multi-value insert * statement of 'statementsPerBatch' values and executes the entire batch @@ -1450,10 +1493,12 @@ int indexOfValues = -1; if (quoteCharStr.length() > 0) { - indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(0, + indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes( + this.statementAfterCommentsPos, this.originalSql, "VALUES ", quoteCharStr.charAt(0), false); } else { - indexOfValues = StringUtils.indexOfIgnoreCase(0, this.originalSql, + indexOfValues = StringUtils.indexOfIgnoreCase(this.statementAfterCommentsPos, + this.originalSql, "VALUES "); } @@ -1932,6 +1977,8 @@ for (int j = 0; j < this.parameterCount; j++) { this.isStream[j] = false; } + + this.statementAfterCommentsPos = this.parseInfo.statementStartPos; } boolean isNull(int paramIndex) { Modified: branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java =================================================================== --- branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java 2007-01-04 01:03:01 UTC (rev 6253) +++ branches/branch_5_0/connector-j/src/testsuite/regression/StatementRegressionTest.java 2007-01-04 18:22:41 UTC (rev 6254) @@ -3689,4 +3689,49 @@ assertEquals(beforeOpenStatementCount, afterOpenStatementCount); } + + /** + * Tests fix for BUG#25025 - Client-side prepared statement parser gets confused by + * in-line (slash-star) comments and therefore can't rewrite batched statements or + * reliably detect type of statements when they're used. + * + * @throws Exception if the test fails. + */ + public void testBug25025() throws Exception { + + Connection multiConn = null; + + createTable("testBug25025", "(field1 INT)"); + + try { + Properties props = new Properties(); + props.setProperty("rewriteBatchedStatements", "true"); + props.setProperty("useServerPrepStmts", "false"); + + multiConn = getConnectionWithProps(props); + + this.pstmt = multiConn.prepareStatement("/* insert foo.bar.baz INSERT INTO foo VALUES (?,?,?,?) to trick parser */ INSERT into testBug25025 VALUES (?)"); + this.pstmt.setInt(1, 1); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 2); + this.pstmt.addBatch(); + this.pstmt.setInt(1, 3); + this.pstmt.addBatch(); + + int[] counts = this.pstmt.executeBatch(); + + assertEquals(3, counts.length); + assertEquals(1, counts[0]); + assertEquals(1, counts[1]); + assertEquals(1, counts[2]); + assertEquals(true, + ((com.mysql.jdbc.PreparedStatement)this.pstmt).canRewriteAsMultivalueInsertStatement()); + } finally { + closeMemberJDBCResources(); + + if (multiConn != null) { + multiConn.close(); + } + } + } }