/* * 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.tajo.jdbc; import org.apache.tajo.IntegrationTest; import org.apache.tajo.QueryTestCaseBase; import org.apache.tajo.error.Errors.ResultCode; import org.apache.tajo.exception.SQLExceptionUtil; import org.apache.tajo.util.UriUtil; import org.junit.AfterClass; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.sql.*; import java.util.Properties; import static org.apache.tajo.TajoConstants.DEFAULT_DATABASE_NAME; import static org.apache.tajo.error.Errors.ResultCode.CLIENT_CONNECTION_EXCEPTION; import static org.apache.tajo.exception.SQLExceptionUtil.toSQLState; import static org.apache.tajo.jdbc.TestTajoJdbc.buildConnectionUri; import static org.junit.Assert.*; @Category(IntegrationTest.class) public class TestTajoJdbcNegative extends QueryTestCaseBase { private static InetSocketAddress tajoMasterAddress; @BeforeClass public static void setUp() throws Exception { tajoMasterAddress = testingCluster.getMaster().getTajoMasterClientService().getBindAddress(); Class.forName("org.apache.tajo.jdbc.TajoDriver").newInstance(); } @AfterClass public static void tearDown() throws Exception { } @Test(expected = SQLException.class) public void testGetConnection() throws SQLException { DriverManager.getConnection("jdbc:taju://" + tajoMasterAddress.getHostName() + ":" + tajoMasterAddress.getPort() + "/default"); } @Test public void testUnresolvedError() throws SQLException { try { DriverManager.getConnection("jdbc:tajo://tajo-unknown-asdnkl213.asd:2002/default"); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.CLIENT_CONNECTION_EXCEPTION), s.getSQLState()); assertEquals("Can't resolve host name: tajo-unknown-asdnkl213.asd:2002", s.getMessage()); } } @Test public void testConnectionRefused() throws SQLException, IOException { Integer port = null; try { ServerSocket s = new ServerSocket(0); port = s.getLocalPort(); s.close(); DriverManager.getConnection("jdbc:tajo://localhost:" + port + "/default"); fail("Must be failed."); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.CLIENT_CONNECTION_EXCEPTION), s.getSQLState()); } } @Test public void testConnectionClosedAtCreateStmt() throws SQLException, IOException { String connUri = buildConnectionUri(tajoMasterAddress.getHostName(), tajoMasterAddress.getPort(), DEFAULT_DATABASE_NAME); Connection conn = DriverManager.getConnection(connUri); assertTrue(conn.isValid(100)); conn.close(); try (Statement stmt = conn.createStatement()) { fail("Must be failed."); stmt.isClosed(); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.CLIENT_CONNECTION_DOES_NOT_EXIST), s.getSQLState()); assertEquals("This connection has been closed.", s.getMessage()); } } @Test public void testConnectionClosed() throws SQLException, IOException { String connUri = buildConnectionUri(tajoMasterAddress.getHostName(), tajoMasterAddress.getPort(), DEFAULT_DATABASE_NAME); Connection conn = DriverManager.getConnection(connUri); assertTrue(conn.isValid(100)); try (Statement stmt = conn.createStatement()) { conn.close(); stmt.executeUpdate("SELECT 1;"); fail("Must be failed."); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.CLIENT_CONNECTION_DOES_NOT_EXIST), s.getSQLState()); assertEquals("This connection has been closed.", s.getMessage()); } } @Test public void testSyntaxErrorOnExecuteUpdate() throws Exception { String connUri = buildConnectionUri(tajoMasterAddress.getHostName(), tajoMasterAddress.getPort(), DEFAULT_DATABASE_NAME); Connection conn = DriverManager.getConnection(connUri); assertTrue(conn.isValid(100)); try (Statement stmt = conn.createStatement()) { stmt.executeUpdate("CREATE TABLE \n1table123u8sd ( name RECORD(last TEXT, first TEXT) )"); fail("Must be failed"); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.SYNTAX_ERROR), s.getSQLState()); assertEquals( "ERROR: syntax error at or near \"1\"\n" + "LINE 2: 1table123u8sd ( name RECORD(last TEXT, first TEXT) )\n" + " ^", s.getMessage()); } } @Test public void testSyntaxErrorOnExecuteQuery() throws Exception { String connUri = buildConnectionUri(tajoMasterAddress.getHostName(), tajoMasterAddress.getPort(), DEFAULT_DATABASE_NAME); Connection conn = DriverManager.getConnection(connUri); assertTrue(conn.isValid(100)); try (Statement stmt = conn.createStatement()) { try (ResultSet result = stmt.executeQuery("SELECT\n*\nFROM_ LINEITEM")) { fail("Must be failed"); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.SYNTAX_ERROR), s.getSQLState()); assertEquals( "ERROR: syntax error at or near \"from_\"\n" + "LINE 3: FROM_ LINEITEM\n" + " ^^^^^", s.getMessage()); } } } @Test public void testImmediateException() throws Exception { String connUri = buildConnectionUri(tajoMasterAddress.getHostName(), tajoMasterAddress.getPort(), DEFAULT_DATABASE_NAME); Connection conn = DriverManager.getConnection(connUri); assertTrue(conn.isValid(100)); try (Statement stmt = conn.createStatement()) { stmt.executeUpdate("CREATE DATABASE IF NOT EXISTS TestTajoJdbcNegative"); stmt.executeUpdate("CREATE TABLE TestTajoJdbcNegative.table123u8sd ( name RECORD (last TEXT, first TEXT) )"); try (ResultSet resultSet = stmt.executeQuery("select name FROM TestTajoJdbcNegative.table123u8sd")) { fail("Getting a record type field must be failed"); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.NOT_IMPLEMENTED), s.getSQLState()); } finally { stmt.executeUpdate("DROP TABLE IF EXISTS TestTajoJdbcNegative.table12u79"); stmt.executeUpdate("DROP DATABASE IF EXISTS TestTajoJdbcNegative"); } } } @Test public void testExceptionDuringProcessing() throws Exception { String connUri = buildConnectionUri(tajoMasterAddress.getHostName(), tajoMasterAddress.getPort(), DEFAULT_DATABASE_NAME); Connection conn = DriverManager.getConnection(connUri); assertTrue(conn.isValid(100)); try (Statement stmt = conn.createStatement()) { try (ResultSet resultSet = stmt.executeQuery( "select fail(3, l_orderkey, 'testQueryFailure') from default.lineitem where l_orderkey > 0")) { fail("Failure must occur here."); } catch (SQLException s) { assertEquals(toSQLState(ResultCode.INTERNAL_ERROR), s.getSQLState()); } } } private void assumeConnectTimeout(String host, int port, int connectTimeout) throws IOException { try (Socket socket = new Socket()) { // Try to connect to a private address in the 10.x.y.z range. // These addresses are usually not routed, so an attempt to // connect to them will hang the connection attempt, which is // what we want to simulate in this test. socket.connect(new InetSocketAddress(host, port), connectTimeout); // Abort the test if we can connect. Assume.assumeTrue(false); } catch (SocketTimeoutException x) { // Expected timeout during connect, continue the test. Assume.assumeTrue(true); } catch (Throwable x) { // Abort if any other exception happens. Assume.assumeTrue(false); } } @Test(timeout = 5000) public final void testConnectTimeout() throws Exception { final String host = "10.255.255.1"; final int port = 80; int connectTimeout = 1000; assumeConnectTimeout(host, port, connectTimeout); long startTime = Long.MIN_VALUE; long endTime; try { // artificially cause connection timeout String connUri = buildConnectionUri(host, port, DEFAULT_DATABASE_NAME); connUri = UriUtil.addParam(connUri, "connectTimeout", "1"); // 1 seconds connUri = UriUtil.addParam(connUri, "retry", "0"); // 1 seconds startTime = System.currentTimeMillis(); new JdbcConnection(connUri, new Properties()); fail("Must be failed"); } catch (SQLException t) { endTime = System.currentTimeMillis(); assertEquals(t.getSQLState(), SQLExceptionUtil.toSQLState(CLIENT_CONNECTION_EXCEPTION)); assertEquals("connection timed out: /10.255.255.1:80", t.getMessage()); // default is 15 seconds. So, if timeout is shorter than 1~2 seconds. // We can ensure the parameter was effective. assertTrue(((endTime - startTime) / 1000) < 2); } } }