Skip to content

Commit

Permalink
Merge pull request #4064 from stefanuhrig/hana_sql_hint
Browse files Browse the repository at this point in the history
[GEOT-7230] Implement a HINT parameter as part of the HANA plug-in
  • Loading branch information
stefanuhrig committed Oct 21, 2022
2 parents 471142b + ffc51f5 commit e61ed86
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 32 deletions.
48 changes: 30 additions & 18 deletions docs/user/library/jdbc/hana.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ HANA Plugin

Supports direct access to a HANA database.

A free version of HANA (HANA Express Edition) can be downloaded at the link below.
A free version of HANA (HANA Express Edition) can be downloaded at the link
below.

You need HANA's JDBC driver ``ngdbc.jar`` to connect to HANA. Its license does not allow redistribution, so you have to download it separately. It can be downloaded from SAP's Development Tools site and is also part of HANA Express Edition.
You need HANA's JDBC driver ``ngdbc.jar`` to connect to HANA. Its license does
not allow redistribution, so you have to download it separately. It can be
downloaded from SAP's Development Tools site and is also part of HANA Express
Edition.

**References**

Expand All @@ -14,7 +18,8 @@ You need HANA's JDBC driver ``ngdbc.jar`` to connect to HANA. Its license does n

**Maven**

Note that the ``groupId`` is ``org.geotools.jdbc`` for this and other JDBC plugin modules.
Note that the ``groupId`` is ``org.geotools.jdbc`` for this and other JDBC
plugin modules.

::

Expand All @@ -27,19 +32,25 @@ Note that the ``groupId`` is ``org.geotools.jdbc`` for this and other JDBC plugi
Connection Parameters
^^^^^^^^^^^^^^^^^^^^^

============== ============================================
Parameter Description
============== ============================================
================ ===============================================================
Parameter Description
================ ===============================================================
``dbtype`` Must be the string ``hana``
``host`` Machine name or IP address to connect to
``port`` Port to connect to. If set and different from 0, parameters "instance" and "database" are ignored. If not set or 0, the "instance" parameter must be set.
``port`` Port to connect to. If set and different from 0, parameters
``instance`` and ``database`` are ignored. If not set or 0, the
``instance`` parameter must be set.
``instance`` Instance of the database
``database`` Database to connect to. Leave empty in case of single-container databases. Set to ``SYSTEMDB`` to connect to the system database of a multi-container database.
``database`` Database to connect to. Leave empty in case of single-container
databases. Set to ``SYSTEMDB`` to connect to the system
database of a multi-container database.
``schema`` The database schema to access
``user`` User name
``passwd`` Password
``use ssl`` Use SSL to connect
============== ============================================
``SELECT Hints`` Comma-separated list of hints that will be applied to SELECT
queries, e.g. ``ESTIMATION_SAMPLES(0), NO_HASH_JOIN``.
================ ===============================================================

Creating
^^^^^^^^
Expand All @@ -54,24 +65,25 @@ Here is an example of connecting::
params.put("schema", "geotools"); //the table schema
params.put("user", "SYSTEM"); //the user to connect with
params.put("passwd", "pw"); //the password of the user

DataStore datastore = DataStoreFinder.getDataStore(params);

Advanced GeoTools Parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

+----------------------+-------------------------------------------+
| Parameter | Description |
+======================+===========================================+
| ``encode functions`` | Flag controlling if a set of filter |
| | functions are translated directly in SQL. |
| | Default is false. |
+----------------------+-------------------------------------------+
==================== ===========================================================
Parameter Description
==================== ===========================================================
``encode functions`` Flag controlling if a set of filter functions are
translated directly in SQL. Default is false.
==================== ===========================================================

Importing spatial reference systems
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

HANA includes only a few spatial reference systems by default. Therefore, the plugin contains an application for installing all EPSG spatial reference systems.
HANA includes only a few spatial reference systems by default. Therefore, the
plugin contains an application for installing all EPSG spatial reference
systems.

