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