package org.mariadb.jdbc;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.*;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.*;
public class ExecuteBatchTest extends BaseTest {
static String oneHundredLengthString = "";
static boolean profileSql = true;
static {
char[] chars = new char[100];
for (int i = 27; i < 127; i++) chars[i - 27] = (char) i;
oneHundredLengthString = new String(chars);
}
/**
* Create test tables.
*
* @throws SQLException if connection error occur
*/
@BeforeClass()
public static void initClass() throws SQLException {
createTable("ExecuteBatchTest", "id int not null primary key auto_increment, test varchar(100) , test2 int");
createTable("ExecuteBatchUseBatchMultiSend", "test varchar(100)");
}
/**
* CONJ-426: Test that executeBatch can be properly interrupted.
*
* @throws Exception If the test fails
*/
@Test
public void interruptExecuteBatch() throws Exception {
Assume.assumeTrue(sharedOptions().useBatchMultiSend);
ExecutorService service = Executors.newFixedThreadPool(1);
final CyclicBarrier barrier = new CyclicBarrier(2);
final AtomicBoolean wasInterrupted = new AtomicBoolean(false);
final AtomicReference<Exception> exceptionRef = new AtomicReference<>();
service.submit(new Runnable() {
@Override
public void run() {
try {
PreparedStatement preparedStatement = sharedConnection.prepareStatement(
"INSERT INTO ExecuteBatchTest(test, test2) values (?, ?)");
// Send a large enough batch that will take long enough to allow us to interrupt it
for (int i = 0; i < 1_000_000; i++) {
preparedStatement.setString(1, String.valueOf(System.nanoTime()));
preparedStatement.setInt(2, i);
preparedStatement.addBatch();
}
barrier.await();
preparedStatement.executeBatch();
} catch (InterruptedException ex) {
exceptionRef.set(ex);
Thread.currentThread().interrupt();
} catch (BrokenBarrierException ex) {
exceptionRef.set(ex);
} catch (SQLException ex) {
exceptionRef.set(ex);
wasInterrupted.set(Thread.currentThread().isInterrupted());
} catch (Exception ex) {
exceptionRef.set(ex);
}
}
});
barrier.await();
// Allow the query time to send
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
// Interrupt the thread
service.shutdownNow();
assertTrue(
service.awaitTermination(1, TimeUnit.MINUTES)
);
assertNotNull(exceptionRef.get());
//ensure that even interrupted, connection status is when sending in bulk (all corresponding bulk send are read)
ResultSet rs = sharedConnection.createStatement().executeQuery("SELECT 123456");
assertTrue(rs.next());
assertEquals(123456, rs.getInt(1));
StringWriter writer = new StringWriter();
exceptionRef.get().printStackTrace(new PrintWriter(writer));
assertTrue(
"Exception should be a SQLException: \n" + writer.toString(),
exceptionRef.get() instanceof SQLException
);
assertTrue(wasInterrupted.get());
}
@Test
public void serverBulk8mTest() throws SQLException {
Assume.assumeTrue(checkMaxAllowedPacketMore8m("serverBulk8mTest"));
Assume.assumeTrue(runLongTest);
sharedConnection.createStatement().execute("TRUNCATE TABLE ExecuteBatchTest");
try (Connection connection = setConnection("&useComMulti=false&useBatchMultiSend=true&profileSql=" + profileSql)) {
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO ExecuteBatchTest(test, test2) values (?, ?)");
//packet size : 7 200 068 kb
addBatchData(preparedStatement, 60000, connection);
}
}
@Test
public void serverBulk20mTest() throws SQLException {
Assume.assumeTrue(checkMaxAllowedPacketMore20m("serverBulk20mTest"));
Assume.assumeTrue(runLongTest);
sharedConnection.createStatement().execute("TRUNCATE TABLE ExecuteBatchTest");
try (Connection connection = setConnection("&useComMulti=false&useBatchMultiSend=true&profileSql=" + profileSql)) {
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO ExecuteBatchTest(test, test2) values (?, ?)");
//packet size : 7 200 068 kb
addBatchData(preparedStatement, 160000, connection);
}
}
@Test
public void serverStd8mTest() throws SQLException {
Assume.assumeTrue(checkMaxAllowedPacketMore8m("serverStd8mTest"));
Assume.assumeTrue(runLongTest);
sharedConnection.createStatement().execute("TRUNCATE TABLE ExecuteBatchTest");
try (Connection connection = setConnection("&useComMulti=false&useBatchMultiSend=false&profileSql=" + profileSql)) {
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO ExecuteBatchTest(test, test2) values (?, ?)");
addBatchData(preparedStatement, 60000, connection);
}
}
@Test
public void clientBulkTest() throws SQLException {
Assume.assumeTrue(checkMaxAllowedPacketMore8m("serverStd8mTest"));
Assume.assumeTrue(runLongTest);
sharedConnection.createStatement().execute("TRUNCATE TABLE ExecuteBatchTest");
try (Connection connection = setConnection("&useComMulti=false&useBatchMultiSend=true&useServerPrepStmts=false&profileSql=" + profileSql)) {
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO ExecuteBatchTest(test, test2) values (?, ?)");
addBatchData(preparedStatement, 60000, connection);
}
}
@Test
public void clientRewriteValuesNotPossible8mTest() throws SQLException {
Assume.assumeTrue(checkMaxAllowedPacketMore8m("clientRewriteValuesNotPossibleTest"));
Assume.assumeTrue(runLongTest);
sharedConnection.createStatement().execute("TRUNCATE TABLE ExecuteBatchTest");
try (Connection connection = setConnection("&rewriteBatchedStatements=true&profileSql=" + profileSql)) {
PreparedStatement preparedStatement = connection.prepareStatement(
"INSERT INTO ExecuteBatchTest(test, test2) values (?, ?) ON DUPLICATE KEY UPDATE id=?");
addBatchData(preparedStatement, 60000, connection, true);
}
}
@Test
public void clientRewriteValuesNotPossible20mTest() throws SQLException {
Assume.assumeTrue(checkMaxAllowedPacketMore8m("clientRewriteValuesNotPossibleTest"));
Assume.assumeTrue(runLongTest);
sharedConnection.createStatement().execute("TRUNCATE TABLE ExecuteBatchTest");
try (Connection connection = setConnection("&rewriteBatchedStatements=true&profileSql=" + profileSql)) {
PreparedStatement preparedStatement = connection.prepareStatement(
"INSERT INTO ExecuteBatchTest(test, test2) values (?, ?) ON DUPLICATE KEY UPDATE id=?");
addBatchData(preparedStatement, 160000, connection, true);
}
}
@Test
public void clientRewriteValuesPossibleTest() throws SQLException {
// 8mb
// 20mb
// 40mb
}
@Test
public void clientRewriteMultiTest() throws SQLException {
// 8mb
// 20mb
// 40mb
}
@Test
public void clientStdMultiTest() throws SQLException {
// 8mb
// 20mb
// 40mb
}
private void addBatchData(PreparedStatement preparedStatement, int batchNumber, Connection connection) throws SQLException {
addBatchData(preparedStatement, batchNumber, connection, false);
}
private void addBatchData(PreparedStatement preparedStatement, int batchNumber, Connection connection, boolean additionnalParameter)
throws SQLException {
for (int i = 0; i < batchNumber; i++) {
preparedStatement.setString(1, oneHundredLengthString);
preparedStatement.setInt(2, i);
if (additionnalParameter) preparedStatement.setInt(3, i);
preparedStatement.addBatch();
}
int[] resultInsert = preparedStatement.executeBatch();
//test result Size
assertEquals(batchNumber, resultInsert.length);
for (int i = 0; i < batchNumber; i++) {
assertEquals(1, resultInsert[i]);
}
//check that connection is OK and results are well inserted
ResultSet resultSet = connection.createStatement().executeQuery("SELECT * FROM ExecuteBatchTest");
for (int i = 0; i < batchNumber; i++) {
assertTrue(resultSet.next());
assertEquals(i + 1, resultSet.getInt(1));
assertEquals(oneHundredLengthString, resultSet.getString(2));
assertEquals(i, resultSet.getInt(3));
}
assertFalse(resultSet.next());
}
@Test
public void useBatchMultiSend() throws Exception {
try (Connection connection = setConnection("&useBatchMultiSend=true")) {
String sql = "insert into ExecuteBatchUseBatchMultiSend (test) values (?)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
for (int i = 0; i < 10; i++) {
pstmt.setInt(1, i);
pstmt.addBatch();
}
int[] updateCounts = pstmt.executeBatch();
assertEquals(10, updateCounts.length);
for (int i = 0; i < updateCounts.length; i++) {
assertEquals(sharedIsRewrite() ? Statement.SUCCESS_NO_INFO : 1, updateCounts[i]);
}
}
}
}
}