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: 2026-01-30 03:16:47 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, CSLConstList openOptions,
     557             :                             int update)
     558             : {
     559          65 :     CPLAssert(layers_.size() == 0);
     560             : 
     561          65 :     if (!STARTS_WITH_CI(newName, GetPrefix()))
     562             :     {
     563           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     564             :                  "%s does not conform to HANA driver naming convention,"
     565             :                  " %s*\n",
     566             :                  newName, GetPrefix());
     567           0 :         return FALSE;
     568             :     }
     569             : 
     570          65 :     updateMode_ = update;
     571          65 :     detectGeometryType_ = CPLFetchBool(
     572             :         openOptions, OGRHanaOpenOptionsConstants::DETECT_GEOMETRY_TYPE, true);
     573             : 
     574          65 :     std::size_t prefixLength = strlen(GetPrefix());
     575             :     char **connOptions =
     576          65 :         CSLTokenizeStringComplex(newName + prefixLength, ";", TRUE, FALSE);
     577             : 
     578          65 :     const char *paramSchema = CSLFetchNameValueDef(
     579             :         connOptions, OGRHanaOpenOptionsConstants::SCHEMA, nullptr);
     580          65 :     if (paramSchema != nullptr)
     581          63 :         schemaName_ = paramSchema;
     582             : 
     583          65 :     int ret = FALSE;
     584             : 
     585          65 :     CPLString connectionStr = BuildConnectionString(connOptions);
     586             : 
     587          65 :     if (!connectionStr.empty())
     588             :     {
     589          56 :         connEnv_ = odbc::Environment::create();
     590          56 :         conn_ = connEnv_->createConnection();
     591          56 :         conn_->setAutoCommit(false);
     592             : 
     593          56 :         const char *paramConnTimeout = CSLFetchNameValueDef(
     594             :             connOptions, OGRHanaOpenOptionsConstants::CONNECTION_TIMEOUT,
     595             :             nullptr);
     596          56 :         if (paramConnTimeout != nullptr)
     597           0 :             conn_->setConnectionTimeout(
     598           0 :                 static_cast<unsigned long>(atoi(paramConnTimeout)));
     599             : 
     600             :         try
     601             :         {
     602          56 :             conn_->connect(connectionStr.c_str());
     603             :         }
     604           0 :         catch (const odbc::Exception &ex)
     605             :         {
     606           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     607           0 :                      "HANA connection failed: %s\n", ex.what());
     608             :         }
     609             : 
     610          56 :         if (conn_->connected())
     611             :         {
     612          56 :             DetermineVersions();
     613             : 
     614          56 :             const char *paramTables = CSLFetchNameValueDef(
     615             :                 connOptions, OGRHanaOpenOptionsConstants::TABLES, "");
     616          56 :             InitializeLayers(paramSchema, paramTables);
     617          56 :             ret = TRUE;
     618             :         }
     619             :     }
     620             : 
     621          65 :     CSLDestroy(connOptions);
     622             : 
     623          65 :     return ret;
     624             : }
     625             : 
     626             : /************************************************************************/
     627             : /*                            DeleteLayer()                             */
     628             : /************************************************************************/
     629             : 
     630           0 : OGRErr OGRHanaDataSource::DeleteLayer(int index)
     631             : {
     632           0 :     if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
     633           0 :         return OGRERR_FAILURE;
     634             : 
     635             :     const std::unique_ptr<OGRLayer> &layer =
     636           0 :         layers_[static_cast<std::size_t>(index)];
     637           0 :     CPLDebug("HANA", "DeleteLayer(%s)", layer->GetName());
     638             : 
     639           0 :     if (auto tableLayer = dynamic_cast<OGRHanaTableLayer *>(layer.get()))
     640             :     {
     641           0 :         OGRErr err = tableLayer->DropTable();
     642           0 :         if (OGRERR_NONE == err)
     643           0 :             return err;
     644             :     }
     645             : 
     646           0 :     layers_.erase(layers_.begin() + index);
     647             : 
     648           0 :     return OGRERR_NONE;
     649             : }
     650             : 
     651          18 : void OGRHanaDataSource::CreateTable(
     652             :     const CPLString &tableName, const CPLString &fidName,
     653             :     const CPLString &fidType, const CPLString &geomColumnName,
     654             :     OGRwkbGeometryType geomType, bool geomColumnNullable,
     655             :     const CPLString &geomColumnIndexType, int geomSrid)
     656             : {
     657          36 :     CPLString sql;
     658          50 :     if (geomType == OGRwkbGeometryType::wkbNone ||
     659          32 :         !(!geomColumnName.empty() && geomSrid >= 0))
     660             :     {
     661           6 :         sql = "CREATE COLUMN TABLE " +
     662          18 :               GetFullTableNameQuoted(schemaName_, tableName) + " (" +
     663          24 :               QuotedIdentifier(fidName) + " " + fidType +
     664          12 :               " GENERATED BY DEFAULT AS IDENTITY, PRIMARY KEY ( " +
     665          18 :               QuotedIdentifier(fidName) + "));";
     666             :     }
     667             :     else
     668             :     {
     669          12 :         sql = "CREATE COLUMN TABLE " +
     670          36 :               GetFullTableNameQuoted(schemaName_, tableName) + " (" +
     671          48 :               QuotedIdentifier(fidName) + " " + fidType +
     672          24 :               " GENERATED BY DEFAULT AS IDENTITY, " +
     673          48 :               QuotedIdentifier(geomColumnName) + " ST_GEOMETRY (" +
     674          48 :               std::to_string(geomSrid) + ")" +
     675          24 :               (geomColumnNullable ? "" : " NOT NULL") +
     676          36 :               " SPATIAL INDEX PREFERENCE " + geomColumnIndexType +
     677          36 :               ", PRIMARY KEY ( " + QuotedIdentifier(fidName) + "));";
     678             :     }
     679             : 
     680          18 :     ExecuteSQL(sql);
     681          18 : }
     682             : 
     683          56 : void OGRHanaDataSource::DetermineVersions()
     684             : {
     685          56 :     odbc::DatabaseMetaDataRef dbmd = conn_->getDatabaseMetaData();
     686          56 :     CPLString dbVersion(dbmd->getDBMSVersion());
     687          56 :     hanaVersion_ = HanaVersion::fromString(dbVersion);
     688             : 
     689          56 :     if (hanaVersion_.major() < 4)
     690             :     {
     691           0 :         cloudVersion_ = HanaVersion(0, 0, 0);
     692           0 :         return;
     693             :     }
     694             : 
     695         112 :     odbc::StatementRef stmt = conn_->createStatement();
     696          56 :     const char *sql = "SELECT CLOUD_VERSION FROM SYS.M_DATABASE;";
     697             : 
     698         112 :     odbc::ResultSetRef rsVersion = stmt->executeQuery(sql);
     699          56 :     if (rsVersion->next())
     700             :         cloudVersion_ =
     701          56 :             HanaVersion::fromString(rsVersion->getString(1)->c_str());
     702             : 
     703          56 :     rsVersion->close();
     704             : }
     705             : 
     706             : /************************************************************************/
     707             : /*                      FindSchemaAndTableNames()                       */
     708             : /************************************************************************/
     709             : 
     710             : std::pair<CPLString, CPLString>
     711          15 : OGRHanaDataSource::FindSchemaAndTableNames(const char *query)
     712             : {
     713          30 :     odbc::PreparedStatementRef stmt = PrepareStatement(query);
     714          15 :     if (stmt.get() == nullptr)
     715           0 :         return {"", ""};
     716             : 
     717          30 :     odbc::ResultSetMetaDataRef rsmd = stmt->getMetaData();
     718             : 
     719             :     // Note, getTableName returns correct table name also in the case
     720             :     // when the original sql query uses a view
     721          30 :     CPLString tableName = rsmd->getTableName(1);
     722          15 :     if (tableName == "M_DATABASE_")
     723           0 :         tableName = "M_DATABASE";
     724          30 :     CPLString schemaName = rsmd->getSchemaName(1);
     725          15 :     if (schemaName.empty() && !tableName.empty())
     726          14 :         schemaName = FindSchemaName(tableName.c_str());
     727          15 :     return {schemaName, tableName};
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                          FindLayerByName()                           */
     732             : /************************************************************************/
     733             : 
     734          67 : int OGRHanaDataSource::FindLayerByName(const char *name)
     735             : {
     736         604 :     for (size_t i = 0; i < layers_.size(); ++i)
     737             :     {
     738         574 :         if (EQUAL(name, layers_[i]->GetName()))
     739          37 :             return static_cast<int>(i);
     740             :     }
     741          30 :     return -1;
     742             : }
     743             : 
     744             : /************************************************************************/
     745             : /*                           FindSchemaName()                           */
     746             : /************************************************************************/
     747             : 
     748          14 : CPLString OGRHanaDataSource::FindSchemaName(const char *objectName)
     749             : {
     750          26 :     auto getSchemaName = [&](const char *sql)
     751             :     {
     752          52 :         odbc::PreparedStatementRef stmt = PrepareStatement(sql);
     753          26 :         stmt->setString(1, odbc::String(objectName));
     754          52 :         odbc::ResultSetRef rsEntries = stmt->executeQuery();
     755          26 :         CPLString ret;
     756          40 :         while (rsEntries->next())
     757             :         {
     758             :             // return empty string if there is more than one schema.
     759          26 :             if (!ret.empty())
     760             :             {
     761          12 :                 ret.clear();
     762          12 :                 break;
     763             :             }
     764          14 :             ret = *rsEntries->getString(1);
     765             :         }
     766          26 :         rsEntries->close();
     767             : 
     768          52 :         return ret;
     769          14 :     };
     770             : 
     771             :     CPLString ret = getSchemaName(
     772          14 :         "SELECT SCHEMA_NAME FROM SYS.TABLES WHERE TABLE_NAME = ?");
     773          14 :     if (ret.empty())
     774          24 :         ret = getSchemaName(
     775          12 :             "SELECT SCHEMA_NAME FROM SYS.VIEWS WHERE VIEW_NAME = ?");
     776             : 
     777          28 :     return ret;
     778             : }
     779             : 
     780             : /************************************************************************/
     781             : /*                          CreateStatement()                           */
     782             : /************************************************************************/
     783             : 
     784         224 : odbc::StatementRef OGRHanaDataSource::CreateStatement()
     785             : {
     786         224 :     return conn_->createStatement();
     787             : }
     788             : 
     789             : /************************************************************************/
     790             : /*                          PrepareStatement()                          */
     791             : /************************************************************************/
     792             : 
     793         335 : odbc::PreparedStatementRef OGRHanaDataSource::PrepareStatement(const char *sql)
     794             : {
     795         335 :     CPLAssert(sql != nullptr);
     796             : 
     797             :     try
     798             :     {
     799         335 :         CPLDebug("HANA", "Prepare statement %s.", sql);
     800             : 
     801         670 :         std::u16string sqlUtf16 = odbc::StringConverter::utf8ToUtf16(sql);
     802         335 :         return conn_->prepareStatement(sqlUtf16.c_str());
     803             :     }
     804           2 :     catch (const odbc::Exception &ex)
     805             :     {
     806           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to prepare statement: %s",
     807           1 :                  ex.what());
     808             :     }
     809           1 :     return nullptr;
     810             : }
     811             : 
     812             : /************************************************************************/
     813             : /*                               Commit()                               */
     814             : /************************************************************************/
     815             : 
     816         134 : void OGRHanaDataSource::Commit()
     817             : {
     818         134 :     conn_->commit();
     819         134 : }
     820             : 
     821             : /************************************************************************/
     822             : /*                             ExecuteSQL()                             */
     823             : /************************************************************************/
     824             : 
     825          96 : void OGRHanaDataSource::ExecuteSQL(const CPLString &sql)
     826             : {
     827             :     std::u16string sqlUtf16 =
     828         192 :         odbc::StringConverter::utf8ToUtf16(sql.c_str(), sql.length());
     829         192 :     odbc::StatementRef stmt = conn_->createStatement();
     830          96 :     stmt->execute(sqlUtf16.c_str());
     831          92 :     if (!IsTransactionStarted())
     832          91 :         conn_->commit();
     833          92 : }
     834             : 
     835             : /************************************************************************/
     836             : /*                            GetSrsById()                              */
     837             : /*                                                                      */
     838             : /*      Return a SRS corresponding to a particular id.  The returned    */
     839             : /*      object has its reference counter incremented. Consequently      */
     840             : /*      the caller should call Release() on it (if not null) once done  */
     841             : /*      with it.                                                        */
     842             : /************************************************************************/
     843             : 
     844          30 : OGRSpatialReference *OGRHanaDataSource::GetSrsById(int srid)
     845             : {
     846          30 :     if (srid < 0)
     847           0 :         return nullptr;
     848             : 
     849          30 :     auto it = srsCache_.find(srid);
     850          30 :     if (it != srsCache_.end())
     851             :     {
     852           2 :         it->second->Reference();
     853           2 :         return it->second;
     854             :     }
     855             : 
     856          28 :     OGRSpatialReference *srs = nullptr;
     857             : 
     858          28 :     CPLString wkt = GetSrsWktById(*conn_, srid);
     859          28 :     if (!wkt.empty())
     860             :     {
     861          28 :         srs = new OGRSpatialReference();
     862          28 :         OGRErr err = srs->importFromWkt(wkt.c_str());
     863          28 :         if (OGRERR_NONE != err)
     864             :         {
     865           0 :             delete srs;
     866           0 :             srs = nullptr;
     867             :         }
     868             :     }
     869             : 
     870          28 :     srsCache_.insert({srid, srs});
     871             : 
     872          28 :     if (srs)
     873          28 :         srs->Reference();
     874          28 :     return srs;
     875             : }
     876             : 
     877             : /************************************************************************/
     878             : /*                              GetSrsId()                              */
     879             : /************************************************************************/
     880             : 
     881          14 : int OGRHanaDataSource::GetSrsId(const OGRSpatialReference *srs)
     882             : {
     883          14 :     if (srs == nullptr)
     884           1 :         return UNDETERMINED_SRID;
     885             : 
     886             :     /* -------------------------------------------------------------------- */
     887             :     /*      Try to find srs id using authority name and code (EPSG:3857).   */
     888             :     /* -------------------------------------------------------------------- */
     889          26 :     OGRSpatialReference srsLocal(*srs);
     890             : 
     891          13 :     const char *authorityName = srsLocal.GetAuthorityName(nullptr);
     892          13 :     if (authorityName == nullptr || strlen(authorityName) == 0)
     893             :     {
     894           0 :         srsLocal.AutoIdentifyEPSG();
     895           0 :         authorityName = srsLocal.GetAuthorityName(nullptr);
     896           0 :         if (authorityName != nullptr && EQUAL(authorityName, "EPSG"))
     897             :         {
     898           0 :             const char *authorityCode = srsLocal.GetAuthorityCode(nullptr);
     899           0 :             if (authorityCode != nullptr && strlen(authorityCode) > 0)
     900             :             {
     901           0 :                 srsLocal.importFromEPSG(atoi(authorityCode));
     902           0 :                 authorityName = srsLocal.GetAuthorityName(nullptr);
     903             :             }
     904             :         }
     905             :     }
     906             : 
     907          13 :     int authorityCode = 0;
     908          13 :     if (authorityName != nullptr)
     909             :     {
     910          13 :         authorityCode = atoi(srsLocal.GetAuthorityCode(nullptr));
     911          13 :         if (authorityCode > 0)
     912             :         {
     913          13 :             int ret = GetSridWithFilter(
     914             :                 *conn_,
     915          26 :                 CPLString().Printf("SRS_ID = %d AND ORGANIZATION = '%s'",
     916          13 :                                    authorityCode, authorityName));
     917          13 :             if (ret != UNDETERMINED_SRID)
     918          13 :                 return ret;
     919             :         }
     920             :     }
     921             : 
     922             :     /* -------------------------------------------------------------------- */
     923             :     /*      Try to find srs id using wkt content.                           */
     924             :     /* -------------------------------------------------------------------- */
     925             : 
     926           0 :     char *wkt = nullptr;
     927           0 :     OGRErr err = srsLocal.exportToWkt(&wkt);
     928           0 :     CPLString strWkt(wkt);
     929           0 :     CPLFree(wkt);
     930             : 
     931           0 :     if (OGRERR_NONE != err)
     932           0 :         return UNDETERMINED_SRID;
     933             : 
     934           0 :     int srid = GetSridWithFilter(
     935           0 :         *conn_, CPLString().Printf("DEFINITION = '%s'", strWkt.c_str()));
     936           0 :     if (srid != UNDETERMINED_SRID)
     937           0 :         return srid;
     938             : 
     939             :     /* -------------------------------------------------------------------- */
     940             :     /*      Try to add a new spatial reference system to the database       */
     941             :     /* -------------------------------------------------------------------- */
     942             : 
     943           0 :     char *proj4 = nullptr;
     944           0 :     err = srsLocal.exportToProj4(&proj4);
     945           0 :     CPLString strProj4(proj4);
     946           0 :     CPLFree(proj4);
     947             : 
     948           0 :     if (OGRERR_NONE != err)
     949           0 :         return srid;
     950             : 
     951           0 :     if (authorityName != nullptr && authorityCode > 0)
     952             :     {
     953           0 :         srid = authorityCode;
     954             :     }
     955             :     else
     956             :     {
     957           0 :         odbc::StatementRef stmt = conn_->createStatement();
     958           0 :         const char *sql =
     959             :             "SELECT MAX(SRS_ID) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS WHERE "
     960             :             "SRS_ID >= 10000000 AND SRS_ID < 20000000";
     961           0 :         odbc::ResultSetRef rsSrid = stmt->executeQuery(sql);
     962           0 :         while (rsSrid->next())
     963             :         {
     964           0 :             odbc::Int val = rsSrid->getInt(1);
     965           0 :             srid = val.isNull() ? 10000000 : *val + 1;
     966             :         }
     967           0 :         rsSrid->close();
     968             :     }
     969             : 
     970             :     try
     971             :     {
     972           0 :         CreateSpatialReferenceSystem(srsLocal, srid, authorityName,
     973             :                                      authorityCode, strWkt, strProj4);
     974           0 :         return srid;
     975             :     }
     976           0 :     catch (const odbc::Exception &ex)
     977             :     {
     978           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     979           0 :                  "Unable to create an SRS in the database: %s.\n", ex.what());
     980             :     }
     981             : 
     982           0 :     return UNDETERMINED_SRID;
     983             : }
     984             : 
     985             : /************************************************************************/
     986             : /*                          IsSrsRoundEarth()                           */
     987             : /************************************************************************/
     988             : 
     989          12 : bool OGRHanaDataSource::IsSrsRoundEarth(int srid)
     990             : {
     991          12 :     const char *sql =
     992             :         "SELECT ROUND_EARTH FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
     993             :         "WHERE SRS_ID = ?";
     994          24 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
     995          12 :     stmt->setInt(1, odbc::Int(srid));
     996          12 :     odbc::ResultSetRef rs = stmt->executeQuery();
     997          12 :     bool ret = false;
     998          12 :     if (rs->next())
     999          12 :         ret = (*rs->getString(1) == "TRUE");
    1000          12 :     rs->close();
    1001          24 :     return ret;
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                       HasSrsPlanarEquivalent()                       */
    1006             : /************************************************************************/
    1007             : 
    1008           2 : bool OGRHanaDataSource::HasSrsPlanarEquivalent(int srid)
    1009             : {
    1010           2 :     const char *sql = "SELECT COUNT(*) FROM SYS.ST_SPATIAL_REFERENCE_SYSTEMS "
    1011             :                       "WHERE SRS_ID = ?";
    1012           4 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
    1013           2 :     stmt->setInt(1, ToPlanarSRID(srid));
    1014           2 :     odbc::ResultSetRef rs = stmt->executeQuery();
    1015           2 :     std::int64_t count = 0;
    1016           2 :     if (rs->next())
    1017           2 :         count = *rs->getLong(1);
    1018           2 :     rs->close();
    1019           4 :     return count > 0;
    1020             : }
    1021             : 
    1022             : /************************************************************************/
    1023             : /*                          GetQueryColumns()                           */
    1024             : /************************************************************************/
    1025             : 
    1026          55 : OGRErr OGRHanaDataSource::GetQueryColumns(
    1027             :     const CPLString &schemaName, const CPLString &query,
    1028             :     std::vector<ColumnDescription> &columnDescriptions)
    1029             : {
    1030          55 :     columnDescriptions.clear();
    1031             : 
    1032         110 :     odbc::PreparedStatementRef stmtQuery = PrepareStatement(query);
    1033             : 
    1034          55 :     if (stmtQuery.isNull())
    1035           0 :         return OGRERR_FAILURE;
    1036             : 
    1037         110 :     odbc::ResultSetMetaDataRef rsmd = stmtQuery->getMetaData();
    1038          55 :     std::size_t numColumns = rsmd->getColumnCount();
    1039          55 :     if (numColumns == 0)
    1040           0 :         return OGRERR_NONE;
    1041             : 
    1042          55 :     columnDescriptions.reserve(numColumns);
    1043             : 
    1044         110 :     odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
    1045             :     odbc::PreparedStatementRef stmtArrayTypeInfo =
    1046             :         PrepareStatement("SELECT DATA_TYPE_NAME FROM "
    1047             :                          "SYS.TABLE_COLUMNS_ODBC WHERE SCHEMA_NAME = ? "
    1048             :                          "AND TABLE_NAME = ? AND COLUMN_NAME = ? AND "
    1049         110 :                          "DATA_TYPE_NAME LIKE '% ARRAY'");
    1050             : 
    1051         254 :     for (unsigned short clmIndex = 1; clmIndex <= numColumns; ++clmIndex)
    1052             :     {
    1053         199 :         CPLString typeName = rsmd->getColumnTypeName(clmIndex);
    1054             : 
    1055         199 :         if (typeName.empty())
    1056           0 :             continue;
    1057             : 
    1058         199 :         bool isArray = false;
    1059         199 :         CPLString tableName = rsmd->getTableName(clmIndex);
    1060         199 :         CPLString columnName = rsmd->getColumnName(clmIndex);
    1061         199 :         CPLString defaultValue;
    1062         199 :         short dataType = rsmd->getColumnType(clmIndex);
    1063             : 
    1064         199 :         if (!schemaName.empty() && !tableName.empty())
    1065             :         {
    1066             :             // Retrieve information about default value in column
    1067             :             odbc::ResultSetRef rsColumns =
    1068             :                 dmd->getColumns(nullptr, schemaName.c_str(), tableName.c_str(),
    1069         141 :                                 columnName.c_str());
    1070         141 :             if (rsColumns->next())
    1071             :             {
    1072             :                 odbc::String defaultValueStr =
    1073         276 :                     rsColumns->getString(13 /*COLUMN_DEF*/);
    1074         138 :                 if (!defaultValueStr.isNull())
    1075             :                     defaultValue =
    1076          12 :                         FormatDefaultValue(defaultValueStr->c_str(), dataType);
    1077             :             }
    1078         141 :             rsColumns->close();
    1079             : 
    1080             :             // Retrieve information about array type
    1081         141 :             stmtArrayTypeInfo->setString(1, schemaName);
    1082         141 :             stmtArrayTypeInfo->setString(2, tableName);
    1083         141 :             stmtArrayTypeInfo->setString(3, columnName);
    1084         141 :             odbc::ResultSetRef rsArrayTypes = stmtArrayTypeInfo->executeQuery();
    1085         141 :             if (rsArrayTypes->next())
    1086             :             {
    1087          11 :                 typeName = *rsArrayTypes->getString(1);
    1088          11 :                 dataType = GetArrayDataType(typeName);
    1089             : 
    1090          11 :                 if (dataType == QGRHanaDataTypes::Unknown)
    1091             :                 {
    1092           0 :                     CPLError(
    1093             :                         CE_Failure, CPLE_AppDefined,
    1094             :                         "GetQueryColumns(): Unsupported type of array (%s)",
    1095             :                         typeName.c_str());
    1096           0 :                     return OGRERR_FAILURE;
    1097             :                 }
    1098             : 
    1099          11 :                 isArray = true;
    1100             :             }
    1101         141 :             rsArrayTypes->close();
    1102             :         }
    1103             : 
    1104         199 :         if (!isArray && !IsKnownDataType(dataType))
    1105             :         {
    1106           0 :             odbc::ResultSetRef rsTypeInfo = dmd->getTypeInfo(dataType);
    1107           0 :             if (rsTypeInfo->next())
    1108             :             {
    1109           0 :                 odbc::String name = rsTypeInfo->getString(1);
    1110           0 :                 if (name.isNull())
    1111           0 :                     continue;
    1112           0 :                 if (name->compare("SHORTTEXT") == 0 ||
    1113           0 :                     name->compare("ALPHANUM") == 0)
    1114             :                 {
    1115           0 :                     dataType = QGRHanaDataTypes::WVarChar;
    1116             :                 }
    1117             :             }
    1118           0 :             rsTypeInfo->close();
    1119             :         }
    1120             : 
    1121         199 :         if (dataType == QGRHanaDataTypes::Geometry)
    1122             :         {
    1123          33 :             GeometryColumnDescription geometryColumnDesc;
    1124          33 :             if (schemaName.empty() || tableName.empty())
    1125          18 :                 geometryColumnDesc = GetGeometryColumnDescription(
    1126          18 :                     *conn_, query, columnName, detectGeometryType_);
    1127             :             else
    1128          48 :                 geometryColumnDesc = GetGeometryColumnDescription(
    1129             :                     *conn_, schemaName, tableName, columnName,
    1130          48 :                     detectGeometryType_);
    1131          33 :             geometryColumnDesc.isNullable = rsmd->isNullable(clmIndex);
    1132             : 
    1133          66 :             columnDescriptions.push_back({true, AttributeColumnDescription(),
    1134          33 :                                           std::move(geometryColumnDesc)});
    1135             :         }
    1136             :         else
    1137             :         {
    1138         166 :             AttributeColumnDescription attributeColumnDesc;
    1139         166 :             attributeColumnDesc.name = std::move(columnName);
    1140         166 :             attributeColumnDesc.type = dataType;
    1141         166 :             attributeColumnDesc.typeName = std::move(typeName);
    1142         166 :             attributeColumnDesc.isArray = isArray;
    1143         166 :             attributeColumnDesc.isNullable = rsmd->isNullable(clmIndex);
    1144         166 :             attributeColumnDesc.isAutoIncrement =
    1145         166 :                 rsmd->isAutoIncrement(clmIndex);
    1146         166 :             attributeColumnDesc.length =
    1147         166 :                 static_cast<int>(rsmd->getColumnLength(clmIndex));
    1148         166 :             attributeColumnDesc.precision = rsmd->getPrecision(clmIndex);
    1149         166 :             attributeColumnDesc.scale = rsmd->getScale(clmIndex);
    1150         166 :             attributeColumnDesc.defaultValue = std::move(defaultValue);
    1151             : 
    1152         166 :             columnDescriptions.push_back({false, std::move(attributeColumnDesc),
    1153             :                                           GeometryColumnDescription()});
    1154             :         }
    1155             :     }
    1156             : 
    1157          55 :     return OGRERR_NONE;
    1158             : }
    1159             : 
    1160             : /************************************************************************/
    1161             : /*                        GetTablePrimaryKeys()                         */
    1162             : /************************************************************************/
    1163             : 
    1164             : std::vector<CPLString>
    1165          55 : OGRHanaDataSource::GetTablePrimaryKeys(const char *schemaName,
    1166             :                                        const char *tableName)
    1167             : {
    1168          55 :     std::vector<CPLString> ret;
    1169             : 
    1170         110 :     odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
    1171             :     odbc::ResultSetRef rsPrimaryKeys =
    1172         110 :         dmd->getPrimaryKeys(nullptr, schemaName, tableName);
    1173         129 :     while (rsPrimaryKeys->next())
    1174             :     {
    1175          74 :         ret.push_back(*rsPrimaryKeys->getString(4));
    1176             :     }
    1177          55 :     rsPrimaryKeys->close();
    1178             : 
    1179         110 :     return ret;
    1180             : }
    1181             : 
    1182             : /************************************************************************/
    1183             : /*                          InitializeLayers()                          */
    1184             : /************************************************************************/
    1185             : 
    1186          56 : void OGRHanaDataSource::InitializeLayers(const char *schemaName,
    1187             :                                          const char *tableNames)
    1188             : {
    1189         112 :     std::vector<CPLString> tablesToFind = SplitStrings(tableNames, ",");
    1190          56 :     const bool hasTablesToFind = !tablesToFind.empty();
    1191             : 
    1192         112 :     auto addLayersFromQuery = [&](const char *query, bool updatable)
    1193             :     {
    1194         224 :         odbc::PreparedStatementRef stmt = PrepareStatement(query);
    1195         112 :         stmt->setString(1, odbc::String(schemaName));
    1196         224 :         odbc::ResultSetRef rsTables = stmt->executeQuery();
    1197         579 :         while (rsTables->next())
    1198             :         {
    1199         467 :             odbc::String tableName = rsTables->getString(1);
    1200         467 :             if (tableName.isNull())
    1201           0 :                 continue;
    1202             :             auto pos =
    1203         467 :                 std::find(tablesToFind.begin(), tablesToFind.end(), *tableName);
    1204         467 :             if (pos != tablesToFind.end())
    1205           0 :                 tablesToFind.erase(pos);
    1206             : 
    1207             :             auto layer = std::make_unique<OGRHanaTableLayer>(
    1208         467 :                 this, schemaName_.c_str(), tableName->c_str(), updatable);
    1209         467 :             layers_.push_back(std::move(layer));
    1210             :         }
    1211         112 :         rsTables->close();
    1212         112 :     };
    1213             : 
    1214             :     // Look for layers in Tables
    1215         112 :     std::ostringstream osTables;
    1216          56 :     osTables << "SELECT TABLE_NAME FROM SYS.TABLES WHERE SCHEMA_NAME = ?";
    1217          56 :     if (!tablesToFind.empty())
    1218             :         osTables << " AND TABLE_NAME IN ("
    1219           0 :                  << JoinStrings(tablesToFind, ",", Literal) << ")";
    1220             : 
    1221          56 :     addLayersFromQuery(osTables.str().c_str(), updateMode_);
    1222             : 
    1223          56 :     if (!(hasTablesToFind && tablesToFind.empty()))
    1224             :     {
    1225             :         // Look for layers in Views
    1226          56 :         std::ostringstream osViews;
    1227          56 :         osViews << "SELECT VIEW_NAME FROM SYS.VIEWS WHERE SCHEMA_NAME = ?";
    1228             :         // cppcheck-suppress knownConditionTrueFalse
    1229          56 :         if (!tablesToFind.empty())
    1230             :             osViews << " AND VIEW_NAME IN ("
    1231           0 :                     << JoinStrings(tablesToFind, ",", Literal) << ")";
    1232             : 
    1233          56 :         addLayersFromQuery(osViews.str().c_str(), false);
    1234             :     }
    1235             : 
    1236             :     // Report about tables that could not be found
    1237          56 :     for (const auto &tableName : tablesToFind)
    1238             :     {
    1239           0 :         const char *layerName = tableName.c_str();
    1240           0 :         if (GetLayerByName(layerName) == nullptr)
    1241           0 :             CPLDebug("HANA",
    1242             :                      "Table '%s' not found or does not "
    1243             :                      "have any geometry column.",
    1244             :                      layerName);
    1245             :     }
    1246          56 : }
    1247             : 
    1248             : /************************************************************************/
    1249             : /*                            LaunderName()                             */
    1250             : /************************************************************************/
    1251             : 
    1252         115 : std::pair<OGRErr, CPLString> OGRHanaDataSource::LaunderName(const char *name)
    1253             : {
    1254         115 :     CPLAssert(name != nullptr);
    1255             : 
    1256         115 :     if (!CPLIsUTF8(name, -1))
    1257             :     {
    1258           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s is not a valid UTF-8 string.",
    1259             :                  name);
    1260           0 :         return {OGRERR_FAILURE, ""};
    1261             :     }
    1262             : 
    1263        1050 :     auto getUTF8SequenceLength = [](char c)
    1264             :     {
    1265        1050 :         if ((c & 0x80) == 0x00)
    1266        1040 :             return 1;
    1267          10 :         if ((c & 0xE0) == 0xC0)
    1268           6 :             return 2;
    1269           4 :         if ((c & 0xF0) == 0xE0)
    1270           0 :             return 3;
    1271           4 :         if ((c & 0xF8) == 0xF0)
    1272           4 :             return 4;
    1273             : 
    1274           0 :         throw std::runtime_error("Invalid UTF-8 sequence");
    1275             :     };
    1276             : 
    1277         230 :     CPLString newName(name);
    1278         115 :     bool hasNonASCII = false;
    1279         115 :     size_t i = 0;
    1280             : 
    1281        1165 :     while (name[i] != '\0')
    1282             :     {
    1283        1050 :         char c = name[i];
    1284        1050 :         int len = getUTF8SequenceLength(c);
    1285        1050 :         if (len == 1)
    1286             :         {
    1287        1040 :             if (c == '-' || c == '#')
    1288           6 :                 newName[i] = '_';
    1289             :             else
    1290        1034 :                 newName[i] = static_cast<char>(
    1291        1034 :                     CPLToupper(static_cast<unsigned char>(c)));
    1292             :         }
    1293             :         else
    1294             :         {
    1295          10 :             hasNonASCII = true;
    1296             :         }
    1297             : 
    1298        1050 :         i += len;
    1299             :     }
    1300             : 
    1301         115 :     if (!hasNonASCII)
    1302         222 :         return {OGRERR_NONE, newName};
    1303             : 
    1304           4 :     const char *sql = "SELECT UPPER(?) FROM DUMMY";
    1305           8 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
    1306           4 :     stmt->setString(1, odbc::String(newName.c_str()));
    1307           8 :     odbc::ResultSetRef rsName = stmt->executeQuery();
    1308           4 :     OGRErr err = OGRERR_NONE;
    1309           4 :     if (rsName->next())
    1310             :     {
    1311           4 :         newName.swap(*rsName->getString(1));
    1312             :     }
    1313             :     else
    1314             :     {
    1315           0 :         err = OGRERR_FAILURE;
    1316           0 :         newName.clear();
    1317             :     }
    1318           4 :     rsName->close();
    1319           4 :     return {err, newName};
    1320             : }
    1321             : 
    1322             : /************************************************************************/
    1323             : /*                       CreateSpatialReference()                       */
    1324             : /************************************************************************/
    1325             : 
    1326           0 : void OGRHanaDataSource::CreateSpatialReferenceSystem(
    1327             :     const OGRSpatialReference &srs, int srid, const char *authorityName,
    1328             :     int authorityCode, const CPLString &wkt, const CPLString &proj4)
    1329             : {
    1330           0 :     CPLString refName((srs.IsProjected()) ? srs.GetAttrValue("PROJCS")
    1331           0 :                                           : srs.GetAttrValue("GEOGCS"));
    1332           0 :     if (refName.empty() || EQUAL(refName.c_str(), "UNKNOWN"))
    1333           0 :         refName = "OGR_PROJECTION_" + std::to_string(srid);
    1334             : 
    1335           0 :     OGRErr err = OGRERR_NONE;
    1336           0 :     CPLString ellipsoidParams;
    1337           0 :     const double semiMajor = srs.GetSemiMajor(&err);
    1338           0 :     if (OGRERR_NONE == err)
    1339           0 :         ellipsoidParams += " SEMI MAJOR AXIS " + std::to_string(semiMajor);
    1340           0 :     const double semiMinor = srs.GetSemiMinor(&err);
    1341           0 :     const double invFlattening = srs.GetInvFlattening(&err);
    1342           0 :     if (OGRERR_NONE == err)
    1343             :         ellipsoidParams +=
    1344           0 :             " INVERSE FLATTENING " + std::to_string(invFlattening);
    1345             :     else
    1346           0 :         ellipsoidParams += " SEMI MINOR AXIS " + std::to_string(semiMinor);
    1347             : 
    1348           0 :     const char *linearUnits = nullptr;
    1349           0 :     srs.GetLinearUnits(&linearUnits);
    1350           0 :     const char *angularUnits = nullptr;
    1351           0 :     srs.GetAngularUnits(&angularUnits);
    1352             : 
    1353           0 :     CPLString xRange, yRange;
    1354             :     double dfWestLongitudeDeg, dfSouthLatitudeDeg, dfEastLongitudeDeg,
    1355             :         dfNorthLatitudeDeg;
    1356           0 :     if (srs.GetAreaOfUse(&dfWestLongitudeDeg, &dfSouthLatitudeDeg,
    1357             :                          &dfEastLongitudeDeg, &dfNorthLatitudeDeg, nullptr))
    1358             :     {
    1359           0 :         xRange = CPLString().Printf("%s BETWEEN %f AND %f",
    1360           0 :                                     srs.IsGeographic() ? "LONGITUDE" : "X",
    1361           0 :                                     dfWestLongitudeDeg, dfEastLongitudeDeg);
    1362           0 :         yRange = CPLString().Printf("%s BETWEEN %f AND %f",
    1363           0 :                                     srs.IsGeographic() ? "LATITUDE" : "Y",
    1364           0 :                                     dfSouthLatitudeDeg, dfNorthLatitudeDeg);
    1365             :     }
    1366             :     else
    1367             :     {
    1368           0 :         xRange = CPLString().Printf("%s UNBOUNDED",
    1369           0 :                                     srs.IsGeographic() ? "LONGITUDE" : "X");
    1370           0 :         yRange = CPLString().Printf("%s UNBOUNDED ",
    1371           0 :                                     srs.IsGeographic() ? "LATITUDE" : "Y");
    1372             :     }
    1373             : 
    1374           0 :     CPLString organization;
    1375           0 :     if (authorityName != nullptr && authorityCode > 0)
    1376             :     {
    1377           0 :         organization = CPLString().Printf(
    1378             :             "ORGANIZATION %s IDENTIFIED BY %d",
    1379           0 :             QuotedIdentifier(authorityName).c_str(), authorityCode);
    1380             :     }
    1381             : 
    1382           0 :     CPLString sql = CPLString().Printf(
    1383             :         "CREATE SPATIAL REFERENCE SYSTEM %s "
    1384             :         "IDENTIFIED BY %d "
    1385             :         "TYPE %s "
    1386             :         "LINEAR UNIT OF MEASURE %s "
    1387             :         "ANGULAR UNIT OF MEASURE %s "
    1388             :         "%s "  // ELLIPSOID
    1389             :         "COORDINATE %s "
    1390             :         "COORDINATE %s "
    1391             :         "%s "  // ORGANIZATION
    1392             :         "DEFINITION %s "
    1393             :         "TRANSFORM DEFINITION %s",
    1394           0 :         QuotedIdentifier(refName).c_str(), srid,
    1395           0 :         srs.IsGeographic() ? "ROUND EARTH" : "PLANAR",
    1396           0 :         QuotedIdentifier(
    1397           0 :             (linearUnits == nullptr || EQUAL(linearUnits, "unknown"))
    1398             :                 ? "metre"
    1399             :                 : linearUnits)
    1400           0 :             .tolower()
    1401             :             .c_str(),
    1402           0 :         QuotedIdentifier(
    1403           0 :             (angularUnits == nullptr || EQUAL(angularUnits, "unknown"))
    1404             :                 ? "degree"
    1405             :                 : angularUnits)
    1406           0 :             .tolower()
    1407             :             .c_str(),
    1408           0 :         (ellipsoidParams.empty() ? ""
    1409           0 :                                  : ("ELLIPSOID" + ellipsoidParams).c_str()),
    1410             :         xRange.c_str(), yRange.c_str(), organization.c_str(),
    1411           0 :         Literal(wkt).c_str(), Literal(proj4).c_str());
    1412             : 
    1413           0 :     ExecuteSQL(sql);
    1414           0 : }
    1415             : 
    1416             : /************************************************************************/
    1417             : /*                     CreateParseArrayFunctions()                      */
    1418             : /************************************************************************/
    1419             : 
    1420           1 : void OGRHanaDataSource::CreateParseArrayFunctions(const char *schemaName)
    1421             : {
    1422           8 :     auto replaceAll = [](const CPLString &str, const CPLString &before,
    1423             :                          const CPLString &after) -> CPLString
    1424             :     {
    1425          16 :         CPLString res = str;
    1426          16 :         return res.replaceAll(before, after);
    1427             :     };
    1428             : 
    1429             :     // clang-format off
    1430             :     const CPLString parseStringArrayFunc =
    1431             :         "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_STRING_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
    1432             :           "RETURNS TABLE(VALUE NVARCHAR(512))\n"
    1433             :           "LANGUAGE SQLSCRIPT\n"
    1434             :           "SQL SECURITY INVOKER AS\n"
    1435             :         "BEGIN\n"
    1436             :             "DECLARE arrValues NVARCHAR(512) ARRAY;\n"
    1437             :             "DECLARE idx INTEGER = 1;\n"
    1438             :             "DECLARE curPos INTEGER = 1;\n"
    1439             :             "DECLARE lastPos INTEGER = 1;\n"
    1440             :             "DECLARE delimiterLength INTEGER = LENGTH(delimiter);\n"
    1441             : 
    1442             :             "IF(NOT(:str IS NULL)) THEN\n"
    1443             :                "WHILE(:curPos > 0) DO\n"
    1444             :                    "curPos = LOCATE(:str, :delimiter, :lastPos);\n"
    1445             :                    "IF :curPos = 0 THEN\n"
    1446             :                         "BREAK;\n"
    1447             :                     "END IF;\n"
    1448             : 
    1449             :                     "arrValues[:idx] = SUBSTRING(:str, :lastPos, :curPos - :lastPos);\n"
    1450             :                     "lastPos = :curPos + :delimiterLength;\n"
    1451             :                     "idx = :idx + 1;\n"
    1452             :                 "END WHILE;\n"
    1453             : 
    1454             :                 "arrValues[:idx] = SUBSTRING(:str, :lastPos, LENGTH(:str));\n"
    1455             :             "END IF;\n"
    1456             : 
    1457             :             "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
    1458             :             "RETURN SELECT * FROM :ret;\n"
    1459           2 :         "END;\n";
    1460             :     // clang-format on
    1461             : 
    1462             :     CPLString sql = replaceAll(parseStringArrayFunc, "{SCHEMA}",
    1463           3 :                                QuotedIdentifier(schemaName));
    1464           1 :     ExecuteSQL(sql);
    1465             : 
    1466             :     // clang-format off
    1467             :     const CPLString parseTypeArrayFunc =
    1468             :         "CREATE OR REPLACE FUNCTION {SCHEMA}.OGR_PARSE_{TYPE}_ARRAY(IN str NCLOB, IN delimiter NVARCHAR(10))\n"
    1469             :            "RETURNS TABLE(VALUE {TYPE})\n"
    1470             :            "LANGUAGE SQLSCRIPT\n"
    1471             :            "SQL SECURITY INVOKER AS\n"
    1472             :         "BEGIN\n"
    1473             :             "DECLARE arrValues {TYPE} ARRAY;\n"
    1474             :             "DECLARE elemValue STRING;\n"
    1475             :             "DECLARE idx INTEGER = 1;\n"
    1476             :             "DECLARE CURSOR cursor_values FOR\n"
    1477             :                   "SELECT * FROM OGR_PARSE_STRING_ARRAY(:str, :delimiter);\n"
    1478             : 
    1479             :             "FOR row_value AS cursor_values DO\n"
    1480             :                 "elemValue = TRIM(row_value.VALUE);\n"
    1481             :                 "IF(UPPER(elemValue) = 'NULL') THEN\n"
    1482             :                     "arrValues[:idx] = CAST(NULL AS {TYPE});\n"
    1483             :                 "ELSE\n"
    1484             :                     "arrValues[:idx] = CAST(:elemValue AS {TYPE});\n"
    1485             :                 "END IF;\n"
    1486             :                 "idx = :idx + 1;\n"
    1487             :             "END FOR;\n"
    1488             : 
    1489             :             "ret = UNNEST(:arrValues) AS(\"VALUE\");\n"
    1490             :             "RETURN SELECT * FROM :ret;\n"
    1491           2 :         "END;\n";
    1492             :     // clang-format on
    1493             : 
    1494           2 :     sql = replaceAll(parseTypeArrayFunc, "{SCHEMA}",
    1495           3 :                      QuotedIdentifier(schemaName));
    1496             : 
    1497           8 :     for (const CPLString &type : GetSupportedArrayTypes())
    1498             :     {
    1499           7 :         if (type == "STRING")
    1500           1 :             continue;
    1501           6 :         ExecuteSQL(replaceAll(sql, "{TYPE}", type));
    1502             :     }
    1503           1 : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                      ParseArrayFunctionsExist()                      */
    1507             : /************************************************************************/
    1508             : 
    1509           1 : bool OGRHanaDataSource::ParseArrayFunctionsExist(const char *schemaName)
    1510             : {
    1511           1 :     const char *sql =
    1512             :         "SELECT COUNT(*) FROM FUNCTIONS WHERE SCHEMA_NAME = ? AND "
    1513             :         "FUNCTION_NAME LIKE 'OGR_PARSE_%_ARRAY'";
    1514           2 :     odbc::PreparedStatementRef stmt = PrepareStatement(sql);
    1515           1 :     stmt->setString(1, odbc::String(schemaName));
    1516           1 :     odbc::ResultSetRef rsFunctions = stmt->executeQuery();
    1517           1 :     auto numFunctions = rsFunctions->next() ? *rsFunctions->getLong(1) : 0;
    1518           1 :     rsFunctions->close();
    1519           1 :     return (static_cast<std::size_t>(numFunctions) ==
    1520           2 :             GetSupportedArrayTypes().size());
    1521             : }
    1522             : 
    1523             : /************************************************************************/
    1524             : /*                              GetLayer()                              */
    1525             : /************************************************************************/
    1526             : 
    1527          43 : const OGRLayer *OGRHanaDataSource::GetLayer(int index) const
    1528             : {
    1529          43 :     if (index < 0 || static_cast<std::size_t>(index) >= layers_.size())
    1530           6 :         return nullptr;
    1531          37 :     return layers_[static_cast<std::size_t>(index)].get();
    1532             : }
    1533             : 
    1534             : /************************************************************************/
    1535             : /*                           GetLayerByName()                           */
    1536             : /************************************************************************/
    1537             : 
    1538          39 : OGRLayer *OGRHanaDataSource::GetLayerByName(const char *name)
    1539             : {
    1540          39 :     return GetLayer(FindLayerByName(name));
    1541             : }
    1542             : 
    1543             : /************************************************************************/
    1544             : /*                            ICreateLayer()                            */
    1545             : /************************************************************************/
    1546             : 
    1547             : OGRLayer *
    1548          18 : OGRHanaDataSource::ICreateLayer(const char *layerNameIn,
    1549             :                                 const OGRGeomFieldDefn *poGeomFieldDefn,
    1550             :                                 CSLConstList options)
    1551             : {
    1552          18 :     if (layerNameIn == nullptr)
    1553           0 :         return nullptr;
    1554             : 
    1555             :     const auto geomType =
    1556          18 :         poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
    1557             :     const auto srs =
    1558          18 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
    1559             : 
    1560             :     // Check if we are allowed to create new objects in the database
    1561          36 :     odbc::DatabaseMetaDataRef dmd = conn_->getDatabaseMetaData();
    1562          18 :     if (dmd->isReadOnly())
    1563             :     {
    1564           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1565             :                  "Unable to create Layer %s.\n"
    1566             :                  "Database %s is read only.",
    1567           0 :                  layerNameIn, dmd->getDatabaseName().c_str());
    1568           0 :         return nullptr;
    1569             :     }
    1570             : 
    1571          18 :     bool launderNames = CPLFetchBool(
    1572             :         options, OGRHanaLayerCreationOptionsConstants::LAUNDER, true);
    1573          36 :     CPLString layerName(layerNameIn);
    1574          18 :     if (launderNames)
    1575             :     {
    1576          17 :         auto nameRes = LaunderName(layerNameIn);
    1577          17 :         if (nameRes.first != OGRERR_NONE)
    1578           0 :             return nullptr;
    1579          17 :         layerName.swap(nameRes.second);
    1580             :     }
    1581             : 
    1582          18 :     CPLDebug("HANA", "Creating layer %s.", layerName.c_str());
    1583             : 
    1584          18 :     int layerIndex = FindLayerByName(layerName.c_str());
    1585          18 :     if (layerIndex >= 0)
    1586             :     {
    1587           0 :         bool overwriteLayer = CPLFetchBool(
    1588             :             options, OGRHanaLayerCreationOptionsConstants::OVERWRITE, false);
    1589           0 :         if (!overwriteLayer)
    1590             :         {
    1591           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1592             :                      "Layer %s already exists, CreateLayer failed.\n"
    1593             :                      "Use the layer creation option OVERWRITE=YES to "
    1594             :                      "replace it.",
    1595             :                      layerName.c_str());
    1596           0 :             return nullptr;
    1597             :         }
    1598             : 
    1599           0 :         DeleteLayer(layerIndex);
    1600             :     }
    1601             : 
    1602             :     int batchSize =
    1603          18 :         CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::BATCH_SIZE,
    1604             :                     DEFAULT_BATCH_SIZE);
    1605          18 :     if (batchSize <= 0)
    1606             :     {
    1607           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1608             :                  "Unable to create layer %s. The value of %s parameter must be "
    1609             :                  "greater than 0.",
    1610             :                  layerName.c_str(),
    1611             :                  OGRHanaLayerCreationOptionsConstants::BATCH_SIZE);
    1612           0 :         return nullptr;
    1613             :     }
    1614             : 
    1615          18 :     int defaultStringSize = CPLFetchInt(
    1616             :         options, OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE,
    1617             :         DEFAULT_STRING_SIZE);
    1618          18 :     if (defaultStringSize <= 0)
    1619             :     {
    1620           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1621             :                  "Unable to create layer %s. The value of %s parameter must be "
    1622             :                  "greater than 0.",
    1623             :                  layerName.c_str(),
    1624             :                  OGRHanaLayerCreationOptionsConstants::DEFAULT_STRING_SIZE);
    1625           0 :         return nullptr;
    1626             :     }
    1627             : 
    1628             :     CPLString geomColumnName(CSLFetchNameValueDef(
    1629             :         options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NAME,
    1630          36 :         "OGR_GEOMETRY"));
    1631          18 :     if (launderNames)
    1632             :     {
    1633          17 :         auto nameRes = LaunderName(geomColumnName.c_str());
    1634          17 :         if (nameRes.first != OGRERR_NONE)
    1635           0 :             return nullptr;
    1636          17 :         geomColumnName.swap(nameRes.second);
    1637             :     }
    1638             : 
    1639          18 :     const bool geomColumnNullable = CPLFetchBool(
    1640             :         options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_NULLABLE, true);
    1641             :     CPLString geomColumnIndexType(CSLFetchNameValueDef(
    1642             :         options, OGRHanaLayerCreationOptionsConstants::GEOMETRY_INDEX,
    1643          36 :         "DEFAULT"));
    1644             : 
    1645          18 :     const char *paramFidName = CSLFetchNameValueDef(
    1646             :         options, OGRHanaLayerCreationOptionsConstants::FID, "OGR_FID");
    1647          36 :     CPLString fidName(paramFidName);
    1648          18 :     if (launderNames)
    1649             :     {
    1650          17 :         auto nameRes = LaunderName(paramFidName);
    1651          17 :         if (nameRes.first != OGRERR_NONE)
    1652           0 :             return nullptr;
    1653          17 :         fidName.swap(nameRes.second);
    1654             :     }
    1655             : 
    1656             :     CPLString fidType =
    1657          18 :         CPLFetchBool(options, OGRHanaLayerCreationOptionsConstants::FID64,
    1658             :                      false)
    1659             :             ? "BIGINT"
    1660          36 :             : "INTEGER";
    1661             : 
    1662          18 :     CPLDebug("HANA", "Geometry Column Name %s.", geomColumnName.c_str());
    1663          18 :     CPLDebug("HANA", "FID Column Name %s, Type %s.", fidName.c_str(),
    1664             :              fidType.c_str());
    1665             : 
    1666          18 :     int srid = CPLFetchInt(options, OGRHanaLayerCreationOptionsConstants::SRID,
    1667             :                            UNDETERMINED_SRID);
    1668          18 :     if (srid < 0 && srs != nullptr)
    1669          12 :         srid = GetSrsId(srs);
    1670             : 
    1671             :     try
    1672             :     {
    1673          18 :         CreateTable(layerName, fidName, fidType, geomColumnName, geomType,
    1674             :                     geomColumnNullable, geomColumnIndexType, srid);
    1675             :     }
    1676           0 :     catch (const odbc::Exception &ex)
    1677             :     {
    1678           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1679             :                  "Unable to create layer %s. CreateLayer failed:%s\n",
    1680           0 :                  layerName.c_str(), ex.what());
    1681           0 :         return nullptr;
    1682             :     }
    1683             : 
    1684             :     // Create new layer object
    1685           0 :     auto layer = std::make_unique<OGRHanaTableLayer>(this, schemaName_.c_str(),
    1686          36 :                                                      layerName.c_str(), true);
    1687          18 :     if (geomType != wkbNone && layer->GetLayerDefn()->GetGeomFieldCount() > 0)
    1688          12 :         layer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
    1689          18 :     if (batchSize > 0)
    1690          18 :         layer->SetBatchSize(static_cast<std::size_t>(batchSize));
    1691          18 :     if (defaultStringSize > 0)
    1692          18 :         layer->SetDefaultStringSize(
    1693             :             static_cast<std::size_t>(defaultStringSize));
    1694          18 :     layer->SetLaunderFlag(launderNames);
    1695          18 :     layer->SetPrecisionFlag(CPLFetchBool(
    1696             :         options, OGRHanaLayerCreationOptionsConstants::PRECISION, true));
    1697          18 :     layer->SetCustomColumnTypes(CSLFetchNameValue(
    1698             :         options, OGRHanaLayerCreationOptionsConstants::COLUMN_TYPES));
    1699             : 
    1700          18 :     layers_.push_back(std::move(layer));
    1701             : 
    1702          18 :     return layers_.back().get();
    1703             : }
    1704             : 
    1705             : /************************************************************************/
    1706             : /*                           TestCapability()                           */
    1707             : /************************************************************************/
    1708             : 
    1709          27 : int OGRHanaDataSource::TestCapability(const char *capabilities) const
    1710             : {
    1711          27 :     if (EQUAL(capabilities, ODsCCreateLayer))
    1712           4 :         return updateMode_;
    1713          23 :     else if (EQUAL(capabilities, ODsCDeleteLayer))
    1714           4 :         return updateMode_;
    1715          19 :     else if (EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
    1716           2 :         return updateMode_;
    1717          17 :     else if (EQUAL(capabilities, ODsCMeasuredGeometries))
    1718           5 :         return TRUE;
    1719          12 :     else if (EQUAL(capabilities, ODsCRandomLayerWrite))
    1720           0 :         return updateMode_;
    1721          12 :     else if (EQUAL(capabilities, ODsCTransactions))
    1722           4 :         return TRUE;
    1723             :     else
    1724           8 :         return FALSE;
    1725             : }
    1726             : 
    1727             : /************************************************************************/
    1728             : /*                             ExecuteSQL()                             */
    1729             : /************************************************************************/
    1730             : 
    1731          29 : OGRLayer *OGRHanaDataSource::ExecuteSQL(const char *sqlCommand,
    1732             :                                         OGRGeometry *spatialFilter,
    1733             :                                         const char *dialect)
    1734             : {
    1735          29 :     sqlCommand = SkipLeadingSpaces(sqlCommand);
    1736             : 
    1737          29 :     if (IsGenericSQLDialect(dialect))
    1738           0 :         return GDALDataset::ExecuteSQL(sqlCommand, spatialFilter, dialect);
    1739             : 
    1740          29 :     if (STARTS_WITH_CI(sqlCommand, "DELLAYER:"))
    1741             :     {
    1742          10 :         const char *layerName = SkipLeadingSpaces(sqlCommand + 9);
    1743          10 :         int layerIndex = FindLayerByName(layerName);
    1744          10 :         if (layerIndex >= 0)
    1745           0 :             DeleteLayer(layerIndex);
    1746          10 :         return nullptr;
    1747             :     }
    1748          19 :     if (STARTS_WITH_CI(sqlCommand, "SELECT"))
    1749             :     {
    1750          28 :         auto stmt = PrepareStatement(sqlCommand);
    1751          14 :         if (stmt.isNull())
    1752           1 :             return nullptr;
    1753             : 
    1754          26 :         auto layer = std::make_unique<OGRHanaResultLayer>(this, sqlCommand);
    1755          13 :         if (spatialFilter != nullptr)
    1756           1 :             layer->SetSpatialFilter(spatialFilter);
    1757          13 :         return layer.release();
    1758             :     }
    1759             : 
    1760             :     try
    1761             :     {
    1762           8 :         ExecuteSQL(sqlCommand);
    1763             :     }
    1764           6 :     catch (const odbc::Exception &ex)
    1765             :     {
    1766           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1767             :                  "Failed to execute SQL statement '%s': %s", sqlCommand,
    1768           3 :                  ex.what());
    1769             :     }
    1770             : 
    1771           5 :     return nullptr;
    1772             : }
    1773             : 
    1774             : /************************************************************************/
    1775             : /*                          StartTransaction()                          */
    1776             : /************************************************************************/
    1777             : 
    1778          15 : OGRErr OGRHanaDataSource::StartTransaction(CPL_UNUSED int bForce)
    1779             : {
    1780          15 :     if (isTransactionStarted_)
    1781             :     {
    1782           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1783             :                  "Transaction already established");
    1784           2 :         return OGRERR_FAILURE;
    1785             :     }
    1786             : 
    1787          13 :     isTransactionStarted_ = true;
    1788          13 :     return OGRERR_NONE;
    1789             : }
    1790             : 
    1791             : /************************************************************************/
    1792             : /*                         CommitTransaction()                          */
    1793             : /************************************************************************/
    1794             : 
    1795          10 : OGRErr OGRHanaDataSource::CommitTransaction()
    1796             : {
    1797          10 :     if (!isTransactionStarted_)
    1798             :     {
    1799           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
    1800           2 :         return OGRERR_FAILURE;
    1801             :     }
    1802             : 
    1803           8 :     isTransactionStarted_ = false;
    1804             : 
    1805             :     try
    1806             :     {
    1807         127 :         for (size_t i = 0; i < layers_.size(); ++i)
    1808             :         {
    1809         119 :             OGRHanaLayer *layer = static_cast<OGRHanaLayer *>(layers_[i].get());
    1810         119 :             if (layer->IsTableLayer())
    1811             :             {
    1812         119 :                 OGRHanaTableLayer *tableLayer =
    1813             :                     static_cast<OGRHanaTableLayer *>(layer);
    1814         119 :                 tableLayer->FlushPendingBatches(false);
    1815             :             }
    1816             :         }
    1817             : 
    1818           8 :         conn_->commit();
    1819             :     }
    1820           0 :     catch (const odbc::Exception &ex)
    1821             :     {
    1822           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1823           0 :                  "Failed to commit transaction: %s", ex.what());
    1824           0 :         return OGRERR_FAILURE;
    1825             :     }
    1826           8 :     return OGRERR_NONE;
    1827             : }
    1828             : 
    1829             : /************************************************************************/
    1830             : /*                        RollbackTransaction()                         */
    1831             : /************************************************************************/
    1832             : 
    1833           6 : OGRErr OGRHanaDataSource::RollbackTransaction()
    1834             : {
    1835           6 :     if (!isTransactionStarted_)
    1836             :     {
    1837           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established");
    1838           2 :         return OGRERR_FAILURE;
    1839             :     }
    1840             : 
    1841           4 :     isTransactionStarted_ = false;
    1842             : 
    1843             :     try
    1844             :     {
    1845           4 :         conn_->rollback();
    1846             :     }
    1847           0 :     catch (const odbc::Exception &ex)
    1848             :     {
    1849           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1850           0 :                  "Failed to roll back transaction: %s", ex.what());
    1851           0 :         return OGRERR_FAILURE;
    1852             :     }
    1853           4 :     return OGRERR_NONE;
    1854             : }

Generated by: LCOV version 1.14