Skip to content

Commit

Permalink
CONJ-142: Using a semicolon in a string with "rewriteBatchedStatement…
Browse files Browse the repository at this point in the history
…s=true" fails

Added test case for CONJ-142 and made corrections to getInsertIncipit
in MySQLStatement

Also changed from splitting to looking for first question mark when
parsing JDBC url.
  • Loading branch information
Rasmus Johansson committed Apr 13, 2015
1 parent d4e01e2 commit 830e29c
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 15 deletions.
6 changes: 2 additions & 4 deletions src/main/java/org/mariadb/jdbc/JDBCUrl.java
Expand Up @@ -93,11 +93,9 @@ private static JDBCUrl parseConnectorJUrl(String url) {
//check if there are parameters
if (database.indexOf('?') > -1)
{
String[] halfs = database.split("\\?");
String[] credentials = database.substring(database.indexOf('?'), database.length()).split("&");

database = halfs[0];

String[] credentials = halfs[1].split("&");
database = database.substring(0, database.indexOf('?'));

for (int i = 0; i < credentials.length; i++)
{
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/mariadb/jdbc/MySQLConnection.java
Expand Up @@ -60,7 +60,7 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
import java.util.concurrent.Executor;


public final class MySQLConnection implements Connection {
public final class MySQLConnection implements Connection {
/**
* the protocol to communicate with.
*/
Expand Down Expand Up @@ -88,7 +88,7 @@ public final class MySQLConnection implements Connection {
*
* @param protocol the protocol to use.
*/
private MySQLConnection( MySQLProtocol protocol) {
private MySQLConnection(MySQLProtocol protocol) {
this.protocol = protocol;
clientInfoProperties = protocol.getInfo();
}
Expand Down
25 changes: 20 additions & 5 deletions src/main/java/org/mariadb/jdbc/MySQLStatement.java
Expand Up @@ -1134,13 +1134,28 @@ private void isInsertRewriteable(String sql) {
*/
protected int getInsertIncipit(String sql) {
String sqlUpper = sql.toUpperCase();
if (! sqlUpper.startsWith("INSERT")
|| sqlUpper.indexOf(";") != -1) {

if (! sqlUpper.startsWith("INSERT"))
return -1;
}

int idx = sqlUpper.indexOf(" VALUE");
int index = sqlUpper.indexOf("(", idx);
return index;
int startBracket = sqlUpper.indexOf("(", idx);
int endBracket = sqlUpper.indexOf(")", startBracket);

// Check for semicolons. Allow them inside the VALUES() brackets, otherwise return -1
// there can be multiple, so let's loop through them

int semicolonPos = sqlUpper.indexOf(';');

while (semicolonPos > -1)
{
if (semicolonPos < startBracket || semicolonPos > endBracket)
return -1;

semicolonPos = sqlUpper.indexOf(';', semicolonPos + 1);
}

return startBracket;
}

/**
Expand Down
12 changes: 11 additions & 1 deletion src/test/java/org/mariadb/jdbc/BaseTest.java
Expand Up @@ -29,11 +29,15 @@ public class BaseTest {
public static void beforeClassBaseTest() {
String url = System.getProperty("dbUrl", mDefUrl);
JDBCUrl jdbcUrl = JDBCUrl.parse(url);

hostname = jdbcUrl.getHostname();
port = jdbcUrl.getPort();
database = jdbcUrl.getDatabase();
username = jdbcUrl.getUsername();
password = jdbcUrl.getPassword();

logInfo("Properties parsed from JDBC URL - hostname: " + hostname + ", port: " + port + ", database: " + database + ", username: " + username + ", password: " + password);

if (database != null && "".equals(username)) {
String[] tokens = database.contains("?") ? database.split("\\?") : null;
if (tokens != null) {
Expand Down Expand Up @@ -109,7 +113,7 @@ protected void setConnection(Map<String, String> props) throws SQLException {
openConnection(connU, info);
}
protected void setConnection(Properties info) throws SQLException {
openConnection(connU, info);
openConnection(connURI, info);
}
protected void setConnection(String parameters) throws SQLException {
openConnection(connURI + parameters, null);
Expand Down Expand Up @@ -216,4 +220,10 @@ void requireMinimumVersion(int major, int minor) throws SQLException {
(dbMajor == major && dbMinor >= minor));

}

// common function for logging information
static void logInfo(String message)
{
System.out.println(message);
}
}
66 changes: 63 additions & 3 deletions src/test/java/org/mariadb/jdbc/MultiTest.java
Expand Up @@ -5,7 +5,9 @@
import org.junit.Test;

import java.sql.*;
import java.util.Properties;

import junit.framework.Assert;
import static org.junit.Assert.*;


Expand All @@ -24,10 +26,13 @@ public static void beforeClassMultiTest() throws SQLException {
Statement st = connection.createStatement();
st.executeUpdate("drop table if exists t1");
st.executeUpdate("drop table if exists t2");
st.executeUpdate("create table t2(id int, test varchar(100))");
st.executeUpdate("drop table if exists t3");
st.executeUpdate("create table t1(id int, test varchar(100))");
st.executeUpdate("create table t2(id int, test varchar(100))");
st.executeUpdate("create table t3(message text)");
st.execute("insert into t1 values(1,'a'),(2,'a')");
st.execute("insert into t2 values(1,'a'),(2,'a')");
st.execute("insert into t3 values('hello')");
}

@AfterClass
Expand All @@ -36,7 +41,7 @@ public static void afterClass() throws SQLException {
Statement st = connection.createStatement();
st.executeUpdate("drop table if exists t1");
st.executeUpdate("drop table if exists t2");

st.executeUpdate("drop table if exists t3");
} catch (Exception e) {
// eat
}
Expand Down Expand Up @@ -151,7 +156,9 @@ public void setMaxRowsMulti() throws Exception {
@Test
public void rewriteBatchedStatementsInsertTest() throws SQLException {
// set the rewrite batch statements parameter
setConnection("&rewriteBatchedStatements=true");
Properties props = new Properties();
props.setProperty("rewriteBatchedStatements", "true");
connection.setClientInfo(props);

int cycles = 3000;
PreparedStatement preparedStatement = prepareStatementBatch(cycles);
Expand All @@ -176,6 +183,54 @@ public void rewriteBatchedStatementsInsertTest() throws SQLException {
assertEquals(cycles, totalUpdates);
}

/**
* CONJ-142: Using a semicolon in a string with "rewriteBatchedStatements=true" fails
* @throws SQLException
*/
@Test
public void rewriteBatchedStatementsSemicolon() throws SQLException {
// set the rewrite batch statements parameter
Properties props = new Properties();
props.setProperty("rewriteBatchedStatements", "true");
setConnection(props);

connection.createStatement().execute("TRUNCATE t3");

PreparedStatement sqlInsert = connection.prepareStatement("INSERT INTO t3 (message) VALUES (?)");
sqlInsert.setString(1, "aa");
sqlInsert.addBatch();
sqlInsert.setString(1, "b;b");
sqlInsert.addBatch();
sqlInsert.setString(1, ";ccccccc");
sqlInsert.addBatch();
sqlInsert.setString(1, "ddddddddddddddd;");
sqlInsert.addBatch();
sqlInsert.setString(1, ";eeeeeee;;eeeeeeeeee;eeeeeeeeee;");
sqlInsert.addBatch();
int[] updateCounts = sqlInsert.executeBatch();

// rewrite should be ok, so the above should be executed in 1 command updating 5 rows
Assert.assertEquals(1, updateCounts.length);
Assert.assertEquals(5, updateCounts[0]);

connection.commit();

// Test for multiple statements which isn't allowed. rewrite shouldn't work
sqlInsert = connection.prepareStatement("INSERT INTO t3 (message) VALUES (?); INSERT INTO t3 (message) VALUES ('multiple')");
sqlInsert.setString(1, "aa");
sqlInsert.addBatch();
sqlInsert.setString(1, "b;b");
sqlInsert.addBatch();
updateCounts = sqlInsert.executeBatch();

// rewrite should NOT be possible. Therefore there should be 2 commands updating 1 row each.
Assert.assertEquals(2, updateCounts.length);
Assert.assertEquals(1, updateCounts[0]);
Assert.assertEquals(1, updateCounts[1]);

connection.commit();
}

private PreparedStatement prepareStatementBatch(int size) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO t1 VALUES (?, ?)");
for (int i = 0; i < size; i++) {
Expand All @@ -192,6 +247,11 @@ private PreparedStatement prepareStatementBatch(int size) throws SQLException {
*/
@Test
public void rewriteBatchedStatementsUpdateTest() throws SQLException {
// set the rewrite batch statements parameter
Properties props = new Properties();
props.setProperty("rewriteBatchedStatements", "true");
connection.setClientInfo(props);

connection.createStatement().execute("TRUNCATE t1");
int cycles = 1000;
prepareStatementBatch(cycles).executeBatch(); // populate the table
Expand Down

0 comments on commit 830e29c

Please sign in to comment.