/**
* 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.InputStream;
import java.io.Reader;
import java.sql.NClob;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLTimeoutException;
import java.sql.SQLXML;
import java.util.Properties;
import java.util.TimeZone;
import org.apache.calcite.avatica.AvaticaConnection;
import org.apache.calcite.avatica.AvaticaStatement;
import org.apache.calcite.avatica.Helper;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.Meta.StatementHandle;
import org.apache.drill.exec.client.DrillClient;
import org.apache.drill.exec.client.ServerMethod;
import org.apache.drill.exec.proto.UserProtos.CreatePreparedStatementResp;
import org.apache.drill.exec.proto.UserProtos.RequestStatus;
import org.apache.drill.exec.rpc.DrillRpcFuture;
/**
* Implementation of {@link net.hydromatic.avatica.AvaticaFactory} for Drill and
* JDBC 4.1 (corresponds to JDK 1.7).
*/
// Note: Must be public so net.hydromatic.avatica.UnregisteredDriver can
// (reflectively) call no-args constructor.
public class DrillJdbc41Factory extends DrillFactory {
private static final org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(DrillJdbc41Factory.class);
/** Creates a factory for JDBC version 4.1. */
// Note: Must be public so net.hydromatic.avatica.UnregisteredDriver can
// (reflectively) call this constructor.
public DrillJdbc41Factory() {
this(4, 1);
}
/** Creates a JDBC factory with given major/minor version number. */
protected DrillJdbc41Factory(int major, int minor) {
super(major, minor);
}
@Override
DrillConnectionImpl newDrillConnection(DriverImpl driver,
DrillFactory factory,
String url,
Properties info) throws SQLException {
return new DrillConnectionImpl(driver, factory, url, info);
}
@Override
public DrillDatabaseMetaDataImpl newDatabaseMetaData(AvaticaConnection connection) {
return new DrillDatabaseMetaDataImpl((DrillConnectionImpl) connection);
}
@Override
public DrillStatementImpl newStatement(AvaticaConnection connection,
StatementHandle h,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability) {
return new DrillStatementImpl((DrillConnectionImpl) connection,
h,
resultSetType,
resultSetConcurrency,
resultSetHoldability);
}
@Override
public DrillJdbc41PreparedStatement newPreparedStatement(AvaticaConnection connection,
StatementHandle h,
Meta.Signature signature,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throws SQLException {
DrillConnectionImpl drillConnection = (DrillConnectionImpl) connection;
DrillClient client = drillConnection.getClient();
if (drillConnection.getConfig().isServerPreparedStatementDisabled() || !client.getSupportedMethods().contains(ServerMethod.PREPARED_STATEMENT)) {
// fallback to client side prepared statement
return new DrillJdbc41PreparedStatement(drillConnection, h, signature, null, resultSetType, resultSetConcurrency, resultSetHoldability);
}
return newServerPreparedStatement(drillConnection, h, signature, resultSetType,
resultSetConcurrency, resultSetHoldability);
}
private DrillJdbc41PreparedStatement newServerPreparedStatement(DrillConnectionImpl connection,
StatementHandle h,
Meta.Signature signature,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability
) throws SQLException {
String sql = signature.sql;
try {
DrillRpcFuture<CreatePreparedStatementResp> respFuture = connection.getClient().createPreparedStatement(signature.sql);
CreatePreparedStatementResp resp;
try {
resp = respFuture.get();
} catch (InterruptedException e) {
// Preserve evidence that the interruption occurred so that code higher up
// on the call stack can learn of the interruption and respond to it if it
// wants to.
Thread.currentThread().interrupt();
throw new SQLException( "Interrupted", e );
}
final RequestStatus status = resp.getStatus();
if (status != RequestStatus.OK) {
final String errMsgFromServer = resp.getError() != null ? resp.getError().getMessage() : "";
if (status == RequestStatus.TIMEOUT) {
logger.error("Request timed out to create prepare statement: {}", errMsgFromServer);
throw new SQLTimeoutException("Failed to create prepared statement: " + errMsgFromServer);
}
if (status == RequestStatus.FAILED) {
logger.error("Failed to create prepared statement: {}", errMsgFromServer);
throw new SQLException("Failed to create prepared statement: " + errMsgFromServer);
}
logger.error("Failed to create prepared statement. Unknown status: {}, Error: {}", status, errMsgFromServer);
throw new SQLException(String.format(
"Failed to create prepared statement. Unknown status: %s, Error: %s", status, errMsgFromServer));
}
return new DrillJdbc41PreparedStatement(connection,
h,
signature,
resp.getPreparedStatement(),
resultSetType,
resultSetConcurrency,
resultSetHoldability);
} catch (SQLException e) {
throw e;
} catch (RuntimeException e) {
throw Helper.INSTANCE.createException("Error while preparing statement [" + sql + "]", e);
} catch (Exception e) {
throw Helper.INSTANCE.createException("Error while preparing statement [" + sql + "]", e);
}
}
@Override
public DrillResultSetImpl newResultSet(AvaticaStatement statement,
Meta.Signature signature,
TimeZone timeZone,
Meta.Frame firstFrame) {
final ResultSetMetaData metaData =
newResultSetMetaData(statement, signature);
return new DrillResultSetImpl(statement, signature, metaData, timeZone, firstFrame);
}
@Override
public ResultSetMetaData newResultSetMetaData(AvaticaStatement statement,
Meta.Signature signature) {
return new DrillResultSetMetaDataImpl(statement, null, signature);
}
/**
* JDBC 4.1 version of {@link DrillPreparedStatementImpl}.
*/
private static class DrillJdbc41PreparedStatement extends DrillPreparedStatementImpl {
DrillJdbc41PreparedStatement(DrillConnectionImpl connection,
StatementHandle h,
Meta.Signature signature,
org.apache.drill.exec.proto.UserProtos.PreparedStatement pstmt,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
super(connection, h, signature, pstmt,
resultSetType, resultSetConcurrency, resultSetHoldability);
}
// These don't need throwIfClosed(), since getParameter already calls it.
@Override
public void setRowId(int parameterIndex, RowId x) throws SQLException {
getSite(parameterIndex).setRowId(x);
}
@Override
public void setNString(int parameterIndex, String value) throws SQLException {
getSite(parameterIndex).setNString(value);
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value,
long length) throws SQLException {
getSite(parameterIndex).setNCharacterStream(value, length);
}
@Override
public void setNClob(int parameterIndex, NClob value) throws SQLException {
getSite(parameterIndex).setNClob(value);
}
@Override
public void setClob(int parameterIndex, Reader reader,
long length) throws SQLException {
getSite(parameterIndex).setClob(reader, length);
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream,
long length) throws SQLException {
getSite(parameterIndex).setBlob(inputStream, length);
}
@Override
public void setNClob(int parameterIndex, Reader reader,
long length) throws SQLException {
getSite(parameterIndex).setNClob(reader, length);
}
@Override
public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
getSite(parameterIndex).setSQLXML(xmlObject);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x,
long length) throws SQLException {
getSite(parameterIndex).setAsciiStream(x, length);
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x,
long length) throws SQLException {
getSite(parameterIndex).setBinaryStream(x, length);
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader,
long length) throws SQLException {
getSite(parameterIndex).setCharacterStream(reader, length);
}
@Override
public void setAsciiStream(int parameterIndex,
InputStream x) throws SQLException {
getSite(parameterIndex).setAsciiStream(x);
}
@Override
public void setBinaryStream(int parameterIndex,
InputStream x) throws SQLException {
getSite(parameterIndex).setBinaryStream(x);
}
@Override
public void setCharacterStream(int parameterIndex,
Reader reader) throws SQLException {
getSite(parameterIndex).setCharacterStream(reader);
}
@Override
public void setNCharacterStream(int parameterIndex,
Reader value) throws SQLException {
getSite(parameterIndex).setNCharacterStream(value);
}
@Override
public void setClob(int parameterIndex, Reader reader) throws SQLException {
getSite(parameterIndex).setClob(reader);
}
@Override
public void setBlob(int parameterIndex,
InputStream inputStream) throws SQLException {
getSite(parameterIndex).setBlob(inputStream);
}
@Override
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
getSite(parameterIndex).setNClob(reader);
}
}
}
// End DrillJdbc41Factory.java