Skip to content

Commit

Permalink
IGNITE-5438: JDBC Thin Driver: support Statement.setQueryTimeout. This
Browse files Browse the repository at this point in the history
…closes apache#5772. This closes apache#5813.
  • Loading branch information
sanpwc authored and devozerov committed Jan 16, 2019
1 parent 239a760 commit 8734adf
Show file tree
Hide file tree
Showing 5 changed files with 451 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.apache.ignite.jdbc.thin.JdbcThinSelectAfterAlterTable;
import org.apache.ignite.jdbc.thin.JdbcThinStatementCancelSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinStatementSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinStatementTimeoutSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinStreamingNotOrderedSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinStreamingOrderedSelfTest;
import org.apache.ignite.jdbc.thin.JdbcThinTcpIoTest;
Expand Down Expand Up @@ -165,6 +166,7 @@
JdbcThinMetadataPrimaryKeysSelfTest.class,
JdbcThinErrorsSelfTest.class,
JdbcThinStatementCancelSelfTest.class,
JdbcThinStatementTimeoutSelfTest.class,

JdbcThinInsertStatementSelfTest.class,
JdbcThinUpdateStatementSelfTest.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLTimeoutException;
import java.sql.Statement;
import java.util.concurrent.Callable;
import org.apache.ignite.IgniteCache;
Expand Down Expand Up @@ -382,29 +381,6 @@ public void testCloseOnCompletionBeforeQuery() throws Exception {
assert stmt.isClosed() : "Statement must be closed";
}

/**
* @throws Exception If failed.
*/
@org.junit.Test
public void testExecuteQueryTimeout() throws Exception {
fail("https://issues.apache.org/jira/browse/IGNITE-5438");

final String sqlText = "select sleep_func(3)";

stmt.setQueryTimeout(1);

// Timeout
GridTestUtils.assertThrows(log,
new Callable<Object>() {
@Override public Object call() throws Exception {
return stmt.executeQuery(sqlText);
}
},
SQLTimeoutException.class,
"Timeout"
);
}

/**
* @throws Exception If failed.
*/
Expand Down Expand Up @@ -567,29 +543,6 @@ public void testExecuteUpdateProducesResultSet() throws Exception {
);
}

/**
* @throws Exception If failed.
*/
@org.junit.Test
public void testExecuteUpdateTimeout() throws Exception {
fail("https://issues.apache.org/jira/browse/IGNITE-5438");

final String sqlText = "update test set val=1 where _key=sleep_func(3)";

stmt.setQueryTimeout(1);

// Timeout
GridTestUtils.assertThrows(log,
new Callable<Object>() {
@Override public Object call() throws Exception {
return stmt.executeUpdate(sqlText);
}
},
SQLTimeoutException.class,
"Timeout"
);
}

/**
* @throws Exception If failed.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ignite.jdbc.thin;

import java.io.File;
import java.io.FileWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLTimeoutException;
import java.sql.Statement;
import org.apache.ignite.cache.query.annotations.QuerySqlFunction;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.ClientConnectorConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
import org.apache.ignite.testframework.GridTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import static org.apache.ignite.cache.CacheMode.PARTITIONED;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;

/**
* Statement timeout test.
*/
@SuppressWarnings("ThrowableNotThrown")
@RunWith(JUnit4.class)
public class JdbcThinStatementTimeoutSelfTest extends JdbcThinAbstractSelfTest {
/** IP finder. */
private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);

/** URL. */
private static final String URL = "jdbc:ignite:thin://127.0.0.1/";

/** Server thread pull size. */
private static final int SERVER_THREAD_POOL_SIZE = 4;

/** Connection. */
private Connection conn;

/** Statement. */
private Statement stmt;

/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);

CacheConfiguration<?,?> cache = defaultCacheConfiguration();

cache.setCacheMode(PARTITIONED);
cache.setBackups(1);
cache.setWriteSynchronizationMode(FULL_SYNC);
cache.setSqlFunctionClasses(TestSQLFunctions.class);
cache.setIndexedTypes(Integer.class, Integer.class, Long.class, Long.class, String.class,
JdbcThinAbstractDmlStatementSelfTest.Person.class);

cfg.setCacheConfiguration(cache);

TcpDiscoverySpi disco = new TcpDiscoverySpi();

disco.setIpFinder(IP_FINDER);

cfg.setDiscoverySpi(disco);

cfg.setClientConnectorConfiguration(new ClientConnectorConfiguration().setThreadPoolSize(SERVER_THREAD_POOL_SIZE));

return cfg;
}

/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
super.beforeTestsStarted();

startGridsMultiThreaded(3);

for (int i = 0; i < 10000; ++i)
grid(0).cache(DEFAULT_CACHE_NAME).put(i, i);

for (int i = 0; i < 10000; ++i)
grid(0).cache(DEFAULT_CACHE_NAME).put((long)i, (long)i);
}

/**
* Called before execution of every test method in class.
*
* @throws Exception If failed.
*/
@Before
public void before() throws Exception {
conn = DriverManager.getConnection(URL);

conn.setSchema('"' + DEFAULT_CACHE_NAME + '"');

stmt = conn.createStatement();

assert stmt != null;
assert !stmt.isClosed();
}

