| /* |
| * 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.drill.jdbc.impl; |
| |
| import java.io.File; |
| import java.sql.Blob; |
| import java.sql.CallableStatement; |
| import java.sql.Clob; |
| import java.sql.NClob; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLClientInfoException; |
| import java.sql.SQLException; |
| import java.sql.SQLFeatureNotSupportedException; |
| import java.sql.SQLNonTransientConnectionException; |
| import java.sql.SQLXML; |
| import java.sql.Savepoint; |
| import java.sql.Struct; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.TimeZone; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Executor; |
| |
| import org.apache.calcite.avatica.AvaticaConnection; |
| import org.apache.calcite.avatica.AvaticaFactory; |
| import org.apache.calcite.avatica.AvaticaStatement; |
| import org.apache.calcite.avatica.Meta.ExecuteResult; |
| import org.apache.calcite.avatica.Meta.MetaResultSet; |
| import org.apache.calcite.avatica.NoSuchStatementException; |
| import org.apache.calcite.avatica.QueryState; |
| import org.apache.calcite.avatica.UnregisteredDriver; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.drill.common.config.DrillConfig; |
| import org.apache.drill.common.exceptions.DrillRuntimeException; |
| import org.apache.drill.common.exceptions.UserException; |
| import org.apache.drill.exec.client.DrillClient; |
| import org.apache.drill.exec.client.InvalidConnectionInfoException; |
| import org.apache.drill.exec.exception.OutOfMemoryException; |
| import org.apache.drill.exec.memory.BufferAllocator; |
| import org.apache.drill.exec.memory.RootAllocatorFactory; |
| import org.apache.drill.exec.proto.UserBitShared; |
| import org.apache.drill.exec.proto.UserProtos; |
| import org.apache.drill.exec.rpc.RpcException; |
| import org.apache.drill.exec.server.Drillbit; |
| import org.apache.drill.exec.server.RemoteServiceSet; |
| import org.apache.drill.exec.store.SchemaFactory; |
| import org.apache.drill.exec.store.StoragePluginRegistry; |
| import org.apache.drill.jdbc.AlreadyClosedSqlException; |
| import org.apache.drill.jdbc.DrillConnection; |
| import org.apache.drill.jdbc.DrillConnectionConfig; |
| import org.apache.drill.jdbc.InvalidParameterSqlException; |
| import org.apache.drill.jdbc.JdbcApiSqlException; |
| import org.slf4j.Logger; |
| |
| import org.apache.drill.shaded.guava.com.google.common.base.Throwables; |
| import org.slf4j.LoggerFactory; |
| |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.DFS_PLUGIN_NAME; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.ROOT_SCHEMA; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.TMP_SCHEMA; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.UNIT_TEST_DFS_DEFAULT_PROP; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.UNIT_TEST_DFS_ROOT_PROP; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.UNIT_TEST_DFS_TMP_PROP; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.UNIT_TEST_PROP_PREFIX; |
| import static org.apache.drill.exec.util.StoragePluginTestUtils.updateSchemaLocation; |
| |
| /** |
| * Drill's implementation of {@link java.sql.Connection}. |
| */ |
| // (Was abstract to avoid errors _here_ if newer versions of JDBC added |
| // interface methods, but now newer versions would probably use Java 8's default |
| // methods for compatibility.) |
| public class DrillConnectionImpl extends AvaticaConnection implements DrillConnection { |
| |
| private static final Logger logger = LoggerFactory.getLogger(DrillConnectionImpl.class); |
| |
| final DrillStatementRegistry openStatementsRegistry = new DrillStatementRegistry(); |
| final DrillConnectionConfig config; |
| |
| private final DrillClient client; |
| private final BufferAllocator allocator; |
| private Drillbit bit; |
| private RemoteServiceSet serviceSet; |
| |
| |
| public DrillConnectionImpl(DriverImpl driver, AvaticaFactory factory, |
| String url, Properties info) throws SQLException { |
| super(driver, factory, url, info); |
| |
| // Initialize transaction-related settings per Drill behavior. |
| super.setTransactionIsolation(TRANSACTION_NONE); |
| super.setAutoCommit(true); |
| super.setReadOnly(false); |
| |
| this.config = new DrillConnectionConfig(info); |
| try { |
| try { |
| String connect = null; |
| |
| if (config.isLocal()) { |
| try { |
| Class.forName("org.eclipse.jetty.server.Handler"); |
| } catch (ClassNotFoundException e) { |
| throw new SQLNonTransientConnectionException( |
| "Running Drill in embedded mode using Drill's jdbc-all JDBC" |
| + " driver Jar file alone is not supported."); |
| } |
| |
| final DrillConfig dConfig = DrillConfig.create(info); |
| this.allocator = RootAllocatorFactory.newRoot(dConfig); |
| RemoteServiceSet set = GlobalServiceSetReference.SETS.get(); |
| if (set == null) { |
| // We're embedded; start a local drill bit. |
| serviceSet = RemoteServiceSet.getLocalServiceSet(); |
| set = serviceSet; |
| try { |
| bit = new Drillbit(dConfig, serviceSet); |
| bit.run(); |
| } catch (final UserException e) { |
| throw new SQLException( |
| "Failure in starting embedded Drillbit: " + e.getMessage(), |
| e); |
| } catch (Exception e) { |
| // (Include cause exception's text in wrapping exception's text so |
| // it's more likely to get to user (e.g., via SQLLine), and use |
| // toString() since getMessage() text doesn't always mention error:) |
| throw new SQLException("Failure in starting embedded Drillbit: " + e, e); |
| } |
| } else { |
| serviceSet = null; |
| bit = null; |
| } |
| |
| makeTmpSchemaLocationsUnique(bit.getContext().getStorage(), info); |
| |
| this.client = new DrillClient(dConfig, set.getCoordinator()); |
| } else if(config.isDirect()) { |
| final DrillConfig dConfig = DrillConfig.forClient(); |
| this.allocator = RootAllocatorFactory.newRoot(dConfig); |
| this.client = new DrillClient(dConfig, true); // Get a direct connection |
| connect = config.getZookeeperConnectionString(); |
| } else { |
| final DrillConfig dConfig = DrillConfig.forClient(); |
| this.allocator = RootAllocatorFactory.newRoot(dConfig); |
| // TODO: Check: Why does new DrillClient() create another DrillConfig, |
| // with enableServerConfigs true, and cause scanning for function |
| // implementations (needed by a server, but not by a client-only |
| // process, right?)? Probably pass dConfig to construction. |
| this.client = new DrillClient(); |
| connect = config.getZookeeperConnectionString(); |
| } |
| this.client.setClientName("Apache Drill JDBC Driver"); |
| this.client.connect(connect, info); |
| } catch (OutOfMemoryException e) { |
| throw new SQLNonTransientConnectionException("Failure creating root allocator", e); |
| } catch (InvalidConnectionInfoException e) { |
| throw new SQLNonTransientConnectionException("Invalid parameter in connection string: " + e.getMessage(), e); |
| } catch (RpcException e) { |
| // (Include cause exception's text in wrapping exception's text so |
| // it's more likely to get to user (e.g., via SQLLine), and use |
| // toString() since getMessage() text doesn't always mention error:) |
| throw new SQLNonTransientConnectionException("Failure in connecting to Drill: " + e, e); |
| } catch(SQLException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new SQLException("Failure in creating DrillConnectionImpl: " + e, e); |
| } |
| } catch (Throwable t) { |
| cleanup(); |
| throw t; |
| } |
| } |
| |
| |
| @Override |
| protected ResultSet createResultSet(MetaResultSet metaResultSet, QueryState state) throws SQLException { |
| return super.createResultSet(metaResultSet, state); |
| } |
| |
| @Override |
| protected ExecuteResult prepareAndExecuteInternal(AvaticaStatement statement, String sql, long maxRowCount) |
| throws SQLException, NoSuchStatementException { |
| try { |
| return super.prepareAndExecuteInternal(statement, sql, maxRowCount); |
| } catch (RuntimeException e) { |
| Throwables.throwIfInstanceOf(e.getCause(), SQLException.class); |
| throw e; |
| } |
| } |
| /** |
| * Throws AlreadyClosedSqlException <i>iff</i> this Connection is closed. |
| * |
| * @throws AlreadyClosedSqlException if Connection is closed |
| */ |
| @Override |
| protected void checkOpen() throws AlreadyClosedSqlException { |
| if (isClosed()) { |
| throw new AlreadyClosedSqlException("Connection is already closed."); |
| } |
| } |
| |
| @Override |
| public DrillConnectionConfig getConfig() { |
| return config; |
| } |
| |
| BufferAllocator getAllocator() { |
| return allocator; |
| } |
| |
| @Override |
| public DrillClient getClient() { |
| return client; |
| } |
| |
| @Override |
| public void setAutoCommit(boolean autoCommit) throws SQLException { |
| checkOpen(); |
| if (!autoCommit) { |
| throw new SQLFeatureNotSupportedException( |
| "Can't turn off auto-committing; transactions are not supported. " |
| + "(Drill is not transactional.)" ); |
| } |
| assert getAutoCommit() : "getAutoCommit() = " + getAutoCommit(); |
| } |
| |
| @Override |
| public void commit() throws SQLException { |
| checkOpen(); |
| if (getAutoCommit()) { |
| throw new JdbcApiSqlException("Can't call commit() in auto-commit mode."); |
| } else { |
| // (Currently not reachable.) |
| throw new SQLFeatureNotSupportedException( |
| "Connection.commit() is not supported. (Drill is not transactional.)"); |
| } |
| } |
| |
| @Override |
| public void rollback() throws SQLException { |
| checkOpen(); |
| if (getAutoCommit()) { |
| throw new JdbcApiSqlException("Can't call rollback() in auto-commit mode."); |
| } else { |
| // (Currently not reachable.) |
| throw new SQLFeatureNotSupportedException( |
| "Connection.rollback() is not supported. (Drill is not transactional.)"); |
| } |
| } |
| |
| |
| @Override |
| public boolean isClosed() { |
| try { |
| return super.isClosed(); |
| } catch (SQLException e) { |
| // Currently can't happen, since AvaticaConnection.isClosed() never throws |
| // SQLException. |
| throw new DrillRuntimeException( |
| "Unexpected exception from " + getClass().getSuperclass() |
| + ".isClosed(): " + e, |
| e ); |
| } |
| } |
| |
| |
| @Override |
| public Savepoint setSavepoint() throws SQLException { |
| checkOpen(); |
| throw new SQLFeatureNotSupportedException( |
| "Savepoints are not supported. (Drill is not transactional.)" ); |
| } |
| |
| @Override |
| public Savepoint setSavepoint(String name) throws SQLException { |
| checkOpen(); |
| throw new SQLFeatureNotSupportedException( |
| "Savepoints are not supported. (Drill is not transactional.)" ); |
| } |
| |
| @Override |
| public void rollback(Savepoint savepoint) throws SQLException { |
| checkOpen(); |
| throw new SQLFeatureNotSupportedException( |
| "Savepoints are not supported. (Drill is not transactional.)" ); |
| } |
| |
| @Override |
| public void releaseSavepoint(Savepoint savepoint) throws SQLException { |
| checkOpen(); |
| throw new SQLFeatureNotSupportedException( |
| "Savepoints are not supported. (Drill is not transactional.)" ); |
| } |
| |
| |
| private String isolationValueToString(int level) { |
| switch (level) { |
| case TRANSACTION_NONE: |
| return "TRANSACTION_NONE"; |
| case TRANSACTION_READ_UNCOMMITTED: |
| return "TRANSACTION_READ_UNCOMMITTED"; |
| case TRANSACTION_READ_COMMITTED: |
| return "TRANSACTION_READ_COMMITTED"; |
| case TRANSACTION_REPEATABLE_READ: |
| return "TRANSACTION_REPEATABLE_READ"; |
| case TRANSACTION_SERIALIZABLE: |
| return "TRANSACTION_SERIALIZABLE"; |
| default: |
| return "<Unknown transaction isolation level value " + level + ">"; |
| } |
| } |
| |
| @Override |
| public void setTransactionIsolation(int level) throws SQLException { |
| checkOpen(); |
| switch (level) { |
| case TRANSACTION_NONE: |
| // No-op. (Is already set in constructor, and we disallow changing it.) |
| break; |
| case TRANSACTION_READ_UNCOMMITTED: |
| case TRANSACTION_READ_COMMITTED: |
| case TRANSACTION_REPEATABLE_READ: |
| case TRANSACTION_SERIALIZABLE: |
| throw new SQLFeatureNotSupportedException( |
| "Can't change transaction isolation level to Connection." |
| + isolationValueToString(level) + " (from Connection." |
| + isolationValueToString(getTransactionIsolation()) + "). " |
| + "(Drill is not transactional.)"); |
| default: |
| // Invalid value (or new one unknown to code). |
| throw new JdbcApiSqlException( |
| "Invalid transaction isolation level value " + level); |
| //break; |
| } |
| } |
| |
| @Override |
| public void setNetworkTimeout(Executor executor, int milliseconds) |
| throws JdbcApiSqlException, SQLFeatureNotSupportedException { |
| checkOpen(); |
| if (null == executor) { |
| throw new InvalidParameterSqlException( |
| "Invalid (null) \"executor\" parameter to setNetworkTimeout(...)"); |
| } else if (milliseconds < 0) { |
| throw new InvalidParameterSqlException( |
| "Invalid (negative) \"milliseconds\" parameter to" |
| + " setNetworkTimeout(...) (" + milliseconds + ")"); |
| } else { |
| if (0 != milliseconds) { |
| throw new SQLFeatureNotSupportedException( |
| "Setting network timeout is not supported."); |
| } |
| } |
| } |
| |
| @Override |
| public int getNetworkTimeout() throws AlreadyClosedSqlException { |
| checkOpen(); |
| return 0; // (No timeout.) |
| } |
| |
| @Override |
| public DrillStatementImpl createStatement(int resultSetType, |
| int resultSetConcurrency, |
| int resultSetHoldability) throws SQLException { |
| return (DrillStatementImpl) super.createStatement(resultSetType, |
| resultSetConcurrency, resultSetHoldability); |
| } |
| |
| @Override |
| public TimeZone getTimeZone() { |
| return config.getTimeZone(); |
| } |
| |
| |
| // Note: Using dynamic proxies would reduce the quantity (450?) of method |
| // overrides by eliminating those that exist solely to check whether the |
| // object is closed. It would also eliminate the need to throw non-compliant |
| // RuntimeExceptions when Avatica's method declarations won't let us throw |
| // proper SQLExceptions. (Check performance before applying to frequently |
| // called ResultSet.) |
| |
| // No isWrapperFor(Class<?>) (it doesn't throw SQLException if already closed). |
| // No unwrap(Class<T>) (it doesn't throw SQLException if already closed). |
| |
| @Override |
| public CallableStatement prepareCall(String sql) throws SQLException { |
| checkOpen(); |
| try { |
| return super.prepareCall(sql); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public String nativeSQL(String sql) throws SQLException { |
| checkOpen(); |
| try { |
| return super.nativeSQL(sql); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| // No close() (it doesn't throw SQLException if already closed). |
| |
| @Override |
| public CallableStatement prepareCall(String sql, int resultSetType, |
| int resultSetConcurrency) throws SQLException { |
| checkOpen(); |
| try { |
| return super.prepareCall(sql, resultSetType, resultSetConcurrency); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public Map<String,Class<?>> getTypeMap() throws SQLException { |
| checkOpen(); |
| try { |
| return super.getTypeMap(); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public void setTypeMap(Map<String,Class<?>> map) throws SQLException { |
| checkOpen(); |
| try { |
| super.setTypeMap(map); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public CallableStatement prepareCall(String sql, int resultSetType, |
| int resultSetConcurrency, |
| int resultSetHoldability) throws SQLException { |
| checkOpen(); |
| try { |
| return super.prepareCall(sql, resultSetType, resultSetConcurrency, |
| resultSetHoldability); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(String sql, |
| int autoGeneratedKeys) throws SQLException { |
| checkOpen(); |
| try { |
| return super.prepareStatement(sql, autoGeneratedKeys); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(String sql, |
| int columnIndexes[]) throws SQLException { |
| checkOpen(); |
| try { |
| return super.prepareStatement(sql, columnIndexes); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(String sql, |
| String columnNames[]) throws SQLException { |
| checkOpen(); |
| try { |
| return super.prepareStatement(sql, columnNames); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public Clob createClob() throws SQLException { |
| checkOpen(); |
| try { |
| return super.createClob(); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public Blob createBlob() throws SQLException { |
| checkOpen(); |
| try { |
| return super.createBlob(); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public NClob createNClob() throws SQLException { |
| checkOpen(); |
| try { |
| return super.createNClob(); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public SQLXML createSQLXML() throws SQLException { |
| checkOpen(); |
| try { |
| return super.createSQLXML(); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public boolean isValid(int timeout) throws SQLException { |
| checkOpen(); |
| return super.isValid(timeout); |
| } |
| |
| @Override |
| public void setClientInfo(String name, String value) throws SQLClientInfoException { |
| try { |
| checkOpen(); |
| } catch (AlreadyClosedSqlException e) { |
| throw new SQLClientInfoException(e.getMessage(), null, e); |
| } |
| try { |
| super.setClientInfo(name, value); |
| } catch (UnsupportedOperationException e) { |
| SQLFeatureNotSupportedException intended = |
| new SQLFeatureNotSupportedException(e.getMessage(), e); |
| throw new SQLClientInfoException(e.getMessage(), null, intended); |
| } |
| } |
| |
| @Override |
| public void setClientInfo(Properties properties) throws SQLClientInfoException { |
| try { |
| checkOpen(); |
| } catch (AlreadyClosedSqlException e) { |
| throw new SQLClientInfoException(e.getMessage(), null, e); |
| } |
| try { |
| super.setClientInfo(properties); |
| } catch (UnsupportedOperationException e) { |
| SQLFeatureNotSupportedException intended = |
| new SQLFeatureNotSupportedException(e.getMessage(), e); |
| throw new SQLClientInfoException(e.getMessage(), null, intended); |
| } |
| } |
| |
| @Override |
| public Struct createStruct(String typeName, Object[] attributes) throws SQLException { |
| checkOpen(); |
| try { |
| return super.createStruct(typeName, attributes); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| @Override |
| public void setSchema(String schema) throws SQLException { |
| checkOpen(); |
| try { |
| client.runQuery(UserBitShared.QueryType.SQL, String.format("use %s", schema)); |
| } catch (RpcException e) { |
| throw new SQLException("Error when setting schema", e); |
| } |
| } |
| |
| @Override |
| public String getSchema() throws SQLException { |
| checkOpen(); |
| try { |
| UserProtos.GetServerMetaResp response = client.getServerMeta().get(); |
| if (response.getStatus() != UserProtos.RequestStatus.OK) { |
| UserBitShared.DrillPBError drillError = response.getError(); |
| throw new SQLException("Error when getting server meta: " + drillError.getMessage()); |
| } |
| UserProtos.ServerMeta serverMeta = response.getServerMeta(); |
| String currentSchema = serverMeta.hasCurrentSchema() ? serverMeta.getCurrentSchema() : null; |
| return StringUtils.isEmpty(currentSchema) ? null : currentSchema; |
| } catch (InterruptedException | ExecutionException e) { |
| throw new SQLException("Error when getting server meta", e); |
| } |
| } |
| |
| @Override |
| public void abort(Executor executor) throws SQLException { |
| checkOpen(); |
| try { |
| super.abort(executor); |
| } catch (UnsupportedOperationException e) { |
| throw new SQLFeatureNotSupportedException(e.getMessage(), e); |
| } |
| } |
| |
| // do not make public |
| UnregisteredDriver getDriver() { |
| return driver; |
| } |
| |
| // do not make public |
| AvaticaFactory getFactory() { |
| return factory; |
| } |
| |
| private static void closeOrWarn(AutoCloseable autoCloseable, String message, Logger logger) { |
| if (autoCloseable == null) { |
| return; |
| } |
| |
| try { |
| autoCloseable.close(); |
| } catch(Exception e) { |
| logger.warn(message, e); |
| } |
| } |
| |
| // TODO this should be an AutoCloseable, and this should be close() |
| void cleanup() { |
| // First close any open JDBC Statement objects, to close any open ResultSet |
| // objects and release their buffers/vectors. |
| openStatementsRegistry.close(); |
| |
| // TODO all of these should use DeferredException when it is available from DRILL-2245 |
| closeOrWarn(client, "Exception while closing client.", logger); |
| closeOrWarn(allocator, "Exception while closing allocator.", logger); |
| |
| if (bit != null) { |
| bit.close(); |
| } |
| |
| closeOrWarn(serviceSet, "Exception while closing service set.", logger); |
| } |
| |
| // TODO(DRILL-xxxx): Eliminate this test-specific hack from production code. |
| // If we're not going to have tests themselves explicitly handle making names |
| // unique, then at least move this logic into a test base class, and have it |
| // go through DrillConnection.getClient(). |
| /** |
| * Test only code to make JDBC tests run concurrently. If the property <i>drillJDBCUnitTests</i> is set to |
| * <i>true</i> in connection properties: |
| * - Update dfs.tmp workspace location with a temp directory. This temp is for exclusive use for test jvm. |
| * - Update dfs.tmp workspace to immutable, so that test writer don't try to create views in dfs.tmp |
| * @param pluginRegistry |
| */ |
| private static void makeTmpSchemaLocationsUnique(StoragePluginRegistry pluginRegistry, Properties props) { |
| try { |
| if (props != null && "true".equalsIgnoreCase(props.getProperty(UNIT_TEST_PROP_PREFIX))) { |
| final String logMessage = "The {} property was not configured"; |
| |
| final String dfsTmpPath = props.getProperty(UNIT_TEST_DFS_TMP_PROP); |
| final String dfsRootPath = props.getProperty(UNIT_TEST_DFS_ROOT_PROP); |
| final String dfsDefaultPath = props.getProperty(UNIT_TEST_DFS_DEFAULT_PROP); |
| |
| if (dfsTmpPath == null) { |
| logger.warn(logMessage, UNIT_TEST_DFS_TMP_PROP); |
| } else { |
| updateSchemaLocation(DFS_PLUGIN_NAME, pluginRegistry, new File(dfsTmpPath), TMP_SCHEMA); |
| } |
| |
| if (dfsRootPath == null) { |
| logger.warn(logMessage, UNIT_TEST_DFS_ROOT_PROP); |
| } else { |
| updateSchemaLocation(DFS_PLUGIN_NAME, pluginRegistry, new File(dfsRootPath), ROOT_SCHEMA); |
| } |
| |
| if (dfsDefaultPath == null) { |
| logger.warn(logMessage, UNIT_TEST_DFS_DEFAULT_PROP); |
| } else { |
| updateSchemaLocation(DFS_PLUGIN_NAME, pluginRegistry, new File(dfsDefaultPath), SchemaFactory.DEFAULT_WS_NAME); |
| } |
| } |
| } catch(Throwable e) { |
| // Reason for catching Throwable is to capture NoSuchMethodError etc which depend on certain classed to be |
| // present in classpath which may not be available when just using the standalone JDBC. This is unlikely to |
| // happen, but just a safeguard to avoid failing user applications. |
| logger.warn("Failed to update tmp schema locations. This step is purely for testing purpose. " + |
| "Shouldn't be seen in production code."); |
| // Ignore the error and go with defaults |
| } |
| } |
| } |