On Windows::

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.geotools.data.DataAccessFactory.Param;
import org.geotools.data.Parameter;
import org.geotools.jdbc.JDBCDataStore;
import org.geotools.jdbc.JDBCDataStoreFactory;
Expand All @@ -32,19 +34,38 @@
*/
public class HanaDataStoreFactory extends JDBCDataStoreFactory {

private static final String DATABASE_ID = "hana";

public static final Param DBTYPE =
new Param(
"dbtype",
String.class,
"Type",
true,
"hana",
DATABASE_ID,
Collections.singletonMap(Parameter.LEVEL, "program"));

public static final Param PORT = new Param("port", Integer.class, "Port", false);
public static final Param PORT =
new Param(
"port",
Integer.class,
"Port to connect to. If omitted, you have to specify an instance.",
false);

public static final Param INSTANCE =
new Param("instance", Integer.class, "Instance Number", false);
new Param(
"instance",
Integer.class,
"Instance Number. Leave empty if you have specified a port.",
false);

public static final Param DATABASE =
new Param(
"database",
String.class,
"Database. Leave empty if you have specified a port or if you want to connect in single database mode. "
+ "Use SYSTEMDB for the system database. ",
false);

public static final Param USE_SSL = new Param("use ssl", Boolean.class, "Use SSL", false);

Expand All @@ -61,6 +82,15 @@ public class HanaDataStoreFactory extends JDBCDataStoreFactory {
Boolean.FALSE,
Collections.singletonMap(Param.LEVEL, "advanced"));

public static final Param SELECT_HINTS =
new Param(
"SELECT Hints",
String.class,
"Comma-separated list of hints that will be applied to SELECT queries, e.g. ESTIMATION_SAMPLES(0), NO_HASH_JOIN",
false,
null,
Collections.singletonMap(Parameter.IS_LARGE_TEXT, Boolean.TRUE));

private static final String DESCRIPTION = "SAP HANA";

private static final String DRIVER_CLASS_NAME = "com.sap.db.jdbc.Driver";
Expand All @@ -72,7 +102,7 @@ public String getDescription() {

@Override
protected String getDatabaseID() {
return (String) DBTYPE.sample;
return DATABASE_ID;
}

@Override
Expand All @@ -92,13 +122,32 @@ protected String getValidationQuery() {

@Override
protected void setupParameters(Map<String, Object> parameters) {
super.setupParameters(parameters);

parameters.put(DBTYPE.key, DBTYPE);
parameters.put(PORT.key, PORT);
parameters.put(INSTANCE.key, INSTANCE);
parameters.put(USE_SSL.key, USE_SSL);
parameters.put(ENCODE_FUNCTIONS.key, ENCODE_FUNCTIONS);
LinkedHashMap<String, Object> parentParams = new LinkedHashMap<>();
super.setupParameters(parentParams);

// Replace dbtype because the program level annotation is missing
parentParams.put(DBTYPE.key, DBTYPE);

// Replace port parameter as it is not required for HANA
parentParams.put(PORT.key, PORT);

// Replace database parameter to add additional documentation
parentParams.put(DATABASE.key, DATABASE);

// Insert additional parameters at the proper place
for (Map.Entry<String, Object> param : parentParams.entrySet()) {
parameters.put(param.getKey(), param.getValue());
if (PORT.key.equals(param.getKey())) {
parameters.put(INSTANCE.key, INSTANCE);
}
if (DATABASE.key.equals(param.getKey())) {
parameters.put(USE_SSL.key, USE_SSL);
}
if (EXPOSE_PK.key.equals(param.getKey())) {
parameters.put(ENCODE_FUNCTIONS.key, ENCODE_FUNCTIONS);
parameters.put(SELECT_HINTS.key, SELECT_HINTS);
}
}
}

@Override
Expand Down Expand Up @@ -133,6 +182,8 @@ protected JDBCDataStore createDataStoreInternal(JDBCDataStore dataStore, Map<Str
HanaDialect dialect = (HanaDialect) dataStore.getSQLDialect();
Boolean encodeFunctions = (Boolean) ENCODE_FUNCTIONS.lookUp(params);
dialect.setFunctionEncodingEnabled((encodeFunctions != null) && encodeFunctions);
String selectHints = (String) SELECT_HINTS.lookUp(params);
dialect.setSelectHints(selectHints);
return dataStore;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ public HanaDialect(JDBCDataStore dataStore) {

private boolean functionEncodingEnabled;

private String selectHints;

private HanaVersion hanaVersion;

private SchemaCache currentSchemaCache = new SchemaCache();
Expand All @@ -156,6 +158,10 @@ public void setFunctionEncodingEnabled(boolean enabled) {
functionEncodingEnabled = enabled;
}

public void setSelectHints(String selectHints) {
this.selectHints = selectHints;
}

@Override
public void initializeConnection(Connection cx) throws SQLException {
super.initializeConnection(cx);
Expand Down Expand Up @@ -811,7 +817,12 @@ protected boolean supportsSchemaForIndex() {

@Override
public void handleSelectHints(StringBuffer sql, SimpleFeatureType featureType, Query query) {
// TODO Maybe apply estimation samples hint
if ((selectHints == null) || selectHints.trim().isEmpty()) {
return;
}
sql.append(" WITH HINT( ");
sql.append(selectHints);
sql.append(" )");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.geotools.data.hana.HanaDataStoreFactory.ENCODE_FUNCTIONS;

import java.util.LinkedHashMap;
import java.util.Map;
import org.geotools.jdbc.JDBCJNDIDataStoreFactory;

Expand All @@ -35,7 +36,17 @@ public HanaJNDIDataStoreFactory() {
@Override
@SuppressWarnings("unchecked")
protected void setupParameters(Map parameters) {
super.setupParameters(parameters);
parameters.put(ENCODE_FUNCTIONS.key, ENCODE_FUNCTIONS);
LinkedHashMap<String, Object> parentParams = new LinkedHashMap<>();
super.setupParameters(parentParams);

// Insert additional parameters at the proper place
for (Map.Entry<String, Object> param : parentParams.entrySet()) {
parameters.put(param.getKey(), param.getValue());
if (EXPOSE_PK.key.equals(param.getKey())) {
parameters.put(ENCODE_FUNCTIONS.key, ENCODE_FUNCTIONS);
parameters.put(
HanaDataStoreFactory.SELECT_HINTS.key, HanaDataStoreFactory.SELECT_HINTS);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.geotools.data.hana;

import static org.junit.Assert.assertEquals;

import java.util.Map;
import org.geotools.jdbc.JDBCTestSetup;
import org.geotools.jdbc.JDBCTestSupport;
import org.geotools.jdbc.SQLDialect;
import org.junit.Test;

public class HanaSelectHintOnlineTest extends JDBCTestSupport {

@Override
protected JDBCTestSetup createTestSetup() {
return new HanaTestSetupPSPooling();
}

@Override
protected Map<String, Object> createDataStoreFactoryParams() throws Exception {
Map<String, Object> params = super.createDataStoreFactoryParams();
params.put("SELECT Hints", "MYHINT1, MYHINT2");
return params;
}

@Test
public void testSelectHint() throws Exception {
SQLDialect dialect = dataStore.getSQLDialect();
StringBuffer sql = new StringBuffer();
dialect.handleSelectHints(sql, null, null);
assertEquals(" WITH HINT( MYHINT1, MYHINT2 )", sql.toString());
}
}

0 comments on commit e61ed86

Please sign in to comment.