/**
* Called after execution of every test method in class.
*
* @throws Exception If failed.
*/
@After
public void after() throws Exception {
if (stmt != null && !stmt.isClosed()) {
stmt.close();

assert stmt.isClosed();
}

conn.close();

assert stmt.isClosed();
assert conn.isClosed();
}

/**
* Trying to set negative timeout. <code>SQLException</> with message "Invalid timeout value." is expected.
*/
@Test
public void testSettingNegativeQueryTimeout() {
GridTestUtils.assertThrows(log, () -> {
stmt.setQueryTimeout(-1);

return null;
}, SQLException.class, "Invalid timeout value.");
}

/**
* Trying to set zero timeout. Zero timeout means no timeout, so no exception is expected.
*
* @throws Exception If failed.
*/
@Test
public void testSettingZeroQueryTimeout() throws Exception {
stmt.setQueryTimeout(0);

stmt.executeQuery("select sleep_func(1000);");
}

/**
* Setting timeout that is greater than query execution time. <code>SQLTimeoutException</code> is expected.
*
* @throws Exception If failed.
*/
@Test
public void testQueryTimeout() throws Exception {
stmt.setQueryTimeout(2);

GridTestUtils.assertThrows(log, () -> {
stmt.executeQuery("select sleep_func(10) from Integer;");

return null;
}, SQLTimeoutException.class, "The query was cancelled while executing.");
}

/**
* Setting timeout that is greater than query execution time. Running same query multiple times.
* <code>SQLTimeoutException</code> is expected in all cases.
*
* @throws Exception If failed.
*/
@SuppressWarnings("unchecked")
@Test
public void testQueryTimeoutRepeatable() throws Exception {
stmt.setQueryTimeout(2);

GridTestUtils.assertThrows(log, () -> {
stmt.executeQuery("select sleep_func(10) from Integer;");

return null;
}, SQLTimeoutException.class, "The query was cancelled while executing.");

GridTestUtils.assertThrows(log, () -> {
stmt.executeQuery("select sleep_func(10) from Integer;");

return null;
}, SQLTimeoutException.class, "The query was cancelled while executing.");
}

/**
* Setting timeout that is greater than file uploading execution time.
* <code>SQLTimeoutException</code> is expected.
*
* @throws Exception If failed.
*/
@SuppressWarnings("unchecked")
@Test
public void testFileUploadingTimeout() throws Exception {

File file = File.createTempFile("bulkload", "csv");

FileWriter writer = new FileWriter(file);

for (int i = 1; i <= 1_000_000; i++)
writer.write(String.format("%d,%d,\"FirstName%d MiddleName%d\",LastName%d", i, i, i, i, i));

writer.close();

stmt.setQueryTimeout(1);

GridTestUtils.assertThrows(log, () -> {
stmt.executeUpdate(
"copy from '" + file.getAbsolutePath() + "' into Person" +
" (_key, age, firstName, lastName)" +
" format csv");

return null;
}, SQLTimeoutException.class, "The query was cancelled while executing.");
}

/**
* Setting timeout that is greater than batch query execution time.
* <code>SQLTimeoutException</code> is expected.
*
* @throws Exception If failed.
*/
@Test
public void testBatchQuery() throws Exception {
stmt.setQueryTimeout(1);

GridTestUtils.assertThrows(log, () -> {
stmt.addBatch("update Long set _val = _val + 1 where _key < sleep_func (30)");
stmt.addBatch("update Long set _val = _val + 1 where _key > sleep_func (10)");

stmt.executeBatch();

return null;
}, SQLTimeoutException.class, "The query was cancelled while executing.");
}

/**
* Setting timeout that is greater than multiple statements query execution time.
* <code>SQLTimeoutException</code> is expected.
*
* @throws Exception If failed.
*/
@Test
public void testMultipleStatementsQuery() throws Exception {
stmt.setQueryTimeout(1);

GridTestUtils.assertThrows(log, () -> {
stmt.execute(
"update Long set _val = _val + 1 where _key > sleep_func (10);"
+ "update Long set _val = _val + 1 where _key > sleep_func (10);"
+ "update Long set _val = _val + 1 where _key > sleep_func (10);"
+ "update Long set _val = _val + 1 where _key > sleep_func (10);"
+ "select _val, sleep_func(10) as s from Integer limit 10");

return null;
}, SQLTimeoutException.class, "The query was cancelled while executing.");
}

/**
* Setting timeout that is greater than update query execution time.
* <code>SQLTimeoutException</code> is expected.
*
* @throws Exception If failed.
*/
@Test
public void testExecuteUpdateTimeout() throws Exception {
stmt.setQueryTimeout(1);

GridTestUtils.assertThrows(log, () ->
stmt.executeUpdate("update Integer set _val=1 where _key > sleep_func(10)"),
SQLTimeoutException.class, "The query was cancelled while executing.");
}

/**
* Utility class with custom SQL functions.
*/
public static class TestSQLFunctions {
/**
* @param v amount of milliseconds to sleep
* @return amount of milliseconds to sleep
*/
@SuppressWarnings("unused")
@QuerySqlFunction
public static int sleep_func(int v) {
try {
Thread.sleep(v);
}
catch (InterruptedException ignored) {
// No-op
}
return v;
}
}
}

0 comments on commit 8734adf

Please sign in to comment.