HanaClient.java
/*
* 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.plugin.hana;
import com.facebook.airlift.log.Logger;
import com.facebook.presto.plugin.jdbc.BaseJdbcClient;
import com.facebook.presto.plugin.jdbc.BaseJdbcConfig;
import com.facebook.presto.plugin.jdbc.ConnectionFactory;
import com.facebook.presto.plugin.jdbc.DriverConnectionFactory;
import com.facebook.presto.plugin.jdbc.JdbcColumnHandle;
import com.facebook.presto.plugin.jdbc.JdbcConnectorId;
import com.facebook.presto.plugin.jdbc.JdbcIdentity;
import com.facebook.presto.plugin.jdbc.JdbcTableHandle;
import com.facebook.presto.spi.ConnectorSession;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.SchemaTableName;
import com.google.common.base.Joiner;
import com.sap.db.jdbc.Driver;
import javax.inject.Inject;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import static com.facebook.presto.plugin.jdbc.DriverConnectionFactory.basicConnectionProperties;
import static com.facebook.presto.plugin.jdbc.JdbcErrorCode.JDBC_ERROR;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static java.lang.String.format;
import static java.util.Locale.ENGLISH;
public class HanaClient
extends BaseJdbcClient
{
private static final Logger log = Logger.get(HanaClient.class);
private static final Joiner DOT_JOINER = Joiner.on(".");
@Inject
public HanaClient(JdbcConnectorId connectorId, BaseJdbcConfig config)
throws SQLException
{
super(connectorId, config, "\"", connectionFactory(config));
}
private static ConnectionFactory connectionFactory(BaseJdbcConfig config)
{
Properties connectionProperties = basicConnectionProperties(config);
return new DriverConnectionFactory(
new Driver(),
config.getConnectionUrl(),
Optional.ofNullable(config.getUserCredentialName()),
Optional.ofNullable(config.getPasswordCredentialName()),
connectionProperties);
}
@Override
public void abortReadConnection(Connection connection)
{
try {
// Abort connection before closing. Without this, the Hana driver
// attempts to drain the connection by reading all the results.
connection.abort(directExecutor());
}
catch (SQLException e) {
log.error("Encountered exception when aborting the connection", e);
}
}
@Override
public PreparedStatement getPreparedStatement(ConnectorSession session, Connection connection, String sql)
throws SQLException
{
PreparedStatement statement = connection.prepareStatement(sql);
if (statement.isWrapperFor(Statement.class)) {
statement.unwrap(Statement.class);
}
return statement;
}
@Override
public void renameColumn(ConnectorSession session, JdbcIdentity identity, JdbcTableHandle handle, JdbcColumnHandle jdbcColumn, String newColumnName)
{
try (Connection connection = connectionFactory.openConnection(identity)) {
DatabaseMetaData metadata = connection.getMetaData();
if (metadata.storesUpperCaseIdentifiers()) {
newColumnName = newColumnName.toUpperCase(ENGLISH);
}
String sql = format(
"RENAME COLUMN %s TO %s",
singleQuote(handle.getSchemaName(), handle.getTableName(), jdbcColumn.getColumnName()),
singleQuote(newColumnName));
execute(connection, sql);
}
catch (SQLException e) {
// Hana versions earlier than 8 do not support the above RENAME COLUMN syntax
throw new PrestoException(JDBC_ERROR, e);
}
}
@Override
protected void renameTable(JdbcIdentity identity, String catalogName, SchemaTableName oldTable, SchemaTableName newTable)
{
try (Connection connection = connectionFactory.openConnection(identity)) {
String sql = format(
"RENAME TABLE %s TO %s",
singleQuote(oldTable.getSchemaName(), oldTable.getTableName()),
singleQuote(newTable.getSchemaName(), newTable.getTableName()));
execute(connection, sql);
}
catch (SQLException e) {
throw new PrestoException(JDBC_ERROR, e);
}
}
private static String singleQuote(String... objects)
{
List<String> quotedObjs = new ArrayList<>();
for (String obj : objects) {
String quotedStr = singleQuote(obj);
quotedObjs.add(quotedStr);
}
return DOT_JOINER.join(quotedObjs);
}
private static String singleQuote(String literal)
{
// HANA only accepts upper case
return "\"" + literal.toUpperCase() + "\"";
}
@Override
public String normalizeIdentifier(ConnectorSession session, String identifier)
{
return caseSensitiveNameMatchingEnabled ? identifier : identifier.toLowerCase(ENGLISH);
}
}