LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/hana - ogrhanadatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 676 892 75.8 %
Date: 2024-11-21 22:18:42 Functions: 54 57 94.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  SAP HANA Spatial Driver
       4             :  * Purpose:  OGRHanaDataSource class implementation
       5             :  * Author:   Maxim Rylov
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, SAP SE
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_hana.h"
      14             : #include "ogrhanautils.h"
      15             : #include "ogrhanadrivercore.h"
      16             : 
      17             : #include "odbc/Connection.h"
      18             : #include "odbc/Environment.h"
      19             : #include "odbc/Exception.h"
      20             : #include "odbc/DatabaseMetaData.h"
      21             : #include "odbc/PreparedStatement.h"
      22             : #include "odbc/ResultSet.h"
      23             : #include "odbc/ResultSetMetaData.h"
      24             : #include "odbc/Statement.h"
      25             : #include "odbc/StringConverter.h"
      26             : 
      27             : #include <algorithm>
      28             : #include <iterator>
      29             : #include <memory>
      30             : #include <sstream>
      31             : #include <vector>
      32             : 
      33             : using namespace OGRHANA;
      34             : 
      35             : namespace
      36             : {
      37             : 
      38          65 : CPLString BuildConnectionString(char **openOptions)
      39             : {
      40             :     // See notes for constructing connection string for HANA
      41             :     // https://help.sap.com/docs/SAP_HANA_CLIENT/f1b440ded6144a54ada97ff95dac7adf/7cab593774474f2f8db335710b2f5c50.html
      42             : 
      43         130 :     std::vector<CPLString> params;
      44          65 :     bool isValid = true;
      45         130 :     const CPLString specialChars("[]{}(),;?*=!@");
      46             : 
      47        1051 :     auto getOptValue = [&](const char *optionName, bool mandatory = false)
      48             :     {
      49             :         const char *paramValue =
      50        1051 :             CSLFetchNameValueDef(openOptions, optionName, nullptr);
      51        1051 :         if (mandatory && paramValue == nullptr)
      52             :         {
      53           9 :             isValid = false;
      54           9 :             CPLError(CE_Failure, CPLE_AppDefined,
      55             :                      "Mandatory connection parameter '%s' is missing.",
      56             :                      optionName);
      57             :         }
      58        1051 :         return paramValue;
      59          65 :     };
      60             : 
      61         976 :     auto addParameter = [&](const char *paramName, const char *paramValue)
      62             :     {
      63         976 :         if (paramValue == nullptr)
      64         421 :             return;
      65             : 
      66        1110 :         CPLString value(paramValue);
      67         555 :         if (value.find_first_of(specialChars) != std::string::npos)
      68             :         {
      69          56 :             value.replaceAll("}", "}}");
      70          56 :             params.push_back(CPLString(paramName) + "={" + value + "}");
      71             :         }
      72             :         else
      73             :         {
      74         499 :             params.push_back(CPLString(paramName) + "=" + value);
      75             :         }
      76          65 :     };
      77             : 
      78         720 :     auto addOptParameter = [&](const char *optionName, const char *paramName,
      79             :                                bool mandatory = false)
      80             :     {
      81         720 :         const char *paramValue = getOptValue(optionName, mandatory);
      82         720 :         addParameter(paramName, paramValue);
      83         785 :     };
      84             : 
      85          12 :     auto checkIgnoredOptParameter = [&](const char *optionName)
      86             :     {
      87          12 :         if (getOptValue(optionName))
      88           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      89             :                      "Connection parameter '%s' is ignored in the current "
      90             :                      "combination.",
      91             :                      optionName);
      92          77 :     };
      93             : 
      94          65 :     if (const char *paramUserStoreKey =
      95          65 :             getOptValue(OGRHanaOpenOptionsConstants::USER_STORE_KEY))
      96             :     {
      97           0 :         addOptParameter(OGRHanaOpenOptionsConstants::DRIVER, "DRIVER", true);
      98           0 :         CPLString node = CPLString().Printf("@%s", paramUserStoreKey);
      99           0 :         addParameter("SERVERNODE", node.c_str());
     100             : 
     101           0 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DSN);
     102           0 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::HOST);
     103           0 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PORT);
     104           0 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DATABASE);
     105           0 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::USER);
     106           0 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PASSWORD);
     107             :     }
     108          65 :     else if (const char *paramDSN =
     109          65 :                  getOptValue(OGRHanaOpenOptionsConstants::DSN))
     110             :     {
     111           3 :         addParameter(OGRHanaOpenOptionsConstants::DSN, paramDSN);
     112           3 :         addOptParameter(OGRHanaOpenOptionsConstants::USER, "UID", true);
     113           3 :         addOptParameter(OGRHanaOpenOptionsConstants::PASSWORD, "PWD", true);
     114             : 
     115           3 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DRIVER);
     116           3 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::HOST);
     117           3 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::PORT);
     118           3 :         checkIgnoredOptParameter(OGRHanaOpenOptionsConstants::DATABASE);
     119             :     }
     120             :     else
     121             :     {
     122          62 :         addOptParameter(OGRHanaOpenOptionsConstants::DRIVER, "DRIVER", true);
     123             :         const char *paramHost =
     124          62 :             getOptValue(OGRHanaOpenOptionsConstants::HOST, true);
     125             :         const char *paramPort =
     126          62 :             getOptValue(OGRHanaOpenOptionsConstants::PORT, true);
     127          62 :         if (paramHost != nullptr && paramPort != nullptr)
     128             :         {
     129         120 :             CPLString node = CPLString().Printf("%s:%s", paramHost, paramPort);
     130          60 :             addParameter("SERVERNODE", node.c_str());
     131             :         }
     132          62 :         addOptParameter(OGRHanaOpenOptionsConstants::USER, "UID", true);
     133          62 :         addOptParameter(OGRHanaOpenOptionsConstants::PASSWORD, "PWD", true);
     134          62 :         addOptParameter(OGRHanaOpenOptionsConstants::DATABASE, "DATABASENAME");
     135             :     }
     136             : 
     137          65 :     if (const char *paramSchema =
     138          65 :             getOptValue(OGRHanaOpenOptionsConstants::SCHEMA, true))
     139             :     {
     140         126 :         CPLString schema = CPLString().Printf("\"%s\"", paramSchema);
     141          63 :         addParameter("CURRENTSCHEMA", schema.c_str());
     142             :     }
     143             : 
     144          65 :     if (CPLFetchBool(openOptions, OGRHanaOpenOptionsConstants::ENCRYPT, false))
     145             :     {
     146          56 :         addOptParameter(OGRHanaOpenOptionsConstants::ENCRYPT, "ENCRYPT");
     147          56 :         addOptParameter(OGRHanaOpenOptionsConstants::SSL_CRYPTO_PROVIDER,
     148             :                         "sslCryptoProvider");
     149          56 :         addOptParameter(OGRHanaOpenOptionsConstants::SSL_KEY_STORE,
     150             :                         "sslKeyStore");
     151          56 :         addOptParameter(OGRHanaOpenOptionsConstants::SSL_TRUST_STORE,
     152             :                         "sslTrustStore");
     153          56 :         addOptParameter(OGRHanaOpenOptionsConstants::SSL_VALIDATE_CERTIFICATE,
     154             :                         "sslValidateCertificate");
     155          56 :         addOptParameter(OGRHanaOpenOptionsConstants::SSL_HOST_NAME_CERTIFICATE,
     156             :                         "sslHostNameInCertificate");
     157             :     }
     158             : 
     159          65 :     addOptParameter(OGRHanaOpenOptionsConstants::PACKET_SIZE, "PACKETSIZE");
     160          65 :     addOptParameter(OGRHanaOpenOptionsConstants::SPLIT_BATCH_COMMANDS,
     161             :                     "SPLITBATCHCOMMANDS");
     162          65 :     addParameter("CHAR_AS_UTF8", "1");
     163             : 
     164         130 :     CPLString appName;
     165          65 :     appName.Printf("GDAL %s", GDALVersionInfo("RELEASE_NAME"));
     166          65 :     addParameter("sessionVariable:APPLICATION", appName.c_str());
     167             : 
     168         130 :     return isValid ? JoinStrings(params, ";") : "";
     169             : }
     170             : 
     171          54 : int CPLFetchInt(CSLConstList papszStrList, const char *pszKey, int defaultValue)
     172             : {
     173          54 :     const char *const pszValue = CSLFetchNameValue(papszStrList, pszKey);
     174          54 :     if (pszValue == nullptr)
     175          54 :         return defaultValue;
     176           0 :     return atoi(pszValue);
     177             : }
     178             : 
     179          33 : int GetSrid(odbc::ResultSet &resultSet)
     180             : {
     181          33 :     int srid = UNDETERMINED_SRID;
     182          33 :     while (resultSet.next())
     183             :     {
     184          30 :         odbc::Int val = resultSet.getInt(1);
     185          30 :         if (!val.isNull())
     186             :         {
     187          30 :             srid = *val;
     188          30 :             break;
     189             :         }
     190             :     }
     191          33 :     resultSet.close();
     192          33 :     return srid;
     193             : }
     194             : 
     195          24 : int GetColumnSrid(odbc::Connection &conn, const CPLString &schemaName,
     196             :                   const CPLString &tableName, const CPLString &columnName)
     197             : {
     198             :     CPLString sql =
     199             :         "SELECT SRS_ID FROM SYS.ST_GEOMETRY_COLUMNS WHERE SCHEMA_NAME = ?"
     200          48 :         " AND TABLE_NAME = ? AND COLUMN_NAME = ?";
     201          24 :     odbc::PreparedStatementRef stmt = conn.prepareStatement(sql.c_str());
     202          24 :     stmt->setString(1, odbc::String(schemaName));
     203          24 :     stmt->setString(2, odbc::String(tableName));
     204          24 :     if (columnName != nullptr)
     205          24 :         stmt->setString(3, odbc::String(columnName));
     206          48 :     return GetSrid(*stmt->executeQuery());
     207             : }
     208             : 
     209           9 : int GetColumnSrid(odbc::Connection &conn, const CPLString &query,
     210             :                   const CPLString &columnName)
     211             : {
     212          18 :     CPLString clmName = QuotedIdentifier(columnName);
     213             : 
     214             :     CPLString sql =
     215           9 :         CPLString().Printf("SELECT %s.ST_SRID() FROM (%s) WHERE %s IS NOT NULL",
     216          18 :                            clmName.c_str(), query.c_str(), clmName.c_str());
     217             : 
     218           9 :     odbc::StatementRef stmt = conn.createStatement();
     219          18 :     return GetSrid(*stmt->executeQuery(sql.c_str()));
     220             : }
     221             : 
     222          13 : int GetSridWithFilter(odbc::Connection &conn, const CPLString &whereCondition)
     223             : {
     224          13 :     CPLAssert(whereCondition != nullptr);
     225             : 
     226          13 :     int ret = UNDETERMINED_SRID;
     227             : 
     228          26 :     odbc::StatementRef stmt = conn.createStatement();
     229          13 :     CPLString sql = CPLString().Printf(
     230             :         "SELECT SRS_ID FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE %s",
     231          26 :         whereCondition.c_str());
     232          13 :     odbc::ResultSetRef rsSrs = stmt->executeQuery(sql.c_str());
     233          13 :     while (rsSrs->next())
     234             :     {
     235          13 :         odbc::Int val = rsSrs->getInt(1);
     236          13 :         if (!val.isNull())
     237             :         {
     238          13 :             ret = *val;
     239          13 :             break;
     240             :         }
     241             :     }
     242          13 :     rsSrs->close();
     243             : 
     244          26 :     return ret;
     245             : }
     246             : 
     247          28 : CPLString GetSrsWktById(odbc::Connection &conn, int srid)
     248             : {
     249          28 :     CPLString ret;
     250          28 :     const char *sql = "SELECT DEFINITION FROM "
     251             :                       "SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE SRS_ID = ?";
     252          56 :     odbc::PreparedStatementRef stmt = conn.prepareStatement(sql);
     253          28 :     stmt->setInt(1, odbc::Int(srid));
     254          56 :     odbc::ResultSetRef rs = stmt->executeQuery();
     255          28 :     while (rs->next())
     256             :     {
     257          28 :         odbc::String wkt = rs->getString(1);
     258          28 :         if (!wkt.isNull())
     259             :         {
     260          28 :             ret = *wkt;
     261          28 :             if (!ret.empty())
     262          28 :                 break;
     263             :         }
     264             :     }
     265          28 :     rs->close();
     266             : 
     267          56 :     return ret;
     268             : }
     269             : 
     270          32 : OGRwkbGeometryType GetGeometryType(odbc::Connection &conn,
     271             :                                    const CPLString &query,
     272             :                                    const CPLString &columnName)
     273             : {
     274          64 :     CPLString clmName = QuotedIdentifier(columnName);
     275             : 
     276          32 :     CPLString sql = CPLString().Printf(
     277             :         "SELECT DISTINCT UPPER(%s.ST_GeometryType()), %s.ST_Is3D(), "
     278             :         "%s.ST_IsMeasured() FROM %s WHERE %s IS NOT NULL",
     279             :         clmName.c_str(), clmName.c_str(), clmName.c_str(), query.c_str(),
     280          64 :         clmName.c_str());
     281             : 
     282          64 :     odbc::StatementRef stmt = conn.createStatement();
     283          32 :     odbc::ResultSetRef rsGeomInfo = stmt->executeQuery(sql.c_str());
     284          32 :     OGRwkbGeometryType ret = wkbUnknown;
     285          32 :     std::size_t i = 0;
     286          48 :     while (rsGeomInfo->next())
     287             :     {
     288          16 :         ++i;
     289          16 :         auto typeName = rsGeomInfo->getString(1);
     290          16 :         auto hasZ = rsGeomInfo->getInt(2);
     291          16 :         auto hasM = rsGeomInfo->getInt(3);
     292             :         OGRwkbGeometryType geomType =
     293          16 :             ToWkbType(typeName->c_str(), *hasZ == 1, *hasM == 1);
     294          16 :         if (geomType == OGRwkbGeometryType::wkbUnknown)
     295           0 :             continue;
     296          16 :         if (ret == OGRwkbGeometryType::wkbUnknown)
     297          16 :             ret = geomType;
     298           0 :         else if (ret != geomType)
     299             :         {
     300           0 :             ret = OGRwkbGeometryType::wkbUnknown;
     301           0 :             break;
     302             :         }
     303             :     }
     304          32 :     rsGeomInfo->close();
     305             : 
     306          32 :     if (i == 0)
     307          16 :         ret = OGRwkbGeometryType::wkbUnknown;
     308          64 :     return ret;
     309             : }
     310             : 
     311          24 : GeometryColumnDescription GetGeometryColumnDescription(
     312             :     odbc::Connection &conn, const CPLString &schemaName,
     313             :     const CPLString &tableName, const CPLString &columnName,
     314             :     bool detectGeometryType)
     315             : {
     316             :     OGRwkbGeometryType type =
     317             :         detectGeometryType
     318          47 :             ? GetGeometryType(conn,
     319          46 :                               GetFullTableNameQuoted(schemaName, tableName),
     320             :                               columnName)
     321          24 :             : OGRwkbGeometryType::wkbUnknown;
     322          24 :     int srid = GetColumnSrid(conn, schemaName, tableName, columnName);
     323             : 
     324          24 :     return {columnName, type, srid, false};
     325             : }
     326             : 
     327             : GeometryColumnDescription
     328           9 : GetGeometryColumnDescription(odbc::Connection &conn, const CPLString &query,
     329             :                              const CPLString &columnName,
     330             :                              bool detectGeometryType)
     331             : {
     332             :     // For some queries like this SELECT ST_GeomFROMWKT('POINT(0 0)') FROM DUMMY
     333             :     // we need to have a proper column name.
     334           9 :     bool needColumnName = false;
     335          18 :     std::vector<char> specialChars = {'(', ')', '\'', ' '};
     336          45 :     for (const char c : specialChars)
     337             :     {
     338          36 :         if (columnName.find(c) != CPLString::npos)
     339             :         {
     340           0 :             needColumnName = true;
     341           0 :             break;
     342             :         }
     343             :     }
     344             : 
     345          18 :     CPLString preparedQuery = query;
     346           9 :     CPLString clmName = columnName;
     347           9 :     if (needColumnName)
     348             :     {
     349             :         auto it = std::search(
     350             :             preparedQuery.begin(), preparedQuery.end(), columnName.begin(),
     351             :             columnName.end(),
     352           0 :             [](char ch1, char ch2)
     353             :             {
     354           0 :                 return CPLToupper(static_cast<unsigned char>(ch1)) ==
     355           0 :                        CPLToupper(static_cast<unsigned char>(ch2));
     356           0 :             });
     357             : 
     358           0 :         if (it != preparedQuery.end())
     359             :         {
     360           0 :             auto pos = it - preparedQuery.begin();
     361           0 :             CPLString newName = columnName + " AS \"tmp_geom_field\"";
     362             :             preparedQuery.replace(static_cast<std::size_t>(pos),
     363             :                                   columnName.length(), newName, 0,
     364           0 :                                   newName.length());
     365           0 :             clmName = "tmp_geom_field";
     366             :         }
     367             :     }
     368             : 
     369             :     OGRwkbGeometryType type =
     370             :         detectGeometryType
     371          18 :             ? GetGeometryType(conn, "(" + preparedQuery + ")", clmName)
     372           9 :             : OGRwkbGeometryType::wkbUnknown;
     373           9 :     int srid = GetColumnSrid(conn, preparedQuery, clmName);
     374             : 
     375          18 :     return {columnName, type, srid, false};
     376             : }
     377             : 
     378          12 : CPLString FormatDefaultValue(const char *value, short dataType)
     379             : {
     380             :     /*
     381             :      The values that can be set as default values are :
     382             :        - literal string values enclosed in single-quote characters and properly
     383             :      escaped like: 'Nice weather. Isn''t it ?'
     384             :        - numeric values (unquoted)
     385             :        - reserved keywords (unquoted): CURRENT_TIMESTAMP, CURRENT_DATE,
     386             :      CURRENT_TIME, NULL
     387             :        - datetime literal values enclosed in single-quote characters with the
     388             :      following defined format: ‘YYYY/MM/DD HH:MM:SS[.sss]’
     389             :        - any other driver specific expression. e.g. for SQLite:
     390             :      (strftime(‘%Y-%m-%dT%H:%M:%fZ’,’now’))
     391             :      */
     392             : 
     393          12 :     if (EQUAL(value, "NULL"))
     394           0 :         return value;
     395             : 
     396          12 :     switch (dataType)
     397             :     {
     398           1 :         case QGRHanaDataTypes::Bit:
     399             :         case QGRHanaDataTypes::Boolean:
     400           1 :             return value;
     401           6 :         case QGRHanaDataTypes::TinyInt:
     402             :         case QGRHanaDataTypes::SmallInt:
     403             :         case QGRHanaDataTypes::Integer:
     404             :         case QGRHanaDataTypes::BigInt:
     405             :         case QGRHanaDataTypes::Real:
     406             :         case QGRHanaDataTypes::Float:
     407             :         case QGRHanaDataTypes::Double:
     408             :         case QGRHanaDataTypes::Decimal:
     409             :         case QGRHanaDataTypes::Numeric:
     410           6 :             return value;
     411           1 :         case QGRHanaDataTypes::Char:
     412             :         case QGRHanaDataTypes::VarChar:
     413             :         case QGRHanaDataTypes::LongVarChar:
     414             :         case QGRHanaDataTypes::WChar:
     415             :         case QGRHanaDataTypes::WVarChar:
     416             :         case QGRHanaDataTypes::WLongVarChar:
     417           1 :             return Literal(value);
     418           1 :         case QGRHanaDataTypes::Binary:
     419             :         case QGRHanaDataTypes::VarBinary:
     420             :         case QGRHanaDataTypes::LongVarBinary:
     421             :         case QGRHanaDataTypes::RealVector:
     422           1 :             return value;
     423           1 :         case QGRHanaDataTypes::Date:
     424             :         case QGRHanaDataTypes::TypeDate:
     425           1 :             if (EQUAL(value, "CURRENT_DATE"))
     426           0 :                 return value;
     427           1 :             return Literal(value);
     428           1 :         case QGRHanaDataTypes::Time:
     429             :         case QGRHanaDataTypes::TypeTime:
     430           1 :             if (EQUAL(value, "CURRENT_TIME"))
     431           0 :                 return value;
     432           1 :             return Literal(value);
     433           1 :         case QGRHanaDataTypes::Timestamp:
     434             :         case QGRHanaDataTypes::TypeTimestamp:
     435           1 :             if (EQUAL(value, "CURRENT_TIMESTAMP"))
     436           0 :                 return value;
     437           1 :             return Literal(value);
     438           0 :         default:
     439           0 :             return value;
     440             :     }
     441             : }
     442             : 
     443          11 : short GetArrayDataType(const CPLString &typeName)
     444             : {
     445          11 :     if (typeName == "BOOLEAN ARRAY")
     446           1 :         return QGRHanaDataTypes::Boolean;
     447          10 :     else if (typeName == "TINYINT ARRAY")
     448           0 :         return QGRHanaDataTypes::TinyInt;
     449          10 :     else if (typeName == "SMALLINT ARRAY")
     450           1 :         return QGRHanaDataTypes::SmallInt;
     451           9 :     else if (typeName == "INTEGER ARRAY")
     452           2 :         return QGRHanaDataTypes::Integer;
     453           7 :     else if (typeName == "BIGINT ARRAY")
     454           2 :         return QGRHanaDataTypes::BigInt;
     455           5 :     else if (typeName == "DOUBLE ARRAY")
     456           2 :         return QGRHanaDataTypes::Double;
     457           3 :     else if (typeName == "REAL ARRAY")
     458           0 :         return QGRHanaDataTypes::Float;
     459           3 :     else if (typeName == "DECIMAL ARRAY" || typeName == "SMALLDECIMAL ARRAY")
     460           0 :         return QGRHanaDataTypes::Decimal;
     461           3 :     else if (typeName == "CHAR ARRAY")
     462           0 :         return QGRHanaDataTypes::Char;
     463           3 :     else if (typeName == "VARCHAR ARRAY")
     464           0 :         return QGRHanaDataTypes::VarChar;
     465           3 :     else if (typeName == "NCHAR ARRAY")
     466           0 :         return QGRHanaDataTypes::WChar;
     467           3 :     else if (typeName == "NVARCHAR ARRAY")
     468           3 :         return QGRHanaDataTypes::WVarChar;
     469           0 :     else if (typeName == "DATE ARRAY")
     470           0 :         return QGRHanaDataTypes::Date;
     471           0 :     else if (typeName == "TIME ARRAY")
     472           0 :         return QGRHanaDataTypes::Time;
     473           0 :     else if (typeName == "TIMESTAMP ARRAY" || typeName == "SECONDDATE ARRAY")
     474           0 :         return QGRHanaDataTypes::Timestamp;
     475             : 
     476           0 :     return QGRHanaDataTypes::Unknown;
     477             : }
     478             : 
     479           2 : std::vector<CPLString> GetSupportedArrayTypes()
     480             : {
     481          16 :     return {"TINYINT", "SMALLINT", "INT", "BIGINT", "REAL", "DOUBLE", "STRING"};
     482             : }
     483             : 
     484         188 : bool IsKnownDataType(short dataType)
     485             : {
     486         187 :     return dataType == QGRHanaDataTypes::Bit ||
     487         187 :            dataType == QGRHanaDataTypes::Boolean ||
     488         187 :            dataType == QGRHanaDataTypes::TinyInt ||
     489         185 :            dataType == QGRHanaDataTypes::SmallInt ||
     490         132 :            dataType == QGRHanaDataTypes::Integer ||
     491         108 :            dataType == QGRHanaDataTypes::BigInt ||
     492          88 :            dataType == QGRHanaDataTypes::Double ||
     493          87 :            dataType == QGRHanaDataTypes::Real ||
     494          87 :            dataType == QGRHanaDataTypes::Float ||
     495          84 :            dataType == QGRHanaDataTypes::Decimal ||
     496          84 :            dataType == QGRHanaDataTypes::Numeric ||
     497          84 :            dataType == QGRHanaDataTypes::Char ||
     498          84 :            dataType == QGRHanaDataTypes::VarChar ||
     499          84 :            dataType == QGRHanaDataTypes::LongVarChar ||
     500          84 :            dataType == QGRHanaDataTypes::WChar ||
     501          44 :            dataType == QGRHanaDataTypes::WVarChar ||
     502          44 :            dataType == QGRHanaDataTypes::WLongVarChar ||
     503          44 :            dataType == QGRHanaDataTypes::Date ||
     504          43 :            dataType == QGRHanaDataTypes::TypeDate ||
     505          43 :            dataType == QGRHanaDataTypes::Time ||
     506          42 :            dataType == QGRHanaDataTypes::TypeTime ||
     507          42 :            dataType == QGRHanaDataTypes::Timestamp ||
     508          40 :            dataType == QGRHanaDataTypes::TypeTimestamp ||
     509          40 :            dataType == QGRHanaDataTypes::Binary ||
     510          39 :            dataType == QGRHanaDataTypes::VarBinary ||
     511          39 :            dataType == QGRHanaDataTypes::LongVarBinary ||
     512         375 :            dataType == QGRHanaDataTypes::Geometry ||
     513         188 :            dataType == QGRHanaDataTypes::RealVector;
     514             : }
     515             : 
     516             : }  // anonymous namespace
     517             : 
     518             : /************************************************************************/
     519             : /*                               GetPrefix()                            */
     520             : /************************************************************************/
     521             : 
     522         195 : const char *OGRHanaDataSource::GetPrefix()
     523             : {
     524         195 :     return HANA_PREFIX;
     525             : }
     526             : 
     527             : /************************************************************************/
     528             : /*                         OGRHanaDataSource()                          */
     529             : /************************************************************************/
     530             : 
     531          65 : OGRHanaDataSource::OGRHanaDataSource()
     532             : {
     533          65 : }
     534             : 
     535             : /************************************************************************/
     536             : /*                        ~OGRHanaDataSource()                          */
     537             : /************************************************************************/
     538             : 
     539         130 : OGRHanaDataSource::~OGRHanaDataSource()
     540             : {
     541          65 :     layers_.clear();
     542             : 
     543          93 :     for (const auto &kv : srsCache_)
     544             :     {
     545          28 :         OGRSpatialReference *srs = kv.second;
     546          28 :         if (srs != nullptr)
     547          28 :             srs->Release();
     548             :     }
     549          65 :     srsCache_.clear();
     550         130 : }
     551             : 
     552             : /************************************************************************/
     553             : /*                                 Open()                               */
     554             : /************************************************************************/
     555             : 
     556          65 : int OGRHanaDataSource::Open(const char *newName, char **openOptions, int update)
     557             : {
     558          65 :     CPLAssert(layers_.size() == 0);
     559             : 
     560          65 :     if (!STARTS_WITH_CI(newName, GetPrefix()))
     561             :     {
     562           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     563             :                  "%s does not conform to HANA driver naming convention,"
     564             :                  " %s*\n",
     565             :                  newName, GetPrefix());
     566           0 :         return FALSE;
     567             :     }
     568             : 
     569          65 :     updateMode_ = update;
     570          65 :     detectGeometryType_ = CPLFetchBool(
     571             :         openOptions, OGRHanaOpenOptionsConstants::DETECT_GEOMETRY_TYPE, true);
     572             : 
     573          65 :     std::size_t prefixLength = strlen(GetPrefix());
     574             :     char **connOptions =
     575          65 :         CSLTokenizeStringComplex(newName + prefixLength, ";", TRUE, FALSE);
     576             : 
     577          65 :     const char *paramSchema = CSLFetchNameValueDef(
     578             :         connOptions, OGRHanaOpenOptionsConstants::SCHEMA, nullptr);
     579          65 :     if (paramSchema != nullptr)
     580          63 :         schemaName_ = paramSchema;
     581             : 
     582          65 :     int ret = FALSE;
     583             : 
     584          65 :     CPLString connectionStr = BuildConnectionString(connOptions);
     585             : 
     586          65 :     if (!connectionStr.empty())
     587             :     {
     588          56 :         connEnv_ = odbc::Environment::create();
     589          56 :         conn_ = connEnv_->createConnection();
     590          56 :         conn_->setAutoCommit(false);
     591             : 
     592          56 :         const char *paramConnTimeout = CSLFetchNameValueDef(
     593             :             connOptions, OGRHanaOpenOptionsConstants::CONNECTION_TIMEOUT,
     594             :             nullptr);
     595          56 :         if (paramConnTimeout != nullptr)
     596           0 :             conn_->setConnectionTimeout(
     597           0 :                 static_cast<unsigned long>(atoi(paramConnTimeout)));
     598             : 
     599             :         try
     600             :         {
     601          56 :             conn_->connect(connectionStr.c_str());
     602             :         }
     603           0 :         catch (const odbc::Exception &ex)
     604             :         {
     605           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     606           0 :                      "HANA connection failed: %s\n", ex.what());
     607             :         }
     608             : 
     609          56 :         if (conn_->connected())
     610             :         {
     611          56 :             DetermineVersions();
     612             : 
     613          56 :             const char *paramTables = CSLFetchNameValueDef(
     614             :                 connOptions, OGRHanaOpenOptionsConstants::TABLES, "");
     615          56 :             InitializeLayers(paramSchema, paramTables);
     616          56 :             ret = TRUE;
     617             :         }
     618             :     }
     619             : 
     620          65 :     CSLDestroy(connOptions);
     621             : 
     622          65 :     return ret;
     623             : }
     624             : 
     625             : /************************************************************************/
     626             : /*                            DeleteLayer()                             */
     627             : /************************************************************************/
     628             : 
     629           0 : OGRErr OGRHanaDataSource::DeleteLayer(int index)
     630             : {
     631           0 :     if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
     632           0 :         return OGRERR_FAILURE;
     633             : 
     634             :     const std::unique_ptr<OGRLayer> &layer =
     635           0 :         layers_[static_cast<std::size_t>(index)];
     636           0 :     CPLDebug("HANA", "DeleteLayer(%s)", layer->GetName());
     637             : 
     638           0 :     if (auto tableLayer = dynamic_cast<OGRHanaTableLayer *>(layer.get()))
     639             :     {
     640           0 :         OGRErr err = tableLayer->DropTable();
     641           0 :         if (OGRERR_NONE == err)
     642           0 :             return err;
     643             :     }
     644             : 
     645           0 :     layers_.erase(layers_.begin() + index);
     646             : 
     647           0 :     return OGRERR_NONE;
     648             : }
     649             : 
     650          18 : void OGRHanaDataSource::CreateTable(
     651             :     const CPLString &tableName, const CPLString &fidName,
     652             :     const CPLString &fidType, const CPLString &geomColumnName,
     653             :     OGRwkbGeometryType geomType, bool geomColumnNullable,
     654             :     const CPLString &geomColumnIndexType, int geomSrid)
     655             : {
     656          36 :     CPLString sql;
     657          50 :     if (geomType == OGRwkbGeometryType::wkbNone ||
     658          32 :         !(!geomColumnName.empty() && geomSrid >= 0))
     659             :     {
     660           6 :         sql = "CREATE COLUMN TABLE " +
     661          18 :               GetFullTableNameQuoted(schemaName_, tableName) + " (" +
     662          24 :               QuotedIdentifier(fidName) + " " + fidType +
     663          12 :               " GENERATED BY DEFAULT AS IDENTITY, PRIMARY KEY ( " +
     664          18 :               QuotedIdentifier(fidName) + "));";
     665             :     }
     666             :     else
     667             :     {
     668          12 :         sql = "CREATE COLUMN TABLE " +
     669          36 :               GetFullTableNameQuoted(schemaName_, tableName) + " (" +
     670          48 :               QuotedIdentifier(fidName) + " " + fidType +
     671          24 :               " GENERATED BY DEFAULT AS IDENTITY, " +
     672          48 :               QuotedIdentifier(geomColumnName) + " ST_GEOMETRY (" +
     673          48 :               std::to_string(geomSrid) + ")" +
     674          24 :               (geomColumnNullable ? "" : " NOT NULL") +
     675          36 :               " SPATIAL INDEX PREFERENCE " + geomColumnIndexType +
     676          36 :               ", PRIMARY KEY ( " + QuotedIdentifier(fidName) + "));";
     677             :     }
     678             : 
     679          18 :     ExecuteSQL(sql);
     680          18 : }
     681             : 
     682          56 : void OGRHanaDataSource::DetermineVersions()
     683             : {
     684          56 :     odbc::DatabaseMetaDataRef dbmd = conn_->getDatabaseMetaData();
     685          56 :     CPLString dbVersion(dbmd->getDBMSVersion());
     686          56 :     hanaVersion_ = HanaVersion::fromString(dbVersion);
     687             : 
     688          56 :     if (hanaVersion_.major() < 4)
     689             :     {
     690           0 :         cloudVersion_ = HanaVersion(0, 0, 0);
     691           0 :         return;
     692             :     }
     693             : 
     694         112 :     odbc::StatementRef stmt = conn_->createStatement();
     695          56 :     const char *sql = "SELECT CLOUD_VERSION FROM SYS.M_DATABASE;";
     696             : 
     697         112 :     odbc::ResultSetRef rsVersion = stmt->executeQuery(sql);
     698          56 :     if (rsVersion->next())
     699             :         cloudVersion_ =
     700          56 :             HanaVersion::fromString(rsVersion->getString(1)->c_str());
     701             : 
     702          56 :     rsVersion->close();
     703             : }
     704             : 
     705             : /************************************************************************/
     706             : /*                            FindSchemaAndTableNames()                 */
     707             : /************************************************************************/
     708             : 
     709             : std::pair<CPLString, CPLString>
     710          15 : OGRHanaDataSource::FindSchemaAndTableNames(const char *query)
     711             : {
     712          30 :     odbc::PreparedStatementRef stmt = PrepareStatement(query);
     713          15 :     if (stmt.get() == nullptr)
     714           0 :         return {"", ""};
     715             : 
     716          30 :     odbc::ResultSetMetaDataRef rsmd = stmt->getMetaData();
     717             : 
     718             :     // Note, getTableName returns correct table name also in the case
     719             :     // when the original sql query uses a view
     720          30 :     CPLString tableName = rsmd->getTableName(1);
     721          15 :     if (tableName == "M_DATABASE_")
     722           0 :         tableName = "M_DATABASE";
     723          30 :     CPLString schemaName = rsmd->getSchemaName(1);
     724          15 :     if (schemaName.empty() && !tableName.empty())
     725          14 :         schemaName = FindSchemaName(tableName.c_str());
     726          15 :     return {schemaName, tableName};
     727             : }
     728             : 
     729             : /************************************************************************/
     730             : /*                            FindLayerByName()                         */
     731             : /************************************************************************/
     732             : 
     733          67 : int OGRHanaDataSource::FindLayerByName(const char *name)
     734             : {
     735         604 :     for (size_t i = 0; i < layers_.size(); ++i)
     736             :     {
     737         574 :         if (EQUAL(name, layers_[i]->GetName()))
     738          37 :             return static_cast<int>(i);
     739             :     }
     740          30 :     return -1;
     741             : }
     742             : 
     743             : /************************************************************************/
     744             : /*                            FindSchemaName()                          */
     745             : /************************************************************************/
     746             : 
     747          14 : CPLString OGRHanaDataSource::FindSchemaName(const char *objectName)
     748             : {
     749          28 :     auto getSchemaName = [&](const char *sql)
     750             :     {
     751          56 :         odbc::PreparedStatementRef stmt = PrepareStatement(sql);
     752          28 :         stmt->setString(1, odbc::String(objectName));
     753          56 :         odbc::ResultSetRef rsEntries = stmt->executeQuery();
     754          28 :         CPLString ret;
     755          42 :         while (rsEntries->next())
     756             :         {
     757             :             // return empty string if there is more than one schema.
     758          28 :             if (!ret.empty())
     759             :             {
     760          14 :                 ret.clear();
     761          14 :                 break;
     762             :             }
     763          14 :             ret = *rsEntries->getString(1);
     764             :         }
     765          28 :         rsEntries->close();
     766             : 
     767          56 :         return ret;
     768          14 :     };
     769             : 
     770             :     CPLString ret = getSchemaName(
     771          14 :         "SELECT SCHEMA_NAME FROM SYS.TABLES WHERE TABLE_NAME = ?");
     772          14 :     if (ret.empty())
     773          28 :         ret = getSchemaName(
     774          14 :             "SELECT SCHEMA_NAME FROM SYS.VIEWS WHERE VIEW_NAME = ?");
     775             : 
     776          28 :     return ret;
     777             : }
     778             : 
     779             : /************************************************************************/
     780             : /*                              CreateStatement()                       */
     781             : /************************************************************************/
     782             : 
     783         220 : odbc::StatementRef OGRHanaDataSource::CreateStatement()
     784             : {
     785         220 :     return conn_->createStatement();
     786             : }
     787             : 
     788             : /************************************************************************/
     789             : /*                              PrepareStatement()                      */
     790             : /************************************************************************/
     791             : 
     792         337 : odbc::PreparedStatementRef OGRHanaDataSource::PrepareStatement(const char *sql)
     793             : {
     794         337 :     CPLAssert(sql != nullptr);
     795             : 
     796             :     try
     797             :     {
     798         337 :         CPLDebug("HANA", "Prepare statement %s.", sql);
     799             : 
     800         674 :         std::u16string sqlUtf16 = odbc::StringConverter::utf8ToUtf16(sql);
     801         337 :         return conn_->prepareStatement(sqlUtf16.c_str());
     802             :     }
     803           2 :     catch (const odbc::Exception &ex)
     804             :     {
     805           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to prepare statement: %s",
     806           1 :                  ex.what());
     807             :     }
     808           1 :     return nullptr;
     809             : }
     810             : 
     811             : /************************************************************************/
     812             : /*                              Commit()                                */
     813             : /************************************************************************/
     814             : 
     815         134 : void OGRHanaDataSource::Commit()
     816             : {
     817         134 :     conn_->commit();
     818         134 : }
     819             : 
     820             : /************************************************************************/
     821             : /*                            ExecuteSQL()                              */
     822             : /************************************************************************/
     823             : 
     824          96 : void OGRHanaDataSource::ExecuteSQL(const CPLString &sql)
     825             : {
     826             :     std::u16string sqlUtf16 =
     827         192 :         odbc::StringConverter::utf8ToUtf16(sql.c_str(), sql.length());
     828         192 :     odbc::StatementRef stmt = conn_->createStatement();
     829          96 :     stmt->execute(sqlUtf16.c_str());
     830          92 :     if (!IsTransactionStarted())
     831          91 :         conn_->commit();
     832          92 : }
     833             : 
     834             : /************************************************************************/
     835             : /*                            GetSrsById()                              */
     836             : /*                                                                      */
     837             : /*      Return a SRS corresponding to a particular id.  The returned    */
     838             : /*      object has its reference counter incremented. Consequently      */
     839             : /*      the caller should call Release() on it (if not null) once done  */
     840             : /*      with it.                                                        */
     841             : /************************************************************************/
     842             : 
     843          30 : OGRSpatialReference *OGRHanaDataSource::GetSrsById(int srid)
     844             : {
     845          30 :     if (srid < 0)
     846           0 :         return nullptr;
     847             : 
     848          30 :     auto it = srsCache_.find(srid);
     849          30 :     if (it != srsCache_.end())
     850             :     {
     851           2 :         it->second->Reference();
     852           2 :         return it->second;
     853             :     }
     854             : 
     855          28 :     OGRSpatialReference *srs = nullptr;
     856             : 
     857          28 :     CPLString wkt = GetSrsWktById(*conn_, srid);
     858          28 :     if (!wkt.empty())
     859             :     {
     860          28 :         srs = new OGRSpatialReference();
     861          28 :         OGRErr err = srs->importFromWkt(wkt.c_str());
     862          28 :         if (OGRERR_NONE != err)
     863             :         {
     864           0 :             delete srs;
     865           0 :             srs = nullptr;
     866             :         }
     867             :     }
     868             : 
     869          28 :     srsCache_.insert({srid, srs});
     870             : 
     871          28 :     if (srs)
     872          28 :         srs->Reference();
     873          28 :     return srs;
     874             : }
     875             : 
     876             : /************************************************************************/
     877             : /*                               GetSrsId()                             */
     878             : /************************************************************************/
     879             : 
     880          14 : int OGRHanaDataSource::GetSrsId(const OGRSpatialReference *srs)
     881             : {
     882          14 :     if (srs == nullptr)
     883           1 :         return UNDETERMINED_SRID;
     884             : 
     885             :     /* -------------------------------------------------------------------- */
     886             :     /*      Try to find srs id using authority name and code (EPSG:3857).   */
     887             :     /* -------------------------------------------------------------------- */
     888          26 :     OGRSpatialReference srsLocal(*srs);
     889             : 
     890          13 :     const char *authorityName = srsLocal.GetAuthorityName(nullptr);
     891          13 :     if (authorityName == nullptr || strlen(authorityName) == 0)
     892             :     {
     893           0 :         srsLocal.AutoIdentifyEPSG();
     894           0 :         authorityName = srsLocal.GetAuthorityName(nullptr);
     895           0 :         if (authorityName != nullptr && EQUAL(authorityName, "EPSG"))
     896             :         {
     897           0 :             const char *authorityCode = srsLocal.GetAuthorityCode(nullptr);
     898           0 :             if (authorityCode != nullptr && strlen(authorityCode) > 0)
     899             :             {
     900           0 :                 srsLocal.importFromEPSG(atoi(authorityCode));
     901           0 :                 authorityName = srsLocal.GetAuthorityName(nullptr);
     902             :             }
     903             :         }
     904             :     }
     905             : 
     906          13 :     int authorityCode = 0;
     907          13 :     if (authorityName != nullptr)
     908             :     {
     909          13 :         authorityCode = atoi(srsLocal.GetAuthorityCode(nullptr));
     910          13 :         if (authorityCode > 0)
     911             :         {
     912          13 :             int ret = GetSridWithFilter(
     913             :                 *conn_,
     914          26 :                 CPLString().Printf("SRS_ID = %d AND ORGANIZATION = '%s'",
     915          13 :                                    authorityCode, authorityName));
     916          13 :             if (ret != UNDETERMINED_SRID)
     917          13 :                 return ret;
     918             :         }
     919             :     }
     920             : 
     921             :     /* -------------------------------------------------------------------- */
     922             :     /*      Try to find srs id using wkt content.                           */
     923             :     /* -------------------------------------------------------------------- */
     924             : 
     925           0 :     char *wkt = nullptr;
     926           0 :     OGRErr err = srsLocal.exportToWkt(&wkt);
     927           0 :     CPLString strWkt(wkt);
     928           0 :     CPLFree(wkt);
     929             : 
     930           0 :     if (OGRERR_NONE != err)
     931           0 :         return UNDETERMINED_SRID;
     932             : 
     933           0 :     int srid = GetSridWithFilter(
     934           0 :         *conn_, CPLString().Printf("DEFINITION = '%s'", strWkt.c_str()));
     935           0 :     if (srid != UNDETERMINED_SRID)
     936           0 :         return srid;
     937             : 
     938             :     /* -------------------------------------------------------------------- */
     939             :     /*      Try to add a new spatial reference system to the database       */
     940             :     /* -------------------------------------------------------------------- */
     941             : 
     942           0 :     char *proj4 = nullptr;
     943           0 :     err = srsLocal.exportToProj4(&proj4);
     944           0 :     CPLString strProj4(proj4);
     945           0 :     CPLFree(proj4);
     946             : 
     947           0 :     if (OGRERR_NONE != err)
     948           0 :         return srid;
     949             : 
     950           0 :     if (authorityName != nullptr && authorityCode > 0)
     951             :     {
     952           0 :         srid = authorityCode;
     953             :     }
     954             :     else
     955             :     {
     956           0 :         odbc::StatementRef stmt = conn_->createStatement();
     957           0 :         const char *sql =
     958             :             "SELECT MAX(SRS_ID) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE "
     959             :             "SRS_ID >= 10000000 AND SRS_ID < 20000000";
     960           0 :         odbc::ResultSetRef rsSrid = stmt->executeQuery(sql);
     961           0 :         while (rsSrid->next())
     962             :         {
     963           0 :             odbc::Int val = rsSrid->getInt(1);
     964           0 :             srid = val.isNull() ? 10000000 : *val + 1;
     965             :         }
     966           0 :         rsSrid->close();
     967             :     }
     968             : 
     969             :     try
     970             :     {
     971           0 :         CreateSpatialReferenceSystem(srsLocal, srid, authorityName,
     972             :                                      authorityCode, strWkt, strProj4);
     973           0 :         return srid;
     974             :     }
     975           0 :     catch (const odbc::Exception &ex)
     976             :     {
     977           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     978           0 :                  "Unable to create an SRS in the database: %s.\n", ex.what());
     979             :     }
     980             : 
     981           0 :     return UNDETERMINED_SRID;
     982             : }
     983             : 
     984             : /************************************************************************/
     985             : /*                           IsSrsRoundEarth()                          */
     986             : /************************************************************************/
     987             : 
     988          12 : bool OGRHanaDataSource::IsSrsRoundEarth(int srid)
     989             : {
     990          12 :     const char *sql =
     991             :         "SELECT ROUND_EARTH FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
     992             :         "WHERE SRS_ID = ?";
     993          24 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
     994          12 :     stmt->setInt(1, odbc::Int(srid));
     995          12 :     odbc::ResultSetRef rs = stmt->executeQuery();
     996          12 :     bool ret = false;
     997          12 :     if (rs->next())
     998          12 :         ret = (*rs->getString(1) == "TRUE");
     999          12 :     rs->close();
    1000          24 :     return ret;
    1001             : }
    1002             : 
    1003             : /************************************************************************/
    1004             : /*                        HasSrsPlanarEquivalent()                      */
    1005             : /************************************************************************/
    1006             : 
    1007           2 : bool OGRHanaDataSource::HasSrsPlanarEquivalent(int srid)
    1008             : {
    1009           2 :     const char *sql = "SELECT COUNT(*) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
    1010             :                       "WHERE SRS_ID = ?";
    1011           4 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
    1012           2 :     stmt->setInt(1, ToPlanarSRID(srid));
    1013           2 :     odbc::ResultSetRef rs = stmt->executeQuery();
    1014           2 :     std::int64_t count = 0;
    1015           2 :     if (rs->next())
    1016           2 :         count = *rs->getLong(1);
    1017           2 :     rs->close();
    1018           4 :     return count > 0;
    1019             : }
    1020             : 
    1021             : /************************************************************************/
    1022             : /*                           GetQueryColumns()                          */
    1023             : /************************************************************************/
    1024             : 
    1025          55 : OGRErr OGRHanaDataSource::GetQueryColumns(
    1026             :     const CPLString &schemaName, const CPLString &query,
    1027             :     std::vector<ColumnDescription> &columnDescriptions)
    1028             : {
    1029          55 :     columnDescriptions.clear();
    1030             : 
    1031         110 :     odbc::PreparedStatementRef stmtQuery = PrepareStatement(query);
    1032             : 
    1033          55 :     if (stmtQuery.isNull())
    1034           0 :         return OGRERR_FAILURE;
    1035             : 
    1036         110 :     odbc::ResultSetMetaDataRef rsmd = stmtQuery->getMetaData();
    1037          55 :     std::size_t numColumns = rsmd->getColumnCount();
    1038          55 :     if (numColumns == 0)
    1039           0 :         return OGRERR_NONE;
    1040             : 
    1041          55 :     columnDescriptions.reserve(numColumns);
    1042             : 
    1043         110 :     odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
    1044             :     odbc::PreparedStatementRef stmtArrayTypeInfo =
    1045             :         PrepareStatement("SELECT DATA_TYPE_NAME FROM "
    1046             :                          "SYS.TABLE_COLUMNS_ODBC WHERE SCHEMA_NAME = ? "
    1047             :                          "AND TABLE_NAME = ? AND COLUMN_NAME = ? AND "
    1048         110 :                          "DATA_TYPE_NAME LIKE '% ARRAY'");
    1049             : 
    1050         254 :     for (unsigned short clmIndex = 1; clmIndex <= numColumns; ++clmIndex)
    1051             :     {
    1052         199 :         CPLString typeName = rsmd->getColumnTypeName(clmIndex);
    1053             : 
    1054         199 :         if (typeName.empty())
    1055           0 :             continue;
    1056             : 
    1057         199 :         bool isArray = false;
    1058         199 :         CPLString tableName = rsmd->getTableName(clmIndex);
    1059         199 :         CPLString columnName = rsmd->getColumnName(clmIndex);
    1060         199 :         CPLString defaultValue;
    1061         199 :         short dataType = rsmd->getColumnType(clmIndex);
    1062             : 
    1063         199 :         if (!schemaName.empty() && !tableName.empty())
    1064             :         {
    1065             :             // Retrieve information about default value in column
    1066             :             odbc::ResultSetRef rsColumns =
    1067             :                 dmd->getColumns(nullptr, schemaName.c_str(), tableName.c_str(),
    1068         141 :                                 columnName.c_str());
    1069         141 :             if (rsColumns->next())
    1070             :             {
    1071             :                 odbc::String defaultValueStr =
    1072         276 :                     rsColumns->getString(13 /*COLUMN_DEF*/);
    1073         138 :                 if (!defaultValueStr.isNull())
    1074             :                     defaultValue =
    1075          12 :                         FormatDefaultValue(defaultValueStr->c_str(), dataType);
    1076             :             }
    1077         141 :             rsColumns->close();
    1078             : 
    1079             :             // Retrieve information about array type
    1080         141 :             stmtArrayTypeInfo->setString(1, schemaName);
    1081         141 :             stmtArrayTypeInfo->setString(2, tableName);
    1082         141 :             stmtArrayTypeInfo->setString(3, columnName);
    1083         141 :             odbc::ResultSetRef rsArrayTypes = stmtArrayTypeInfo->executeQuery();
    1084         141 :             if (rsArrayTypes->next())
    1085             :             {
    1086          11 :                 typeName = *rsArrayTypes->getString(1);
    1087          11 :                 dataType = GetArrayDataType(typeName);
    1088             : 
    1089          11 :                 if (dataType == QGRHanaDataTypes::Unknown)
    1090             :                 {
    1091           0 :                     CPLError(
    1092             :                         CE_Failure, CPLE_AppDefined,
    1093             :                         "GetQueryColumns(): Unsupported type of array (%s)",
    1094             :                         typeName.c_str());
    1095           0 :                     return OGRERR_FAILURE;
    1096             :                 }
    1097             : 
    1098          11 :                 isArray = true;
    1099             :             }
    1100         141 :             rsArrayTypes->close();
    1101             :         }
    1102             : 
    1103         199 :         if (!isArray && !IsKnownDataType(dataType))
    1104             :         {
    1105           0 :             odbc::ResultSetRef rsTypeInfo = dmd->getTypeInfo(dataType);
    1106           0 :             if (rsTypeInfo->next())
    1107             :             {
    1108           0 :                 odbc::String name = rsTypeInfo->getString(1);
    1109           0 :                 if (name.isNull())
    1110           0 :                     continue;
    1111           0 :                 if (name->compare("SHORTTEXT") == 0 ||
    1112           0 :                     name->compare("ALPHANUM") == 0)
    1113             :                 {
    1114           0 :                     dataType = QGRHanaDataTypes::WVarChar;
    1115             :                 }
    1116             :             }
    1117           0 :             rsTypeInfo->close();
    1118             :         }
    1119             : 
    1120         199 :         if (dataType == QGRHanaDataTypes::Geometry)
    1121             :         {
    1122          33 :             GeometryColumnDescription geometryColumnDesc;
    1123          33 :             if (schemaName.empty() || tableName.empty())
    1124          18 :                 geometryColumnDesc = GetGeometryColumnDescription(
    1125          18 :                     *conn_, query, columnName, detectGeometryType_);
    1126             :             else
    1127          48 :                 geometryColumnDesc = GetGeometryColumnDescription(
    1128             :                     *conn_, schemaName, tableName, columnName,
    1129          48 :                     detectGeometryType_);
    1130          33 :             geometryColumnDesc.isNullable = rsmd->isNullable(clmIndex);
    1131             : 
    1132          66 :             columnDescriptions.push_back({true, AttributeColumnDescription(),
    1133          33 :                                           std::move(geometryColumnDesc)});
    1134             :         }
    1135             :         else
    1136             :         {
    1137         166 :             AttributeColumnDescription attributeColumnDesc;
    1138         166 :             attributeColumnDesc.name = std::move(columnName);
    1139         166 :             attributeColumnDesc.type = dataType;
    1140         166 :             attributeColumnDesc.typeName = std::move(typeName);
    1141         166 :             attributeColumnDesc.isArray = isArray;
    1142         166 :             attributeColumnDesc.isNullable = rsmd->isNullable(clmIndex);
    1143         166 :             attributeColumnDesc.isAutoIncrement =
    1144         166 :                 rsmd->isAutoIncrement(clmIndex);
    1145         166 :             attributeColumnDesc.length =
    1146         166 :                 static_cast<int>(rsmd->getColumnLength(clmIndex));
    1147         166 :             attributeColumnDesc.precision = rsmd->getPrecision(clmIndex);
    1148         166 :             attributeColumnDesc.scale = rsmd->getScale(clmIndex);
    1149         166 :             attributeColumnDesc.defaultValue = std::move(defaultValue);
    1150             : 
    1151         166 :             columnDescriptions.push_back({false, std::move(attributeColumnDesc),
    1152             :                                           GeometryColumnDescription()});
    1153             :         }
    1154             :     }
    1155             : 
    1156          55 :     return OGRERR_NONE;
    1157             : }
    1158             : 
    1159             : /************************************************************************/
    1160             : /*                          GetTablePrimaryKeys()                       */
    1161             : /************************************************************************/
    1162             : 
    1163             : std::vector<CPLString>
    1164          55 : OGRHanaDataSource::GetTablePrimaryKeys(const char *schemaName,
    1165             :                                        const char *tableName)
    1166             : {
    1167          55 :     std::vector<CPLString> ret;
    1168             : 
    1169         110 :     odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
    1170             :     odbc::ResultSetRef rsPrimaryKeys =
    1171         110 :         dmd->getPrimaryKeys(nullptr, schemaName, tableName);
    1172        2877 :     while (rsPrimaryKeys->next())
    1173             :     {
    1174        2822 :         ret.push_back(*rsPrimaryKeys->getString(4));
    1175             :     }
    1176          55 :     rsPrimaryKeys->close();
    1177             : 
    1178         110 :     return ret;
    1179             : }
    1180             : 
    1181             : /************************************************************************/
    1182             : /*                          InitializeLayers()                          */
    1183             : /************************************************************************/
    1184             : 
    1185          56 : void OGRHanaDataSource::InitializeLayers(const char *schemaName,
    1186             :                                          const char *tableNames)
    1187             : {
    1188         112 :     std::vector<CPLString> tablesToFind = SplitStrings(tableNames, ",");
    1189          56 :     const bool hasTablesToFind = !tablesToFind.empty();
    1190             : 
    1191         112 :     auto addLayersFromQuery = [&](const char *query, bool updatable)
    1192             :     {
    1193         224 :         odbc::PreparedStatementRef stmt = PrepareStatement(query);
    1194         112 :         stmt->setString(1, odbc::String(schemaName));
    1195         224 :         odbc::ResultSetRef rsTables = stmt->executeQuery();
    1196         579 :         while (rsTables->next())
    1197             :         {
    1198         467 :             odbc::String tableName = rsTables->getString(1);
    1199         467 :             if (tableName.isNull())
    1200           0 :                 continue;
    1201             :             auto pos =
    1202         467 :                 std::find(tablesToFind.begin(), tablesToFind.end(), *tableName);
    1203         467 :             if (pos != tablesToFind.end())
    1204           0 :                 tablesToFind.erase(pos);
    1205             : 
    1206             :             auto layer = std::make_unique<OGRHanaTableLayer>(
    1207         467 :                 this, schemaName_.c_str(), tableName->c_str(), updatable);
    1208         467 :             layers_.push_back(std::move(layer));
    1209             :         }
    1210         112 :         rsTables->close();
    1211         112 :     };
    1212             : 
    1213             :     // Look for layers in Tables
    1214         112 :     std::ostringstream osTables;
    1215          56 :     osTables << "SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = ?";
    1216          56 :     if (!tablesToFind.empty())
    1217             :         osTables << " AND TABLE_NAME IN ("
    1218           0 :                  << JoinStrings(tablesToFind, ",", Literal) << ")";
    1219             : 
    1220          56 :     addLayersFromQuery(osTables.str().c_str(), updateMode_);
    1221             : 
    1222          56 :     if (!(hasTablesToFind && tablesToFind.empty()))
    1223             :     {
    1224             :         // Look for layers in Views
    1225          56 :         std::ostringstream osViews;
    1226          56 :         osViews << "SELECT VIEW_NAME FROM SYS.VIEWS WHERE SCHEMA_NAME = ?";
    1227             :         // cppcheck-suppress knownConditionTrueFalse
    1228          56 :         if (!tablesToFind.empty())
    1229             :             osViews << " AND VIEW_NAME IN ("
    1230           0 :                     << JoinStrings(tablesToFind, ",", Literal) << ")";
    1231             : 
    1232          56 :         addLayersFromQuery(osViews.str().c_str(), false);
    1233             :     }
    1234             : 
    1235             :     // Report about tables that could not be found
    1236          56 :     for (const auto &tableName : tablesToFind)
    1237             :     {
    1238           0 :         const char *layerName = tableName.c_str();
    1239           0 :         if (GetLayerByName(layerName) == nullptr)
    1240           0 :             CPLDebug("HANA",
    1241             :                      "Table '%s' not found or does not "
    1242             :                      "have any geometry column.",
    1243             :                      layerName);
    1244             :     }
    1245          56 : }
    1246             : 
    1247             : /************************************************************************/
    1248             : /*                          LaunderName()                               */
    1249             : /************************************************************************/
    1250             : 
    1251         115 : std::pair<OGRErr, CPLString> OGRHanaDataSource::LaunderName(const char *name)
    1252             : {
    1253         115 :     CPLAssert(name != nullptr);
    1254             : 
    1255         115 :     if (!CPLIsUTF8(name, -1))
    1256             :     {
    1257           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s is not a valid UTF-8 string.",
    1258             :                  name);
    1259           0 :         return {OGRERR_FAILURE, ""};
    1260             :     }
    1261             : 
    1262        1050 :     auto getUTF8SequenceLength = [](char c)
    1263             :     {
    1264        1050 :         if ((c & 0x80) == 0x00)
    1265        1040 :             return 1;
    1266          10 :         if ((c & 0xE0) == 0xC0)
    1267           6 :             return 2;
    1268           4 :         if ((c & 0xF0) == 0xE0)
    1269           0 :             return 3;
    1270           4 :         if ((c & 0xF8) == 0xF0)
    1271           4 :             return 4;
    1272             : 
    1273           0 :         throw std::runtime_error("Invalid UTF-8 sequence");
    1274             :     };
    1275             : 
    1276         230 :     CPLString newName(name);
    1277         115 :     bool hasNonASCII = false;
    1278         115 :     size_t i = 0;
    1279             : 
    1280        1165 :     while (name[i] != '\0')
    1281             :     {
    1282        1050 :         char c = name[i];
    1283        1050 :         int len = getUTF8SequenceLength(c);
    1284        1050 :         if (len == 1)
    1285             :         {
    1286        1040 :             if (c == '-' || c == '#')
    1287           6 :                 newName[i] = '_';
    1288             :             else
    1289        1034 :                 newName[i] = static_cast<char>(
    1290        1034 :                     CPLToupper(static_cast<unsigned char>(c)));
    1291             :         }
    1292             :         else
    1293             :         {
    1294          10 :             hasNonASCII = true;
    1295             :         }
    1296             : 
    1297        1050 :         i += len;
    1298             :     }
    1299             : 
    1300         115 :     if (!hasNonASCII)
    1301         222 :         return {OGRERR_NONE, newName};
    1302             : 
    1303           4 :     const char *sql = "SELECT UPPER(?) FROM DUMMY";
    1304           8 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
    1305           4 :     stmt->setString(1, odbc::String(newName.c_str()));
    1306           8 :     odbc::ResultSetRef rsName = stmt->executeQuery();
    1307           4 :     OGRErr err = OGRERR_NONE;
    1308           4 :     if (rsName->next())
    1309             :     {
    1310           4 :         newName.swap(*rsName->getString(1));
    1311             :     }
    1312             :     else
    1313             :     {
    1314           0 :         err = OGRERR_FAILURE;
    1315           0 :         newName.clear();
    1316             :     }
    1317           4 :     rsName->close();
    1318           4 :     return {err, newName};
    1319             : }
    1320             : 
    1321             : /************************************************************************/
    1322             : /*                       CreateSpatialReference()                       */
    1323             : /************************************************************************/
    1324             : 
    1325           0 : void OGRHanaDataSource::CreateSpatialReferenceSystem(
    1326             :     const OGRSpatialReference &srs, int srid, const char *authorityName,
    1327             :     int authorityCode, const CPLString &wkt, const CPLString &proj4)
    1328             : {
    1329           0 :     CPLString refName((srs.IsProjected()) ? srs.GetAttrValue("PROJCS")
    1330           0 :                                           : srs.GetAttrValue("GEOGCS"));
    1331           0 :     if (refName.empty() || EQUAL(refName.c_str(), "UNKNOWN"))
    1332           0 :         refName = "OGR_PROJECTION_" + std::to_string(srid);
    1333             : 
    1334           0 :     OGRErr err = OGRERR_NONE;
    1335           0 :     CPLString ellipsoidParams;
    1336           0 :     const double semiMajor = srs.GetSemiMajor(&err);
    1337           0 :     if (OGRERR_NONE == err)
    1338           0 :         ellipsoidParams += " SEMI MAJOR AXIS " + std::to_string(semiMajor);
    1339           0 :     const double semiMinor = srs.GetSemiMinor(&err);
    1340           0 :     const double invFlattening = srs.GetInvFlattening(&err);
    1341           0 :     if (OGRERR_NONE == err)
    1342             :         ellipsoidParams +=
    1343           0 :             " INVERSE FLATTENING " + std::to_string(invFlattening);
    1344             :     else
    1345           0 :         ellipsoidParams += " SEMI MINOR AXIS " + std::to_string(semiMinor);
    1346             : 
    1347           0 :     const char *linearUnits = nullptr;
    1348           0 :     srs.GetLinearUnits(&linearUnits);
    1349           0 :     const char *angularUnits = nullptr;
    1350           0 :     srs.GetAngularUnits(&angularUnits);
    1351             : 
    1352           0 :     CPLString xRange, yRange;
    1353             :     double dfWestLongitudeDeg, dfSouthLatitudeDeg, dfEastLongitudeDeg,
    1354             :         dfNorthLatitudeDeg;
    1355           0 :     if (srs.GetAreaOfUse(&dfWestLongitudeDeg, &dfSouthLatitudeDeg,
    1356             :                          &dfEastLongitudeDeg, &dfNorthLatitudeDeg, nullptr))
    1357             :     {
    1358           0 :         xRange = CPLString().Printf("%s BETWEEN %f AND %f",
    1359           0 :                                     srs.IsGeographic() ? "LONGITUDE" : "X",
    1360           0 :                                     dfWestLongitudeDeg, dfEastLongitudeDeg);
    1361           0 :         yRange = CPLString().Printf("%s BETWEEN %f AND %f",
    1362           0 :                                     srs.IsGeographic() ? "LATITUDE" : "Y",
    1363           0 :                                     dfSouthLatitudeDeg, dfNorthLatitudeDeg);
    1364             :     }
    1365             :     else
    1366             :     {
    1367           0 :         xRange = CPLString().Printf("%s UNBOUNDED",
    1368           0 :                                     srs.IsGeographic() ? "LONGITUDE" : "X");
    1369           0 :         yRange = CPLString().Printf("%s UNBOUNDED ",
    1370           0 :                                     srs.IsGeographic() ? "LATITUDE" : "Y");
    1371             :     }
    1372             : 
    1373           0 :     CPLString organization;
    1374           0 :     if (authorityName != nullptr && authorityCode > 0)
    1375             :     {
    1376           0 :         organization = CPLString().Printf(
    1377             :             "ORGANIZATION %s IDENTIFIED BY %d",
    1378           0 :             QuotedIdentifier(authorityName).c_str(), authorityCode);
    1379             :     }
    1380             : 
    1381           0 :     CPLString sql = CPLString().Printf(
    1382             :         "CREATE SPATIAL REFERENCE SYSTEM %s "
    1383             :         "IDENTIFIED BY %d "
    1384             :         "TYPE %s "
    1385             :         "LINEAR UNIT OF MEASURE %s "
    1386             :         "ANGULAR UNIT OF MEASURE %s "
    1387             :         "%s "  // ELLIPSOID
    1388             :         "COORDINATE %s "
    1389             :         "COORDINATE %s "
    1390             :         "%s "  // ORGANIZATION
    1391             :         "DEFINITION %s "
    1392             :         "TRANSFORM DEFINITION %s",
    1393           0 :         QuotedIdentifier(refName).c_str(), srid,
    1394           0 :         srs.IsGeographic() ? "ROUND EARTH" : "PLANAR",
    1395           0 :         QuotedIdentifier(
    1396           0 :             (linearUnits == nullptr || EQUAL(linearUnits, "unknown"))
    1397             :                 ? "metre"
    1398             :                 : linearUnits)
    1399           0 :             .tolower()
    1400             :             .c_str(),
    1401           0 :         QuotedIdentifier(
    1402           0 :             (angularUnits == nullptr || EQUAL(angularUnits, "unknown"))
    1403             :                 ? "degree"
    1404             :                 : angularUnits)
    1405           0 :             .tolower()
    1406             :             .c_str(),
    1407           0 :         (ellipsoidParams.empty() ? ""
    1408           0 :                                  : ("ELLIPSOID" + ellipsoidParams).c_str()),
    1409             :         xRange.c_str(), yRange.c_str(), organization.c_str(),
    1410           0 :         Literal(wkt).c_str(), Literal(proj4).c_str());
    1411             : 
    1412           0 :     ExecuteSQL(sql);
    1413           0 : }
    1414             : 
    1415             : /************************************************************************/
    1416             : /*                       CreateParseArrayFunctions()                    */
    1417             : /************************************************************************/
    1418             : 
    1419           1 : void OGRHanaDataSource::CreateParseArrayFunctions(const char *schemaName)
    1420             : {
    1421           8 :     auto replaceAll = [](const CPLString &str, const CPLString &before,
    1422             :                          const CPLString &after) -> CPLString
    1423             :     {
    1424          16 :         CPLString res = str;
    1425          16 :         return res.replaceAll(before, after);
    1426             :     };
    1427             : 
    1428             :     // clang-format off
    1429             :     const CPLString parseStringArrayFunc =
    1430             :         "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_STRING_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
    1431             :           "RETURNS TABLE(VALUE NVARCHAR(512))\n"
    1432             :           "LANGUAGE SQLSCRIPT\n"
    1433             :           "SQL SECURITY INVOKER AS\n"
    1434             :         "BEGIN\n"
    1435             :             "DECLARE arrValues NVARCHAR(512) ARRAY;\n"
    1436             :             "DECLARE idx INTEGER = 1;\n"
    1437             :             "DECLARE curPos INTEGER = 1;\n"
    1438             :             "DECLARE lastPos INTEGER = 1;\n"
    1439             :             "DECLARE delimiterLength INTEGER = LENGTH(delimiter);\n"
    1440             : 
    1441             :             "IF(NOT(:str IS NULL)) THEN\n"
    1442             :                "WHILE(:curPos > 0) DO\n"
    1443             :                    "curPos = LOCATE(:str, :delimiter, :lastPos);\n"
    1444             :                    "IF :curPos = 0 THEN\n"
    1445             :                         "BREAK;\n"
    1446             :                     "END IF;\n"
    1447             : 
    1448             :                     "arrValues[:idx] = SUBSTRING(:str, :lastPos, :curPos - :lastPos);\n"
    1449             :                     "lastPos = :curPos + :delimiterLength;\n"
    1450             :                     "idx = :idx + 1;\n"
    1451             :                 "END WHILE;\n"
    1452             : 
    1453             :                 "arrValues[:idx] = SUBSTRING(:str, :lastPos, LENGTH(:str));\n"
    1454             :             "END IF;\n"
    1455             : 
    1456             :             "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
    1457             :             "RETURN SELECT * FROM :ret;\n"
    1458           2 :         "END;\n";
    1459             :     // clang-format on
    1460             : 
    1461             :     CPLString sql = replaceAll(parseStringArrayFunc, "{SCHEMA}",
    1462           3 :                                QuotedIdentifier(schemaName));
    1463           1 :     ExecuteSQL(sql);
    1464             : 
    1465             :     // clang-format off
    1466             :     const CPLString parseTypeArrayFunc =
    1467             :         "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_{TYPE}_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
    1468             :            "RETURNS TABLE(VALUE {TYPE})\n"
    1469             :            "LANGUAGE SQLSCRIPT\n"
    1470             :            "SQL SECURITY INVOKER AS\n"
    1471             :         "BEGIN\n"
    1472             :             "DECLARE arrValues {TYPE} ARRAY;\n"
    1473             :             "DECLARE elemValue STRING;\n"
    1474             :             "DECLARE idx INTEGER = 1;\n"
    1475             :             "DECLARE CURSOR cursor_values FOR\n"
    1476             :                   "SELECT * FROM OGR_PARSE_STRING_ARRAY(:str, :delimiter);\n"
    1477             : 
    1478             :             "FOR row_value AS cursor_values DO\n"
    1479             :                 "elemValue = TRIM(row_value.VALUE);\n"
    1480             :                 "IF(UPPER(elemValue) = 'NULL') THEN\n"
    1481             :                     "arrValues[:idx] = CAST(NULL AS {TYPE});\n"
    1482             :                 "ELSE\n"
    1483             :                     "arrValues[:idx] = CAST(:elemValue AS {TYPE});\n"
    1484             :                 "END IF;\n"
    1485             :                 "idx = :idx + 1;\n"
    1486             :             "END FOR;\n"
    1487             : 
    1488             :             "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
    1489             :             "RETURN SELECT * FROM :ret;\n"
    1490           2 :         "END;\n";
    1491             :     // clang-format on
    1492             : 
    1493           2 :     sql = replaceAll(parseTypeArrayFunc, "{SCHEMA}",
    1494           3 :                      QuotedIdentifier(schemaName));
    1495             : 
    1496           8 :     for (const CPLString &type : GetSupportedArrayTypes())
    1497             :     {
    1498           7 :         if (type == "STRING")
    1499           1 :             continue;
    1500           6 :         ExecuteSQL(replaceAll(sql, "{TYPE}", type));
    1501             :     }
    1502           1 : }
    1503             : 
    1504             : /************************************************************************/
    1505             : /*                       ParseArrayFunctionsExist()                     */
    1506             : /************************************************************************/
    1507             : 
    1508           1 : bool OGRHanaDataSource::ParseArrayFunctionsExist(const char *schemaName)
    1509             : {
    1510           1 :     const char *sql =
    1511             :         "SELECT COUNT(*) FROM FUNCTIONS WHERE SCHEMA_NAME = ? AND "
    1512             :         "FUNCTION_NAME LIKE 'OGR_PARSE_%_ARRAY'";
    1513           2 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
    1514           1 :     stmt->setString(1, odbc::String(schemaName));
    1515           1 :     odbc::ResultSetRef rsFunctions = stmt->executeQuery();
    1516           1 :     auto numFunctions = rsFunctions->next() ? *rsFunctions->getLong(1) : 0;
    1517           1 :     rsFunctions->close();
    1518           1 :     return (static_cast<std::size_t>(numFunctions) ==
    1519           2 :             GetSupportedArrayTypes().size());
    1520             : }
    1521             : 
    1522             : /************************************************************************/
    1523             : /*                               GetLayer()                             */
    1524             : /************************************************************************/
    1525             : 
    1526          43 : OGRLayer *OGRHanaDataSource::GetLayer(int index)
    1527             : {
    1528          43 :     if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
    1529           6 :         return nullptr;
    1530          37 :     return layers_[static_cast<std::size_t>(index)].get();
    1531             : }
    1532             : 
    1533             : /************************************************************************/
    1534             : /*                           GetLayerByName()                           */
    1535             : /************************************************************************/
    1536             : 
    1537          39 : OGRLayer *OGRHanaDataSource::GetLayerByName(const char *name)
    1538             : {
    1539          39 :     return GetLayer(FindLayerByName(name));
    1540             : }
    1541             : 
    1542             : /************************************************************************/
    1543             : /*                              ICreateLayer()                          */
    1544             : /************************************************************************/
    1545             : 
    1546             : OGRLayer *
    1547          18 : OGRHanaDataSource::ICreateLayer(const char *layerNameIn,
    1548             :                                 const OGRGeomFieldDefn *poGeomFieldDefn,
    1549             :                                 CSLConstList options)
    1550             : {
    1551          18 :     if (layerNameIn == nullptr)
    1552           0 :         return nullptr;
    1553             : 
    1554             :     const auto geomType =
    1555          18 :         poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    1556             :     const auto srs =
    1557          18 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
    1558             : 
    1559             :     // Check if we are allowed to create new objects in the database
    1560          36 :     odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
    1561          18 :     if (dmd->isReadOnly())
    1562             :     {
    1563           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1564             :                  "Unable to create Layer %s.\n"
    1565             :                  "Database %s is read only.",
    1566           0 :                  layerNameIn, dmd->getDatabaseName().c_str());
    1567           0 :         return nullptr;
    1568             :     }
    1569             : 
    1570          18 :     bool launderNames = CPLFetchBool(
    1571             :         options, OGRHanaLayerCreationOptionsConstants::LAUNDER, true);
    1572          36 :     CPLString layerName(layerNameIn);
    1573          18 :     if (launderNames)
    1574             :     {
    1575          17 :         auto nameRes = LaunderName(layerNameIn);
    1576          17 :         if (nameRes.first != OGRERR_NONE)
    1577           0 :             return nullptr;
    1578          17 :         layerName.swap(nameRes.second);
    1579             :     }
    1580             : 
    1581          18 :     CPLDebug("HANA", "Creating layer %s.", layerName.c_str());
    1582             : 
    1583          18 :     int layerIndex = FindLayerByName(layerName.c_str());
    1584          18 :     if (layerIndex >= 0)
    1585             :     {
    1586           0 :         bool overwriteLayer = CPLFetchBool(
    1587             :             options, OGRHanaLayerCreationOptionsConstants::OVERWRITE, false);
    1588           0 :         if (!overwriteLayer)
    1589             :         {
    1590           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1591             :                      "Layer %s already exists, CreateLayer failed.\n"
    1592             :                      "Use the layer creation option OVERWRITE=YES to "
    1593             :                      "replace it.",
    1594             :                      layerName.c_str());
    1595           0 :             return nullptr;
    1596             :         }
    1597             : 
    1598           0 :         DeleteLayer(layerIndex);
    1599             :     }
    1600             : 
    1601             :     int batchSize =
    1602          18 :         CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::BATCH_SIZE,
    1603             :                     DEFAULT_BATCH_SIZE);
    1604          18 :     if (batchSize <= 0)
    1605             :     {
    1606           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1607             :                  "Unable to create layer %s. The value of %s parameter must be "
    1608             :                  "greater than 0.",
    1609             :                  layerName.c_str(),
    1610             :                  OGRHanaLayerCreationOptionsConstants::BATCH_SIZE);
    1611           0 :         return nullptr;
    1612             :     }
    1613             : 
    1614          18 :     int defaultStringSize = CPLFetchInt(
    1615             :         options, OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE,
    1616             :         DEFAULT_STRING_SIZE);
    1617          18 :     if (defaultStringSize <= 0)
    1618             :     {
    1619           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1620             :                  "Unable to create layer %s. The value of %s parameter must be "
    1621             :                  "greater than 0.",
    1622             :                  layerName.c_str(),
    1623             :                  OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE);
    1624           0 :         return nullptr;
    1625             :     }
    1626             : 
    1627             :     CPLString geomColumnName(CSLFetchNameValueDef(
    1628             :         options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NAME,
    1629          36 :         "OGR_GEOMETRY"));
    1630          18 :     if (launderNames)
    1631             :     {
    1632          17 :         auto nameRes = LaunderName(geomColumnName.c_str());
    1633          17 :         if (nameRes.first != OGRERR_NONE)
    1634           0 :             return nullptr;
    1635          17 :         geomColumnName.swap(nameRes.second);
    1636             :     }
    1637             : 
    1638          18 :     const bool geomColumnNullable = CPLFetchBool(
    1639             :         options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NULLABLE, true);
    1640             :     CPLString geomColumnIndexType(CSLFetchNameValueDef(
    1641             :         options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_INDEX,
    1642          36 :         "DEFAULT"));
    1643             : 
    1644          18 :     const char *paramFidName = CSLFetchNameValueDef(
    1645             :         options, OGRHanaLayerCreationOptionsConstants::FID, "OGR_FID");
    1646          36 :     CPLString fidName(paramFidName);
    1647          18 :     if (launderNames)
    1648             :     {
    1649          17 :         auto nameRes = LaunderName(paramFidName);
    1650          17 :         if (nameRes.first != OGRERR_NONE)
    1651           0 :             return nullptr;
    1652          17 :         fidName.swap(nameRes.second);
    1653             :     }
    1654             : 
    1655             :     CPLString fidType =
    1656          18 :         CPLFetchBool(options, OGRHanaLayerCreationOptionsConstants::FID64,
    1657             :                      false)
    1658             :             ? "BIGINT"
    1659          36 :             : "INTEGER";
    1660             : 
    1661          18 :     CPLDebug("HANA", "Geometry Column Name %s.", geomColumnName.c_str());
    1662          18 :     CPLDebug("HANA", "FID Column Name %s, Type %s.", fidName.c_str(),
    1663             :              fidType.c_str());
    1664             : 
    1665          18 :     int srid = CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::SRID,
    1666             :                            UNDETERMINED_SRID);
    1667          18 :     if (srid < 0 && srs != nullptr)
    1668          12 :         srid = GetSrsId(srs);
    1669             : 
    1670             :     try
    1671             :     {
    1672          18 :         CreateTable(layerName, fidName, fidType, geomColumnName, geomType,
    1673             :                     geomColumnNullable, geomColumnIndexType, srid);
    1674             :     }
    1675           0 :     catch (const odbc::Exception &ex)
    1676             :     {
    1677           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1678             :                  "Unable to create layer %s. CreateLayer failed:%s\n",
    1679           0 :                  layerName.c_str(), ex.what());
    1680           0 :         return nullptr;
    1681             :     }
    1682             : 
    1683             :     // Create new layer object
    1684           0 :     auto layer = std::make_unique<OGRHanaTableLayer>(this, schemaName_.c_str(),
    1685          36 :                                                      layerName.c_str(), true);
    1686          18 :     if (geomType != wkbNone && layer->GetLayerDefn()->GetGeomFieldCount() > 0)
    1687          12 :         layer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
    1688          18 :     if (batchSize > 0)
    1689          18 :         layer->SetBatchSize(static_cast<std::size_t>(batchSize));
    1690          18 :     if (defaultStringSize > 0)
    1691          18 :         layer->SetDefaultStringSize(
    1692             :             static_cast<std::size_t>(defaultStringSize));
    1693          18 :     layer->SetLaunderFlag(launderNames);
    1694          18 :     layer->SetPrecisionFlag(CPLFetchBool(
    1695             :         options, OGRHanaLayerCreationOptionsConstants::PRECISION, true));
    1696          18 :     layer->SetCustomColumnTypes(CSLFetchNameValue(
    1697             :         options, OGRHanaLayerCreationOptionsConstants::COLUMN_TYPES));
    1698             : 
    1699          18 :     layers_.push_back(std::move(layer));
    1700             : 
    1701          18 :     return layers_.back().get();
    1702             : }
    1703             : 
    1704             : /************************************************************************/
    1705             : /*                           TestCapability()                           */
    1706             : /************************************************************************/
    1707             : 
    1708          27 : int OGRHanaDataSource::TestCapability(const char *capabilities)
    1709             : {
    1710          27 :     if (EQUAL(capabilities, ODsCCreateLayer))
    1711           4 :         return updateMode_;
    1712          23 :     else if (EQUAL(capabilities, ODsCDeleteLayer))
    1713           4 :         return updateMode_;
    1714          19 :     else if (EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
    1715           2 :         return updateMode_;
    1716          17 :     else if (EQUAL(capabilities, ODsCMeasuredGeometries))
    1717           5 :         return TRUE;
    1718          12 :     else if (EQUAL(capabilities, ODsCRandomLayerWrite))
    1719           0 :         return updateMode_;
    1720          12 :     else if (EQUAL(capabilities, ODsCTransactions))
    1721           4 :         return TRUE;
    1722             :     else
    1723           8 :         return FALSE;
    1724             : }
    1725             : 
    1726             : /************************************************************************/
    1727             : /*                             ExecuteSQL()                             */
    1728             : /************************************************************************/
    1729             : 
    1730          29 : OGRLayer *OGRHanaDataSource::ExecuteSQL(const char *sqlCommand,
    1731             :                                         OGRGeometry *spatialFilter,
    1732             :                                         const char *dialect)
    1733             : {
    1734          29 :     sqlCommand = SkipLeadingSpaces(sqlCommand);
    1735             : 
    1736          29 :     if (IsGenericSQLDialect(dialect))
    1737           0 :         return GDALDataset::ExecuteSQL(sqlCommand, spatialFilter, dialect);
    1738             : 
    1739          29 :     if (STARTS_WITH_CI(sqlCommand, "DELLAYER:"))
    1740             :     {
    1741          10 :         const char *layerName = SkipLeadingSpaces(sqlCommand + 9);
    1742          10 :         int layerIndex = FindLayerByName(layerName);
    1743          10 :         if (layerIndex >= 0)
    1744           0 :             DeleteLayer(layerIndex);
    1745          10 :         return nullptr;
    1746             :     }
    1747          19 :     if (STARTS_WITH_CI(sqlCommand, "SELECT"))
    1748             :     {
    1749          28 :         auto stmt = PrepareStatement(sqlCommand);
    1750          14 :         if (stmt.isNull())
    1751           1 :             return nullptr;
    1752             : 
    1753          26 :         auto layer = std::make_unique<OGRHanaResultLayer>(this, sqlCommand);
    1754          13 :         if (spatialFilter != nullptr)
    1755           1 :             layer->SetSpatialFilter(spatialFilter);
    1756          13 :         return layer.release();
    1757             :     }
    1758             : 
    1759             :     try
    1760             :     {
    1761           8 :         ExecuteSQL(sqlCommand);
    1762             :     }
    1763           6 :     catch (const odbc::Exception &ex)
    1764             :     {
    1765           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1766             :                  "Failed to execute SQL statement '%s': %s", sqlCommand,
    1767           3 :                  ex.what());
    1768             :     }
    1769             : 
    1770           5 :     return nullptr;
    1771             : }
    1772             : 
    1773             : /************************************************************************/
    1774             : /*                           StartTransaction()                         */
    1775             : /************************************************************************/
    1776             : 
    1777          15 : OGRErr OGRHanaDataSource::StartTransaction(CPL_UNUSED int bForce)
    1778             : {
    1779          15 :     if (isTransactionStarted_)
    1780             :     {
    1781           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1782             :                  "Transaction already established");
    1783           2 :         return OGRERR_FAILURE;
    1784             :     }
    1785             : 
    1786          13 :     isTransactionStarted_ = true;
    1787          13 :     return OGRERR_NONE;
    1788             : }
    1789             : 
    1790             : /************************************************************************/
    1791             : /*                           CommitTransaction()                        */
    1792             : /************************************************************************/
    1793             : 
    1794          10 : OGRErr OGRHanaDataSource::CommitTransaction()
    1795             : {
    1796          10 :     if (!isTransactionStarted_)
    1797             :     {
    1798           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
    1799           2 :         return OGRERR_FAILURE;
    1800             :     }
    1801             : 
    1802           8 :     isTransactionStarted_ = false;
    1803             : 
    1804             :     try
    1805             :     {
    1806         127 :         for (size_t i = 0; i < layers_.size(); ++i)
    1807             :         {
    1808         119 :             OGRHanaLayer *layer = static_cast<OGRHanaLayer *>(layers_[i].get());
    1809         119 :             if (layer->IsTableLayer())
    1810             :             {
    1811         119 :                 OGRHanaTableLayer *tableLayer =
    1812             :                     static_cast<OGRHanaTableLayer *>(layer);
    1813         119 :                 tableLayer->FlushPendingBatches(false);
    1814             :             }
    1815             :         }
    1816             : 
    1817           8 :         conn_->commit();
    1818             :     }
    1819           0 :     catch (const odbc::Exception &ex)
    1820             :     {
    1821           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1822           0 :                  "Failed to commit transaction: %s", ex.what());
    1823           0 :         return OGRERR_FAILURE;
    1824             :     }
    1825           8 :     return OGRERR_NONE;
    1826             : }
    1827             : 
    1828             : /************************************************************************/
    1829             : /*                           RollbackTransaction()                      */
    1830             : /************************************************************************/
    1831             : 
    1832           6 : OGRErr OGRHanaDataSource::RollbackTransaction()
    1833             : {
    1834           6 :     if (!isTransactionStarted_)
    1835             :     {
    1836           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
    1837           2 :         return OGRERR_FAILURE;
    1838             :     }
    1839             : 
    1840           4 :     isTransactionStarted_ = false;
    1841             : 
    1842             :     try
    1843             :     {
    1844           4 :         conn_->rollback();
    1845             :     }
    1846           0 :     catch (const odbc::Exception &ex)
    1847             :     {
    1848           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1849           0 :                  "Failed to roll back transaction: %s", ex.what());
    1850           0 :         return OGRERR_FAILURE;
    1851             :     }
    1852           4 :     return OGRERR_NONE;
    1853             : }

Generated by: LCOV version 1.14