/* * Licensed 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 com.facebook.presto.jdbc; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.QueryId; import io.airlift.log.Logging; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Set; import java.util.concurrent.Callable; import static com.facebook.presto.jdbc.TestPrestoDriver.closeQuietly; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.testing.Assertions.assertContains; import static java.lang.String.format; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @Test(singleThreaded = true) public class TestPrestoDatabaseMetaData { private TestingPrestoServer server; private Connection connection; @BeforeClass public void setupServer() throws Exception { Logging.initialize(); server = new TestingPrestoServer(); } @AfterClass(alwaysRun = true) public void tearDownServer() { server.close(); } @SuppressWarnings("JDBCResourceOpenedButNotSafelyClosed") @BeforeMethod public void setup() throws Exception { connection = createConnection(); } @AfterMethod(alwaysRun = true) public void tearDown() throws SQLException { closeQuietly(connection); } @Test public void testPassEscapeInMetaDataQuery() throws Exception { DatabaseMetaData metaData = connection.getMetaData(); Set<String> queries = captureQueries(() -> { String schemaPattern = "defau" + metaData.getSearchStringEscape() + "_t"; try (ResultSet resultSet = metaData.getColumns("blackhole", schemaPattern, null, null)) { assertFalse(resultSet.next(), "There should be no results"); } return null; }); assertEquals(queries.size(), 1, "Expected exactly one query, got " + queries.size()); String query = getOnlyElement(queries); assertContains(query, "_t' ESCAPE '", "Metadata query does not contain ESCAPE"); } @Test public void testGetTypeInfo() throws Exception { DatabaseMetaData metaData = connection.getMetaData(); ResultSet typeInfo = metaData.getTypeInfo(); while (typeInfo.next()) { int jdbcType = typeInfo.getInt("DATA_TYPE"); switch (jdbcType) { case Types.BIGINT: assertColumnSpec(typeInfo, Types.BIGINT, 19L, 10L, "bigint"); break; case Types.BOOLEAN: assertColumnSpec(typeInfo, Types.BOOLEAN, null, null, "boolean"); break; case Types.INTEGER: assertColumnSpec(typeInfo, Types.INTEGER, 10L, 10L, "integer"); break; case Types.DECIMAL: assertColumnSpec(typeInfo, Types.DECIMAL, 38L, 10L, "decimal"); break; case Types.VARCHAR: assertColumnSpec(typeInfo, Types.VARCHAR, null, null, "varchar"); break; case Types.TIMESTAMP: assertColumnSpec(typeInfo, Types.TIMESTAMP, 23L, null, "timestamp"); break; case Types.DOUBLE: assertColumnSpec(typeInfo, Types.DOUBLE, 53L, 2L, "double"); break; } } } private static void assertColumnSpec(ResultSet rs, int dataType, Long precision, Long numPrecRadix, String typeName) throws SQLException { String message = " of " + typeName + ": "; assertEquals(rs.getObject("TYPE_NAME"), typeName, "TYPE_NAME" + message); assertEquals(rs.getObject("DATA_TYPE"), (long) dataType, "DATA_TYPE" + message); assertEquals(rs.getObject("PRECISION"), precision, "PRECISION" + message); assertEquals(rs.getObject("LITERAL_PREFIX"), null, "LITERAL_PREFIX" + message); assertEquals(rs.getObject("LITERAL_SUFFIX"), null, "LITERAL_SUFFIX" + message); assertEquals(rs.getObject("CREATE_PARAMS"), null, "CREATE_PARAMS" + message); assertEquals(rs.getObject("NULLABLE"), (long) DatabaseMetaData.typeNullable, "NULLABLE" + message); assertEquals(rs.getObject("CASE_SENSITIVE"), false, "CASE_SENSITIVE" + message); assertEquals(rs.getObject("SEARCHABLE"), (long) DatabaseMetaData.typeSearchable, "SEARCHABLE" + message); assertEquals(rs.getObject("UNSIGNED_ATTRIBUTE"), null, "UNSIGNED_ATTRIBUTE" + message); assertEquals(rs.getObject("FIXED_PREC_SCALE"), false, "FIXED_PREC_SCALE" + message); assertEquals(rs.getObject("AUTO_INCREMENT"), null, "AUTO_INCREMENT" + message); assertEquals(rs.getObject("LOCAL_TYPE_NAME"), null, "LOCAL_TYPE_NAME" + message); assertEquals(rs.getObject("MINIMUM_SCALE"), 0L, "MINIMUM_SCALE" + message); assertEquals(rs.getObject("MAXIMUM_SCALE"), 0L, "MAXIMUM_SCALE" + message); assertEquals(rs.getObject("SQL_DATA_TYPE"), null, "SQL_DATA_TYPE" + message); assertEquals(rs.getObject("SQL_DATETIME_SUB"), null, "SQL_DATETIME_SUB" + message); assertEquals(rs.getObject("NUM_PREC_RADIX"), numPrecRadix, "NUM_PREC_RADIX" + message); } private Set<String> captureQueries(Callable<?> action) throws Exception { Set<QueryId> queryIdsBefore = server.getQueryManager().getAllQueryInfo().stream() .map(QueryInfo::getQueryId) .collect(toImmutableSet()); action.call(); return server.getQueryManager().getAllQueryInfo().stream() .filter(queryInfo -> !queryIdsBefore.contains(queryInfo.getQueryId())) .map(QueryInfo::getQuery) .collect(toImmutableSet()); } private Connection createConnection() throws SQLException { String url = format("jdbc:presto://%s", server.getAddress()); return DriverManager.getConnection(url, "test", null); } }