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 884 76.5 %
Date: 2024-05-14 13:00:50 Functions: 53 56 94.6 %

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

Generated by: LCOV version 1.14