diff --git a/pom.xml b/pom.xml
index e26854a90361d..a5d1cd4232efe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -190,6 +190,7 @@
presto-open-telemetry
redis-hbo-provider
presto-singlestore
+ presto-hana
@@ -415,6 +416,12 @@
${project.version}
+
+ com.facebook.presto
+ presto-hana
+ ${project.version}
+
+
com.facebook.presto
presto-bigquery
@@ -1164,6 +1171,12 @@
3.18.1
+
+ com.sap.cloud.db.jdbc
+ ngdbc
+ 2.17.12
+
+
mysql
mysql-connector-java
diff --git a/presto-docs/src/main/sphinx/connector.rst b/presto-docs/src/main/sphinx/connector.rst
index 5b3165489dd9c..f07506b460cab 100644
--- a/presto-docs/src/main/sphinx/connector.rst
+++ b/presto-docs/src/main/sphinx/connector.rst
@@ -17,6 +17,7 @@ from different data sources.
connector/druid
connector/elasticsearch
connector/googlesheets
+ connector/hana
connector/hive
connector/hive-security
connector/hudi
diff --git a/presto-docs/src/main/sphinx/connector/hana.rst b/presto-docs/src/main/sphinx/connector/hana.rst
new file mode 100644
index 0000000000000..bd8f4b939cb15
--- /dev/null
+++ b/presto-docs/src/main/sphinx/connector/hana.rst
@@ -0,0 +1,100 @@
+====================
+HANA Connector
+====================
+
+The HANA connector allows querying and creating tables in an
+external HANA database. This can be used to join data between
+different systems like HANA and Hive, or between two different
+HANA instances.
+
+Configuration
+-------------
+
+To configure the HANA connector, create a catalog properties file
+in ``etc/catalog`` named, for example, ``hana.properties``, to
+mount the HANA connector as the ``hana`` catalog.
+
+Create the file with the following contents, replacing the
+connection properties as appropriate for your setup:
+
+.. code-block:: none
+
+ connector.name=hana
+ connection-url=jdbc:sap://[serverName[\instanceName][:portNumber]]
+ connection-user=root
+ connection-password=secret
+
+Multiple HANA Databases or Servers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The HANA connector can only access a single database within
+a HANA server. If you have multiple HANA databases,
+or want to connect to multiple HANA instances, you must configure
+multiple catalogs, one for each instance.
+
+To add another catalog, add another properties file to ``etc/catalog``
+with a different name (making sure it ends in ``.properties``). For example,
+if you name the property file ``sales.properties``, Presto will create a
+catalog named ``sales`` using the configured connector.
+
+General Configuration Properties
+---------------------------------
+
+================================================== ==================================================================== ===========
+Property Name Description Default
+================================================== ==================================================================== ===========
+``user-credential-name`` Name of the ``extraCredentials`` property whose value is the JDBC
+ driver's user name. See ``extraCredentials`` in `Parameter Reference
+ `_.
+
+``password-credential-name`` Name of the ``extraCredentials`` property whose value is the JDBC
+ driver's user password. See ``extraCredentials`` in `Parameter
+ Reference `_.
+
+``case-insensitive-name-matching`` Match dataset and table names case-insensitively. ``false``
+
+``case-insensitive-name-matching.cache-ttl`` Duration for which remote dataset and table names will be
+ cached. Set to ``0ms`` to disable the cache. ``1m``
+================================================== ==================================================================== ===========
+
+Querying HANA
+-------------------
+
+The HANA connector provides access to all schemas visible to the specified user in the configured database.
+For the following examples, assume the HANA catalog is ``hana``.
+
+You can see the available schemas by running ``SHOW SCHEMAS``::
+
+ SHOW SCHEMAS FROM hana;
+
+If you have a schema named ``web``, you can view the tables
+in this schema by running ``SHOW TABLES``::
+
+ SHOW TABLES FROM hana.web;
+
+You can see a list of the columns in the ``clicks`` table in the ``web`` database
+using either of the following::
+
+ DESCRIBE hana.web.clicks;
+ SHOW COLUMNS FROM hana.web.clicks;
+
+Finally, you can query the ``clicks`` table in the ``web`` schema::
+
+ SELECT * FROM hana.web.clicks;
+
+If you used a different name for your catalog properties file, use
+that catalog name instead of ``hana`` in the above examples.
+
+HANA Connector Limitations
+--------------------------------
+
+The following SQL statements are not supported:
+
+* :doc:`/sql/delete`
+* :doc:`/sql/grant`
+* :doc:`/sql/revoke`
+* :doc:`/sql/show-grants`
+* :doc:`/sql/show-roles`
+* :doc:`/sql/show-role-grants`
\ No newline at end of file
diff --git a/presto-hana/pom.xml b/presto-hana/pom.xml
new file mode 100644
index 0000000000000..e51efbf0479ff
--- /dev/null
+++ b/presto-hana/pom.xml
@@ -0,0 +1,177 @@
+
+
+ 4.0.0
+
+
+ com.facebook.presto
+ presto-root
+ 0.285-SNAPSHOT
+
+
+ presto-hana
+ Presto - HANA Connector
+ presto-plugin
+
+
+ ${project.parent.basedir}
+
+
+
+
+ com.facebook.presto
+ presto-base-jdbc
+
+
+
+ com.facebook.airlift
+ log
+
+
+
+ com.facebook.airlift
+ configuration
+
+
+
+ com.google.guava
+ guava
+
+
+
+ com.google.inject
+ guice
+
+
+
+ com.sap.cloud.db.jdbc
+ ngdbc
+
+
+
+ javax.inject
+ javax.inject
+
+
+
+
+ com.facebook.presto
+ presto-spi
+ provided
+
+
+
+ com.facebook.presto
+ presto-common
+ provided
+
+
+
+ com.facebook.drift
+ drift-api
+ provided
+
+
+
+ io.airlift
+ slice
+ provided
+
+
+
+ io.airlift
+ units
+ provided
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ provided
+
+
+
+
+ com.facebook.presto
+ presto-testng-services
+ test
+
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+ com.facebook.presto
+ presto-testing-docker
+ test
+
+
+
+ org.testng
+ testng
+ test
+
+
+
+ com.facebook.airlift
+ testing
+ test
+
+
+
+ com.facebook.airlift
+ json
+ test
+
+
+
+ com.facebook.presto
+ presto-main
+ test
+
+
+
+ com.facebook.presto
+ presto-tpch
+ test
+
+
+
+ io.airlift.tpch
+ tpch
+ test
+
+
+
+ com.facebook.presto
+ presto-tests
+ test
+
+
+
+ org.jetbrains
+ annotations
+ 19.0.0
+ test
+
+
+
+
+
+ ci
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+
+
+
+
+
+
+
diff --git a/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaClient.java b/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaClient.java
new file mode 100644
index 0000000000000..3873eb729f897
--- /dev/null
+++ b/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaClient.java
@@ -0,0 +1,148 @@
+/*
+ * 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 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() + "\"";
+ }
+}
diff --git a/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaClientModule.java b/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaClientModule.java
new file mode 100644
index 0000000000000..f357c51cb0f93
--- /dev/null
+++ b/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaClientModule.java
@@ -0,0 +1,33 @@
+/*
+ * 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.presto.plugin.jdbc.BaseJdbcConfig;
+import com.facebook.presto.plugin.jdbc.JdbcClient;
+import com.google.inject.Binder;
+import com.google.inject.Module;
+
+import static com.facebook.airlift.configuration.ConfigBinder.configBinder;
+import static com.google.inject.Scopes.SINGLETON;
+
+public class HanaClientModule
+ implements Module
+{
+ @Override
+ public void configure(Binder binder)
+ {
+ binder.bind(JdbcClient.class).to(HanaClient.class).in(SINGLETON);
+ configBinder(binder).bindConfig(BaseJdbcConfig.class);
+ }
+}
diff --git a/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaPlugin.java b/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaPlugin.java
new file mode 100644
index 0000000000000..e55c7a8555828
--- /dev/null
+++ b/presto-hana/src/main/java/com/facebook/presto/plugin/hana/HanaPlugin.java
@@ -0,0 +1,25 @@
+/*
+ * 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.presto.plugin.jdbc.JdbcPlugin;
+
+public class HanaPlugin
+ extends JdbcPlugin
+{
+ public HanaPlugin()
+ {
+ super("hana", new HanaClientModule());
+ }
+}
diff --git a/presto-hana/src/test/java/com/facebook/presto/plugin/hana/TestHanaPlugin.java b/presto-hana/src/test/java/com/facebook/presto/plugin/hana/TestHanaPlugin.java
new file mode 100644
index 0000000000000..a565456fa0ee2
--- /dev/null
+++ b/presto-hana/src/test/java/com/facebook/presto/plugin/hana/TestHanaPlugin.java
@@ -0,0 +1,33 @@
+/*
+ * 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.presto.spi.Plugin;
+import com.facebook.presto.spi.connector.ConnectorFactory;
+import com.facebook.presto.testing.TestingConnectorContext;
+import com.google.common.collect.ImmutableMap;
+import org.testng.annotations.Test;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+public class TestHanaPlugin
+{
+ @Test
+ public void testCreateConnector()
+ {
+ Plugin plugin = new HanaPlugin();
+ ConnectorFactory factory = getOnlyElement(plugin.getConnectorFactories());
+ factory.create("test", ImmutableMap.of("connection-url", "test"), new TestingConnectorContext());
+ }
+}
diff --git a/presto-main/etc/catalog/hana.properties b/presto-main/etc/catalog/hana.properties
new file mode 100644
index 0000000000000..9161bd301eda3
--- /dev/null
+++ b/presto-main/etc/catalog/hana.properties
@@ -0,0 +1,5 @@
+connector.name=hana
+connection-url=jdbc:sap://hana:39015
+connection-user=hana
+connection-password=secret
+allow-drop-table=true
\ No newline at end of file
diff --git a/presto-main/etc/config.properties b/presto-main/etc/config.properties
index 27a332301da12..a120422c4b6f1 100644
--- a/presto-main/etc/config.properties
+++ b/presto-main/etc/config.properties
@@ -38,6 +38,7 @@ plugin.bundles=\
../presto-mysql/pom.xml,\
../presto-singlestore/pom.xml,\
../presto-sqlserver/pom.xml, \
+ ../presto-hana/pom.xml, \
../presto-prometheus/pom.xml, \
../presto-postgresql/pom.xml, \
../presto-tpcds/pom.xml, \
diff --git a/presto-product-tests/conf/presto/etc/config.properties b/presto-product-tests/conf/presto/etc/config.properties
index 54f5e12b4edc7..250863bcdd69d 100644
--- a/presto-product-tests/conf/presto/etc/config.properties
+++ b/presto-product-tests/conf/presto/etc/config.properties
@@ -37,6 +37,7 @@ plugin.bundles=\
../../../presto-mysql/pom.xml,\
../../../presto-postgresql/pom.xml,\
../../../presto-sqlserver/pom.xml,\
+ ../../../presto-hana/pom.xml,\
../../../presto-ml/pom.xml,\
../../../presto-kafka/pom.xml,\
../../../presto-tpcds/pom.xml,\
diff --git a/presto-server/pom.xml b/presto-server/pom.xml
index 2be0a47bfeab1..c886074250c20 100644
--- a/presto-server/pom.xml
+++ b/presto-server/pom.xml
@@ -196,6 +196,14 @@
provided
+
+ com.facebook.presto
+ presto-hana
+ ${project.version}
+ zip
+ provided
+
+
com.facebook.presto
presto-oracle
diff --git a/presto-server/src/main/assembly/presto.xml b/presto-server/src/main/assembly/presto.xml
index 3b93520a556c3..c81bd9e5b10fe 100644
--- a/presto-server/src/main/assembly/presto.xml
+++ b/presto-server/src/main/assembly/presto.xml
@@ -92,6 +92,10 @@
${project.build.directory}/dependency/presto-example-http-${project.version}
plugin/example-http
+
+ ${project.build.directory}/dependency/presto-hana-${project.version}
+ plugin/hana
+
${project.build.directory}/dependency/presto-hive-hadoop2-${project.version}
plugin/hive-hadoop2
diff --git a/presto-spark-package/pom.xml b/presto-spark-package/pom.xml
index 3b25d2395a4e1..5bed5bfd26299 100644
--- a/presto-spark-package/pom.xml
+++ b/presto-spark-package/pom.xml
@@ -85,6 +85,14 @@
provided
+
+ com.facebook.presto
+ presto-hana
+ ${project.version}
+ zip
+ provided
+
+
com.facebook.presto
presto-postgresql
diff --git a/presto-spark-package/src/main/assembly/presto.xml b/presto-spark-package/src/main/assembly/presto.xml
index ed3d738a64843..165bac55826e0 100644
--- a/presto-spark-package/src/main/assembly/presto.xml
+++ b/presto-spark-package/src/main/assembly/presto.xml
@@ -32,6 +32,10 @@
${project.build.directory}/dependency/presto-teradata-functions-${project.version}
plugin/teradata-functions
+
+ ${project.build.directory}/dependency/presto-hana-${project.version}
+ plugin/hana
+
${project.build.directory}/dependency/presto-hive-hadoop2-${project.version}
plugin/hive-hadoop2
diff --git a/presto-test-coverage/pom.xml b/presto-test-coverage/pom.xml
index b924e4c11b0ba..3928f3a756efa 100644
--- a/presto-test-coverage/pom.xml
+++ b/presto-test-coverage/pom.xml
@@ -78,6 +78,13 @@
compile
+
+ com.facebook.presto
+ presto-hana
+ ${project.version}
+ compile
+
+
com.facebook.presto
presto-mongodb