LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/hana - ogrhanatablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 673 1033 65.2 %
Date: 2025-05-31 00:00:17 Functions: 39 44 88.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  SAP HANA Spatial Driver
       4             :  * Purpose:  OGRHanaTableLayer class implementation
       5             :  * Author:   Maxim Rylov
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, SAP SE
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_hana.h"
      14             : #include "ogrhanafeaturereader.h"
      15             : #include "ogrhanautils.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <cmath>
      19             : #include <cstdio>
      20             : #include <cstring>
      21             : #include <limits>
      22             : #include <sstream>
      23             : 
      24             : #include "odbc/Exception.h"
      25             : #include "odbc/PreparedStatement.h"
      26             : #include "odbc/ResultSet.h"
      27             : #include "odbc/Statement.h"
      28             : #include "odbc/Types.h"
      29             : 
      30             : namespace OGRHANA
      31             : {
      32             : namespace
      33             : {
      34             : 
      35             : constexpr const char *UNSUPPORTED_OP_READ_ONLY =
      36             :     "%s : unsupported operation on a read-only datasource.";
      37             : 
      38          12 : const char *GetColumnDefaultValue(const OGRFieldDefn &field)
      39             : {
      40          12 :     const char *defaultValue = field.GetDefault();
      41          12 :     if (field.GetType() == OFTInteger && field.GetSubType() == OFSTBoolean)
      42           0 :         return (EQUAL(defaultValue, "1") || EQUAL(defaultValue, "'t'"))
      43           1 :                    ? "TRUE"
      44           1 :                    : "FALSE";
      45          11 :     return defaultValue;
      46             : }
      47             : 
      48           1 : CPLString FindGeomFieldName(const OGRFeatureDefn &featureDefn)
      49             : {
      50           1 :     if (featureDefn.GetGeomFieldCount() == 0)
      51           0 :         return "OGR_GEOMETRY";
      52             : 
      53           1 :     int numGeomFields = featureDefn.GetGeomFieldCount();
      54           1 :     for (int i = 1; i <= 2 * numGeomFields; ++i)
      55             :     {
      56           1 :         CPLString name = CPLSPrintf("OGR_GEOMETRY_%d", i);
      57           1 :         if (featureDefn.GetGeomFieldIndex(name) < 0)
      58           1 :             return name;
      59             :     }
      60             : 
      61           0 :     return "OGR_GEOMETRY";
      62             : }
      63             : 
      64          96 : CPLString GetParameterValue(short type, const CPLString &typeName, bool isArray)
      65             : {
      66          96 :     if (isArray)
      67             :     {
      68           4 :         CPLString arrayType = "STRING";
      69           4 :         switch (type)
      70             :         {
      71           0 :             case QGRHanaDataTypes::TinyInt:
      72           0 :                 arrayType = "TINYINT";
      73           0 :                 break;
      74           0 :             case QGRHanaDataTypes::SmallInt:
      75           0 :                 arrayType = "SMALLINT";
      76           0 :                 break;
      77           1 :             case QGRHanaDataTypes::Integer:
      78           1 :                 arrayType = "INT";
      79           1 :                 break;
      80           1 :             case QGRHanaDataTypes::BigInt:
      81           1 :                 arrayType = "BIGINT";
      82           1 :                 break;
      83           0 :             case QGRHanaDataTypes::Float:
      84             :             case QGRHanaDataTypes::Real:
      85           0 :                 arrayType = "REAL";
      86           0 :                 break;
      87           1 :             case QGRHanaDataTypes::Double:
      88           1 :                 arrayType = "DOUBLE";
      89           1 :                 break;
      90           1 :             case QGRHanaDataTypes::WVarChar:
      91           1 :                 arrayType = "STRING";
      92           1 :                 break;
      93             :         }
      94           8 :         return "ARRAY(SELECT * FROM OGR_PARSE_" + arrayType + "_ARRAY(?, '" +
      95           8 :                ARRAY_VALUES_DELIMITER + "'))";
      96             :     }
      97          92 :     else if (typeName.compare("NCLOB") == 0)
      98           0 :         return "TO_NCLOB(?)";
      99          92 :     else if (typeName.compare("CLOB") == 0)
     100           0 :         return "TO_CLOB(?)";
     101          92 :     else if (typeName.compare("BLOB") == 0)
     102           0 :         return "TO_BLOB(?)";
     103             :     else
     104          92 :         return "?";
     105             : }
     106             : 
     107           2 : std::vector<int> ParseIntValues(const std::string &str)
     108             : {
     109           2 :     std::vector<int> values;
     110             :     try
     111             :     {
     112           4 :         std::stringstream stream(str);
     113           6 :         while (stream.good())
     114             :         {
     115           4 :             std::string value;
     116           4 :             std::getline(stream, value, ',');
     117           4 :             values.push_back(std::stoi(value.c_str()));
     118             :         }
     119             :     }
     120           0 :     catch (...)
     121             :     {
     122           0 :         values.clear();
     123             :     }
     124           2 :     return values;
     125             : }
     126             : 
     127           3 : ColumnTypeInfo ParseColumnTypeInfo(const CPLString &typeDef)
     128             : {
     129           0 :     auto incorrectFormatErr = [&]()
     130             :     {
     131           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     132           0 :                  "Column type '%s' has incorrect format.", typeDef.c_str());
     133           3 :     };
     134             : 
     135           6 :     CPLString typeName;
     136           6 :     std::vector<int> typeSize;
     137             : 
     138           3 :     const size_t posStart = typeDef.find("(");
     139           3 :     if (posStart == std::string::npos)
     140             :     {
     141           1 :         typeName = typeDef;
     142             :     }
     143             :     else
     144             :     {
     145           2 :         const size_t posEnd = typeDef.rfind(")");
     146           2 :         if (posEnd != std::string::npos && posEnd > posStart)
     147             :         {
     148           2 :             const size_t posLast = typeDef.find_last_not_of(" \n\r\t");
     149           2 :             if (posLast == posEnd)
     150             :             {
     151           2 :                 typeName = typeDef.substr(0, posStart);
     152             :                 const std::string values =
     153           2 :                     typeDef.substr(posStart + 1, posEnd - posStart - 1);
     154           2 :                 typeSize = ParseIntValues(values);
     155             :             }
     156             :         }
     157             : 
     158           2 :         if (typeSize.empty() || typeSize.size() > 2)
     159             :         {
     160           0 :             incorrectFormatErr();
     161           0 :             return {"", QGRHanaDataTypes::Unknown, 0, 0};
     162             :         }
     163             :     }
     164             : 
     165           3 :     typeName.Trim();
     166             : 
     167           3 :     if (EQUAL(typeName.c_str(), "BOOLEAN"))
     168           0 :         return {std::move(typeName), QGRHanaDataTypes::Boolean, 0, 0};
     169           3 :     else if (EQUAL(typeName.c_str(), "TINYINT"))
     170           0 :         return {std::move(typeName), QGRHanaDataTypes::TinyInt, 0, 0};
     171           3 :     else if (EQUAL(typeName.c_str(), "SMALLINT"))
     172           1 :         return {std::move(typeName), QGRHanaDataTypes::SmallInt, 0, 0};
     173           2 :     else if (EQUAL(typeName.c_str(), "INTEGER"))
     174           0 :         return {std::move(typeName), QGRHanaDataTypes::Integer, 0, 0};
     175           2 :     else if (EQUAL(typeName.c_str(), "DECIMAL"))
     176             :     {
     177           2 :         switch (typeSize.size())
     178             :         {
     179           0 :             case 0:
     180           0 :                 return {std::move(typeName), QGRHanaDataTypes::Decimal, 0, 0};
     181           0 :             case 1:
     182           0 :                 return {std::move(typeName), QGRHanaDataTypes::Decimal,
     183           0 :                         typeSize[0], 0};
     184           2 :             case 2:
     185           2 :                 return {std::move(typeName), QGRHanaDataTypes::Decimal,
     186           2 :                         typeSize[0], typeSize[1]};
     187             :         }
     188             :     }
     189           0 :     else if (EQUAL(typeName.c_str(), "FLOAT"))
     190             :     {
     191           0 :         switch (typeSize.size())
     192             :         {
     193           0 :             case 0:
     194           0 :                 return {std::move(typeName), QGRHanaDataTypes::Float, 10, 0};
     195           0 :             case 1:
     196           0 :                 return {std::move(typeName), QGRHanaDataTypes::Float,
     197           0 :                         typeSize[0], 0};
     198           0 :             default:
     199           0 :                 incorrectFormatErr();
     200           0 :                 return {"", QGRHanaDataTypes::Unknown, 0, 0};
     201             :         }
     202             :     }
     203           0 :     else if (EQUAL(typeName.c_str(), "REAL"))
     204           0 :         return {std::move(typeName), QGRHanaDataTypes::Real, 0, 0};
     205           0 :     else if (EQUAL(typeName.c_str(), "DOUBLE"))
     206           0 :         return {std::move(typeName), QGRHanaDataTypes::Double, 0, 0};
     207           0 :     else if (EQUAL(typeName.c_str(), "VARCHAR"))
     208             :     {
     209           0 :         switch (typeSize.size())
     210             :         {
     211           0 :             case 0:
     212           0 :                 return {std::move(typeName), QGRHanaDataTypes::VarChar, 1, 0};
     213           0 :             case 1:
     214           0 :                 return {std::move(typeName), QGRHanaDataTypes::VarChar,
     215           0 :                         typeSize[0], 0};
     216           0 :             default:
     217           0 :                 incorrectFormatErr();
     218           0 :                 return {"", QGRHanaDataTypes::Unknown, 0, 0};
     219             :         }
     220             :     }
     221           0 :     else if (EQUAL(typeName.c_str(), "NVARCHAR"))
     222             :     {
     223           0 :         switch (typeSize.size())
     224             :         {
     225           0 :             case 0:
     226           0 :                 return {std::move(typeName), QGRHanaDataTypes::WVarChar, 1, 0};
     227           0 :             case 1:
     228           0 :                 return {std::move(typeName), QGRHanaDataTypes::WVarChar,
     229           0 :                         typeSize[0], 0};
     230           0 :             case 2:
     231           0 :                 incorrectFormatErr();
     232           0 :                 return {"", QGRHanaDataTypes::Unknown, 0, 0};
     233             :         }
     234             :     }
     235           0 :     else if (EQUAL(typeName.c_str(), "NCLOB"))
     236           0 :         return {std::move(typeName), QGRHanaDataTypes::WLongVarChar, 0, 0};
     237           0 :     else if (EQUAL(typeName.c_str(), "DATE"))
     238           0 :         return {std::move(typeName), QGRHanaDataTypes::Date, 0, 0};
     239           0 :     else if (EQUAL(typeName.c_str(), "TIME"))
     240           0 :         return {std::move(typeName), QGRHanaDataTypes::Time, 0, 0};
     241           0 :     else if (EQUAL(typeName.c_str(), "TIMESTAMP"))
     242           0 :         return {std::move(typeName), QGRHanaDataTypes::Timestamp, 0, 0};
     243           0 :     else if (EQUAL(typeName.c_str(), "VARBINARY"))
     244             :     {
     245           0 :         switch (typeSize.size())
     246             :         {
     247           0 :             case 0:
     248           0 :                 return {std::move(typeName), QGRHanaDataTypes::VarBinary, 1, 0};
     249           0 :             case 1:
     250           0 :                 return {std::move(typeName), QGRHanaDataTypes::VarBinary,
     251           0 :                         typeSize[0], 0};
     252           0 :             case 2:
     253           0 :                 incorrectFormatErr();
     254           0 :                 return {"", QGRHanaDataTypes::Unknown, 0, 0};
     255             :         }
     256             :     }
     257           0 :     else if (EQUAL(typeName.c_str(), "BLOB"))
     258           0 :         return {std::move(typeName), QGRHanaDataTypes::LongVarBinary, 0, 0};
     259           0 :     else if (EQUAL(typeName.c_str(), "REAL_VECTOR"))
     260             :     {
     261           0 :         switch (typeSize.size())
     262             :         {
     263           0 :             case 0:
     264           0 :                 return {std::move(typeName), QGRHanaDataTypes::RealVector, 1,
     265           0 :                         0};
     266           0 :             case 1:
     267           0 :                 return {std::move(typeName), QGRHanaDataTypes::RealVector,
     268           0 :                         typeSize[0], 0};
     269           0 :             case 2:
     270           0 :                 incorrectFormatErr();
     271           0 :                 return {"", QGRHanaDataTypes::Unknown, 0, 0};
     272             :         }
     273             :     }
     274             : 
     275           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Unknown column type '%s'.",
     276             :              typeName.c_str());
     277           0 :     return {std::move(typeName), QGRHanaDataTypes::Unknown, 0, 0};
     278             : }
     279             : 
     280          65 : CPLString GetColumnDefinition(const ColumnTypeInfo &typeInfo)
     281             : {
     282          65 :     bool isArray = std::strstr(typeInfo.name, "ARRAY") != nullptr;
     283             : 
     284          65 :     if (isArray)
     285             :     {
     286           4 :         switch (typeInfo.type)
     287             :         {
     288           0 :             case QGRHanaDataTypes::SmallInt:
     289           0 :                 return "SMALLINT ARRAY";
     290           1 :             case QGRHanaDataTypes::Integer:
     291           1 :                 return "INTEGER ARRAY";
     292           1 :             case QGRHanaDataTypes::BigInt:
     293           1 :                 return "BIGINT ARRAY";
     294           0 :             case QGRHanaDataTypes::Real:
     295           0 :                 return "REAL ARRAY";
     296           1 :             case QGRHanaDataTypes::Double:
     297           1 :                 return "DOUBLE ARRAY";
     298           1 :             case QGRHanaDataTypes::WVarChar:
     299           1 :                 return "NVARCHAR(512) ARRAY";
     300           0 :             default:
     301           0 :                 return "UNKNOWN";
     302             :         }
     303             :     }
     304             : 
     305          61 :     switch (typeInfo.type)
     306             :     {
     307          34 :         case QGRHanaDataTypes::Boolean:
     308             :         case QGRHanaDataTypes::TinyInt:
     309             :         case QGRHanaDataTypes::SmallInt:
     310             :         case QGRHanaDataTypes::Integer:
     311             :         case QGRHanaDataTypes::BigInt:
     312             :         case QGRHanaDataTypes::Float:
     313             :         case QGRHanaDataTypes::Real:
     314             :         case QGRHanaDataTypes::Double:
     315             :         case QGRHanaDataTypes::Date:
     316             :         case QGRHanaDataTypes::TypeDate:
     317             :         case QGRHanaDataTypes::Time:
     318             :         case QGRHanaDataTypes::TypeTime:
     319             :         case QGRHanaDataTypes::Timestamp:
     320             :         case QGRHanaDataTypes::TypeTimestamp:
     321             :         case QGRHanaDataTypes::Char:
     322             :         case QGRHanaDataTypes::WChar:
     323             :         case QGRHanaDataTypes::LongVarChar:
     324             :         case QGRHanaDataTypes::LongVarBinary:
     325          34 :             return typeInfo.name;
     326           3 :         case QGRHanaDataTypes::Decimal:
     327             :         case QGRHanaDataTypes::Numeric:
     328           6 :             return CPLString().Printf("DECIMAL(%d,%d)", typeInfo.width,
     329           3 :                                       typeInfo.precision);
     330          24 :         case QGRHanaDataTypes::VarChar:
     331             :         case QGRHanaDataTypes::WVarChar:
     332             :         case QGRHanaDataTypes::Binary:
     333             :         case QGRHanaDataTypes::VarBinary:
     334             :         case QGRHanaDataTypes::WLongVarChar:
     335             :         case QGRHanaDataTypes::RealVector:
     336          24 :             return (typeInfo.width == 0)
     337             :                        ? typeInfo.name
     338          48 :                        : CPLString().Printf("%s(%d)", typeInfo.name.c_str(),
     339          48 :                                             typeInfo.width);
     340           0 :         default:
     341           0 :             return "UNKNOWN";
     342             :     }
     343             : }
     344             : 
     345          64 : void SetFieldDefn(OGRFieldDefn &field, const ColumnTypeInfo &typeInfo)
     346             : {
     347          58 :     auto isArray = [&typeInfo]()
     348          58 :     { return std::strstr(typeInfo.name, "ARRAY") != nullptr; };
     349             : 
     350          64 :     switch (typeInfo.type)
     351             :     {
     352           1 :         case QGRHanaDataTypes::Bit:
     353             :         case QGRHanaDataTypes::Boolean:
     354           1 :             field.SetType(OFTInteger);
     355           1 :             field.SetSubType(OFSTBoolean);
     356           1 :             break;
     357           2 :         case QGRHanaDataTypes::TinyInt:
     358             :         case QGRHanaDataTypes::SmallInt:
     359           2 :             field.SetType(isArray() ? OFTIntegerList : OFTInteger);
     360           2 :             field.SetSubType(OFSTInt16);
     361           2 :             break;
     362           4 :         case QGRHanaDataTypes::Integer:
     363           4 :             field.SetType(isArray() ? OFTIntegerList : OFTInteger);
     364           4 :             break;
     365          12 :         case QGRHanaDataTypes::BigInt:
     366          12 :             field.SetType(isArray() ? OFTInteger64List : OFTInteger64);
     367          12 :             break;
     368          13 :         case QGRHanaDataTypes::Double:
     369             :         case QGRHanaDataTypes::Real:
     370             :         case QGRHanaDataTypes::Float:
     371          13 :             field.SetType(isArray() ? OFTRealList : OFTReal);
     372          13 :             if (typeInfo.type != QGRHanaDataTypes::Double)
     373           1 :                 field.SetSubType(OFSTFloat32);
     374          13 :             break;
     375           3 :         case QGRHanaDataTypes::Decimal:
     376             :         case QGRHanaDataTypes::Numeric:
     377           3 :             field.SetType(isArray() ? OFTRealList : OFTReal);
     378           3 :             break;
     379           0 :         case QGRHanaDataTypes::Char:
     380             :         case QGRHanaDataTypes::VarChar:
     381             :         case QGRHanaDataTypes::LongVarChar:
     382           0 :             field.SetType(isArray() ? OFTStringList : OFTString);
     383           0 :             break;
     384          24 :         case QGRHanaDataTypes::WChar:
     385             :         case QGRHanaDataTypes::WVarChar:
     386             :         case QGRHanaDataTypes::WLongVarChar:
     387          24 :             field.SetType(isArray() ? OFTStringList : OFTString);
     388          24 :             break;
     389           1 :         case QGRHanaDataTypes::Date:
     390             :         case QGRHanaDataTypes::TypeDate:
     391           1 :             field.SetType(OFTDate);
     392           1 :             break;
     393           1 :         case QGRHanaDataTypes::Time:
     394             :         case QGRHanaDataTypes::TypeTime:
     395           1 :             field.SetType(OFTTime);
     396           1 :             break;
     397           2 :         case QGRHanaDataTypes::Timestamp:
     398             :         case QGRHanaDataTypes::TypeTimestamp:
     399           2 :             field.SetType(OFTDateTime);
     400           2 :             break;
     401           1 :         case QGRHanaDataTypes::Binary:
     402             :         case QGRHanaDataTypes::VarBinary:
     403             :         case QGRHanaDataTypes::LongVarBinary:
     404             :         case QGRHanaDataTypes::RealVector:
     405           1 :             field.SetType(OFTBinary);
     406           1 :             break;
     407           0 :         default:
     408           0 :             break;
     409             :     }
     410             : 
     411          64 :     field.SetWidth(typeInfo.width);
     412          64 :     field.SetPrecision(typeInfo.precision);
     413          64 : }
     414             : 
     415             : }  // anonymous namespace
     416             : 
     417             : /************************************************************************/
     418             : /*                         OGRHanaTableLayer()                          */
     419             : /************************************************************************/
     420             : 
     421         485 : OGRHanaTableLayer::OGRHanaTableLayer(OGRHanaDataSource *datasource,
     422             :                                      const char *schemaName,
     423         485 :                                      const char *tableName, int update)
     424             :     : OGRHanaLayer(datasource), schemaName_(schemaName), tableName_(tableName),
     425         485 :       updateMode_(update)
     426             : {
     427             :     rawQuery_ =
     428         485 :         "SELECT * FROM " + GetFullTableNameQuoted(schemaName_, tableName_);
     429         485 :     SetDescription(tableName_.c_str());
     430         485 : }
     431             : 
     432             : /************************************************************************/
     433             : /*                        ~OGRHanaTableLayer()                          */
     434             : /************************************************************************/
     435             : 
     436         970 : OGRHanaTableLayer::~OGRHanaTableLayer()
     437             : {
     438         485 :     FlushPendingBatches(true);
     439         970 : }
     440             : 
     441             : /* -------------------------------------------------------------------- */
     442             : /*                             Initialize()                             */
     443             : /* -------------------------------------------------------------------- */
     444             : 
     445          42 : OGRErr OGRHanaTableLayer::Initialize()
     446             : {
     447          42 :     if (initialized_)
     448           0 :         return OGRERR_NONE;
     449             : 
     450             :     OGRErr err =
     451          42 :         InitFeatureDefinition(schemaName_, tableName_, rawQuery_, tableName_);
     452          42 :     if (err != OGRERR_NONE)
     453           0 :         return err;
     454             : 
     455          42 :     if (fidFieldIndex_ != OGRNullFID)
     456             :     {
     457          38 :         CPLDebug("HANA", "table %s has FID column %s.", tableName_.c_str(),
     458             :                  fidFieldName_.c_str());
     459             : 
     460          38 :         CPLString sql = CPLString().Printf(
     461             :             "SELECT COUNT(*) FROM %s",
     462         114 :             GetFullTableNameQuoted(schemaName_, tableName_).c_str());
     463          76 :         odbc::StatementRef stmt = dataSource_->CreateStatement();
     464          76 :         odbc::ResultSetRef rs = stmt->executeQuery(sql.c_str());
     465          38 :         allowAutoFIDOnCreateFeature_ =
     466          38 :             rs->next() ? (*rs->getLong(1) == 0) : false;
     467          38 :         rs->close();
     468             :     }
     469             :     else
     470             :     {
     471           4 :         CPLDebug("HANA",
     472             :                  "table %s has no FID column, FIDs will not be reliable!",
     473             :                  tableName_.c_str());
     474             : 
     475           4 :         allowAutoFIDOnCreateFeature_ = true;
     476             :     }
     477             : 
     478          42 :     return OGRERR_NONE;
     479             : }
     480             : 
     481             : /* -------------------------------------------------------------------- */
     482             : /*                                 ExecuteUpdate()                      */
     483             : /* -------------------------------------------------------------------- */
     484             : 
     485             : std::pair<OGRErr, std::size_t>
     486         144 : OGRHanaTableLayer::ExecuteUpdate(odbc::PreparedStatement &statement,
     487             :                                  bool withBatch, const char *functionName)
     488             : {
     489         144 :     std::size_t ret = 0;
     490             : 
     491             :     try
     492             :     {
     493         144 :         if (withBatch)
     494             :         {
     495           8 :             if (statement.getBatchDataSize() >= batchSize_)
     496           0 :                 statement.executeBatch();
     497           8 :             ret = 1;
     498             :         }
     499             :         else
     500             :         {
     501         136 :             ret = statement.executeUpdate();
     502             :         }
     503             : 
     504         144 :         if (!dataSource_->IsTransactionStarted())
     505         134 :             dataSource_->Commit();
     506             :     }
     507           0 :     catch (odbc::Exception &ex)
     508             :     {
     509           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to execute %s: %s",
     510           0 :                  functionName, ex.what());
     511           0 :         return {OGRERR_FAILURE, 0};
     512             :     }
     513             : 
     514         144 :     return {OGRERR_NONE, ret};
     515             : }
     516             : 
     517             : /* -------------------------------------------------------------------- */
     518             : /*                   CreateDeleteFeatureStatement()                     */
     519             : /* -------------------------------------------------------------------- */
     520             : 
     521           3 : odbc::PreparedStatementRef OGRHanaTableLayer::CreateDeleteFeatureStatement()
     522             : {
     523           3 :     CPLString sql = CPLString().Printf(
     524             :         "DELETE FROM %s WHERE %s = ?",
     525           6 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
     526          12 :         QuotedIdentifier(GetFIDColumn()).c_str());
     527           6 :     return dataSource_->PrepareStatement(sql.c_str());
     528             : }
     529             : 
     530             : /* -------------------------------------------------------------------- */
     531             : /*                   CreateInsertFeatureStatement()                     */
     532             : /* -------------------------------------------------------------------- */
     533             : 
     534             : odbc::PreparedStatementRef
     535          16 : OGRHanaTableLayer::CreateInsertFeatureStatement(bool withFID)
     536             : {
     537          32 :     std::vector<CPLString> columns;
     538          32 :     std::vector<CPLString> values;
     539          16 :     bool hasArray = false;
     540         103 :     for (const AttributeColumnDescription &clmDesc : attrColumns_)
     541             :     {
     542          87 :         if (clmDesc.isFeatureID && !withFID)
     543             :         {
     544           2 :             if (clmDesc.isAutoIncrement)
     545           2 :                 continue;
     546             :         }
     547             : 
     548          85 :         columns.push_back(QuotedIdentifier(clmDesc.name));
     549          85 :         values.push_back(
     550         170 :             GetParameterValue(clmDesc.type, clmDesc.typeName, clmDesc.isArray));
     551          85 :         if (clmDesc.isArray)
     552           4 :             hasArray = true;
     553             :     }
     554             : 
     555          31 :     for (const GeometryColumnDescription &geomClmDesc : geomColumns_)
     556             :     {
     557          15 :         columns.push_back(QuotedIdentifier(geomClmDesc.name));
     558          30 :         values.push_back("ST_GeomFromWKB(? , " +
     559          45 :                          std::to_string(geomClmDesc.srid) + ")");
     560             :     }
     561             : 
     562          16 :     if (hasArray && !parseFunctionsChecked_)
     563             :     {
     564             :         // Create helper functions if needed.
     565           1 :         if (!dataSource_->ParseArrayFunctionsExist(schemaName_.c_str()))
     566           1 :             dataSource_->CreateParseArrayFunctions(schemaName_.c_str());
     567           1 :         parseFunctionsChecked_ = true;
     568             :     }
     569             : 
     570          16 :     const CPLString sql = CPLString().Printf(
     571             :         "INSERT INTO %s (%s) VALUES(%s)",
     572          32 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
     573          64 :         JoinStrings(columns, ", ").c_str(), JoinStrings(values, ", ").c_str());
     574             : 
     575          32 :     return dataSource_->PrepareStatement(sql.c_str());
     576             : }
     577             : 
     578             : /* -------------------------------------------------------------------- */
     579             : /*                     CreateUpdateFeatureStatement()                   */
     580             : /* -------------------------------------------------------------------- */
     581             : 
     582           4 : odbc::PreparedStatementRef OGRHanaTableLayer::CreateUpdateFeatureStatement()
     583             : {
     584           8 :     std::vector<CPLString> values;
     585           4 :     values.reserve(attrColumns_.size());
     586           4 :     bool hasArray = false;
     587             : 
     588          18 :     for (const AttributeColumnDescription &clmDesc : attrColumns_)
     589             :     {
     590          14 :         if (clmDesc.isFeatureID)
     591             :         {
     592           4 :             if (clmDesc.isAutoIncrement)
     593           3 :                 continue;
     594             :         }
     595          11 :         values.push_back(
     596          22 :             QuotedIdentifier(clmDesc.name) + " = " +
     597          22 :             GetParameterValue(clmDesc.type, clmDesc.typeName, clmDesc.isArray));
     598          11 :         if (clmDesc.isArray)
     599           0 :             hasArray = true;
     600             :     }
     601             : 
     602           7 :     for (const GeometryColumnDescription &geomClmDesc : geomColumns_)
     603             :     {
     604           9 :         values.push_back(QuotedIdentifier(geomClmDesc.name) + " = " +
     605           6 :                          "ST_GeomFromWKB(?, " +
     606          12 :                          std::to_string(geomClmDesc.srid) + ")");
     607             :     }
     608             : 
     609           4 :     if (hasArray && !parseFunctionsChecked_)
     610             :     {
     611             :         // Create helper functions if needed.
     612           0 :         if (!dataSource_->ParseArrayFunctionsExist(schemaName_.c_str()))
     613           0 :             dataSource_->CreateParseArrayFunctions(schemaName_.c_str());
     614           0 :         parseFunctionsChecked_ = true;
     615             :     }
     616             : 
     617           4 :     const CPLString sql = CPLString().Printf(
     618             :         "UPDATE %s SET %s WHERE %s = ?",
     619           8 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
     620           8 :         JoinStrings(values, ", ").c_str(),
     621          20 :         QuotedIdentifier(GetFIDColumn()).c_str());
     622             : 
     623           8 :     return dataSource_->PrepareStatement(sql.c_str());
     624             : }
     625             : 
     626             : /* -------------------------------------------------------------------- */
     627             : /*                     ResetPreparedStatements()                        */
     628             : /* -------------------------------------------------------------------- */
     629             : 
     630          65 : void OGRHanaTableLayer::ResetPreparedStatements()
     631             : {
     632          65 :     if (!currentIdentityValueStmt_.isNull())
     633           0 :         currentIdentityValueStmt_ = nullptr;
     634          65 :     if (!insertFeatureStmtWithFID_.isNull())
     635           1 :         insertFeatureStmtWithFID_ = nullptr;
     636          65 :     if (!insertFeatureStmtWithoutFID_.isNull())
     637           0 :         insertFeatureStmtWithoutFID_ = nullptr;
     638          65 :     if (!deleteFeatureStmt_.isNull())
     639           0 :         deleteFeatureStmt_ = nullptr;
     640          65 :     if (!updateFeatureStmt_.isNull())
     641           0 :         updateFeatureStmt_ = nullptr;
     642          65 : }
     643             : 
     644             : /************************************************************************/
     645             : /*                        SetStatementParameters()                      */
     646             : /************************************************************************/
     647             : 
     648         136 : OGRErr OGRHanaTableLayer::SetStatementParameters(
     649             :     odbc::PreparedStatement &statement, OGRFeature *feature, bool newFeature,
     650             :     bool withFID, const char *functionName)
     651             : {
     652         136 :     OGRHanaFeatureReader featReader(*feature);
     653             : 
     654         136 :     unsigned short paramIndex = 0;
     655         136 :     int fieldIndex = -1;
     656         799 :     for (const AttributeColumnDescription &clmDesc : attrColumns_)
     657             :     {
     658         663 :         if (clmDesc.isFeatureID)
     659             :         {
     660         136 :             if (!withFID && clmDesc.isAutoIncrement)
     661          20 :                 continue;
     662             : 
     663         116 :             ++paramIndex;
     664             : 
     665         116 :             switch (clmDesc.type)
     666             :             {
     667         116 :                 case QGRHanaDataTypes::Integer:
     668         116 :                     if (feature->GetFID() == OGRNullFID)
     669           0 :                         statement.setInt(paramIndex, odbc::Int());
     670             :                     else
     671             :                     {
     672         116 :                         if (std::numeric_limits<std::int32_t>::min() >
     673         232 :                                 feature->GetFID() ||
     674         116 :                             std::numeric_limits<std::int32_t>::max() <
     675         116 :                                 feature->GetFID())
     676             :                         {
     677           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     678             :                                      "%s: Feature id with value %s cannot "
     679             :                                      "be stored in a column of type INTEGER",
     680             :                                      functionName,
     681           0 :                                      std::to_string(feature->GetFID()).c_str());
     682           0 :                             return OGRERR_FAILURE;
     683             :                         }
     684             : 
     685         116 :                         statement.setInt(paramIndex,
     686         232 :                                          odbc::Int(static_cast<std::int32_t>(
     687         116 :                                              feature->GetFID())));
     688             :                     }
     689         116 :                     break;
     690           0 :                 case QGRHanaDataTypes::BigInt:
     691           0 :                     if (feature->GetFID() == OGRNullFID)
     692           0 :                         statement.setLong(paramIndex, odbc::Long());
     693             :                     else
     694           0 :                         statement.setLong(paramIndex,
     695           0 :                                           odbc::Long(static_cast<std::int64_t>(
     696           0 :                                               feature->GetFID())));
     697           0 :                     break;
     698           0 :                 default:
     699           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     700             :                              "%s: Unexpected type ('%s') in the field "
     701             :                              "'%s'",
     702           0 :                              functionName, std::to_string(clmDesc.type).c_str(),
     703             :                              clmDesc.name.c_str());
     704           0 :                     return OGRERR_FAILURE;
     705             :             }
     706         116 :             continue;
     707             :         }
     708             :         else
     709         527 :             ++paramIndex;
     710             : 
     711         527 :         ++fieldIndex;
     712             : 
     713         527 :         switch (clmDesc.type)
     714             :         {
     715           1 :             case QGRHanaDataTypes::Bit:
     716             :             case QGRHanaDataTypes::Boolean:
     717           1 :                 statement.setBoolean(paramIndex,
     718           1 :                                      featReader.GetFieldAsBoolean(fieldIndex));
     719           1 :                 break;
     720           0 :             case QGRHanaDataTypes::TinyInt:
     721           0 :                 if (clmDesc.isArray)
     722           0 :                     statement.setString(
     723           0 :                         paramIndex, featReader.GetFieldAsIntArray(fieldIndex));
     724             :                 else
     725           0 :                     statement.setByte(paramIndex,
     726           0 :                                       featReader.GetFieldAsByte(fieldIndex));
     727           0 :                 break;
     728           1 :             case QGRHanaDataTypes::SmallInt:
     729           1 :                 if (clmDesc.isArray)
     730           0 :                     statement.setString(
     731           0 :                         paramIndex, featReader.GetFieldAsIntArray(fieldIndex));
     732             :                 else
     733           1 :                     statement.setShort(paramIndex,
     734           2 :                                        featReader.GetFieldAsShort(fieldIndex));
     735           1 :                 break;
     736           3 :             case QGRHanaDataTypes::Integer:
     737           3 :                 if (clmDesc.isArray)
     738           1 :                     statement.setString(
     739           2 :                         paramIndex, featReader.GetFieldAsIntArray(fieldIndex));
     740             :                 else
     741           2 :                     statement.setInt(paramIndex,
     742           4 :                                      featReader.GetFieldAsInt(fieldIndex));
     743           3 :                 break;
     744         128 :             case QGRHanaDataTypes::BigInt:
     745         128 :                 if (clmDesc.isArray)
     746           1 :                     statement.setString(
     747             :                         paramIndex,
     748           2 :                         featReader.GetFieldAsBigIntArray(fieldIndex));
     749             :                 else
     750         127 :                     statement.setLong(paramIndex,
     751         254 :                                       featReader.GetFieldAsLong(fieldIndex));
     752         128 :                 break;
     753           1 :             case QGRHanaDataTypes::Float:
     754             :             case QGRHanaDataTypes::Real:
     755           1 :                 if (clmDesc.isArray)
     756           0 :                     statement.setString(
     757           0 :                         paramIndex, featReader.GetFieldAsRealArray(fieldIndex));
     758             :                 else
     759           1 :                     statement.setFloat(paramIndex,
     760           2 :                                        featReader.GetFieldAsFloat(fieldIndex));
     761           1 :                 break;
     762         128 :             case QGRHanaDataTypes::Double:
     763         128 :                 if (clmDesc.isArray)
     764           1 :                     statement.setString(
     765             :                         paramIndex,
     766           2 :                         featReader.GetFieldAsDoubleArray(fieldIndex));
     767             :                 else
     768         127 :                     statement.setDouble(
     769         254 :                         paramIndex, featReader.GetFieldAsDouble(fieldIndex));
     770         128 :                 break;
     771           1 :             case QGRHanaDataTypes::Decimal:
     772             :             case QGRHanaDataTypes::Numeric:
     773           1 :                 if ((!feature->IsFieldSet(fieldIndex) ||
     774           2 :                      feature->IsFieldNull(fieldIndex)) &&
     775           1 :                     feature->GetFieldDefnRef(fieldIndex)->GetDefault() ==
     776             :                         nullptr)
     777           0 :                     statement.setDecimal(paramIndex, odbc::Decimal());
     778             :                 else
     779           1 :                     statement.setDouble(
     780           2 :                         paramIndex, featReader.GetFieldAsDouble(fieldIndex));
     781           1 :                 break;
     782           0 :             case QGRHanaDataTypes::Char:
     783             :             case QGRHanaDataTypes::VarChar:
     784             :             case QGRHanaDataTypes::LongVarChar:
     785           0 :                 if (clmDesc.isArray)
     786           0 :                     statement.setString(
     787             :                         paramIndex,
     788           0 :                         featReader.GetFieldAsStringArray(fieldIndex));
     789             :                 else
     790           0 :                     statement.setString(paramIndex,
     791           0 :                                         featReader.GetFieldAsString(
     792           0 :                                             fieldIndex, clmDesc.length));
     793           0 :                 break;
     794         255 :             case QGRHanaDataTypes::WChar:
     795             :             case QGRHanaDataTypes::WVarChar:
     796             :             case QGRHanaDataTypes::WLongVarChar:
     797         255 :                 if (clmDesc.isArray)
     798           1 :                     statement.setString(
     799             :                         paramIndex,
     800           2 :                         featReader.GetFieldAsStringArray(fieldIndex));
     801             :                 else
     802         254 :                     statement.setString(paramIndex,
     803         254 :                                         featReader.GetFieldAsNString(
     804         254 :                                             fieldIndex, clmDesc.length));
     805         255 :                 break;
     806           3 :             case QGRHanaDataTypes::Binary:
     807             :             case QGRHanaDataTypes::VarBinary:
     808             :             case QGRHanaDataTypes::LongVarBinary:
     809             :             case QGRHanaDataTypes::RealVector:
     810             :             {
     811           3 :                 Binary bin = featReader.GetFieldAsBinary(fieldIndex);
     812           3 :                 statement.setBytes(paramIndex, bin.data, bin.size);
     813             :             }
     814           3 :             break;
     815           1 :             case QGRHanaDataTypes::DateTime:
     816             :             case QGRHanaDataTypes::TypeDate:
     817           1 :                 statement.setDate(paramIndex,
     818           1 :                                   featReader.GetFieldAsDate(fieldIndex));
     819           1 :                 break;
     820           1 :             case QGRHanaDataTypes::Time:
     821             :             case QGRHanaDataTypes::TypeTime:
     822           1 :                 statement.setTime(paramIndex,
     823           1 :                                   featReader.GetFieldAsTime(fieldIndex));
     824           1 :                 break;
     825           4 :             case QGRHanaDataTypes::Timestamp:
     826             :             case QGRHanaDataTypes::TypeTimestamp:
     827           4 :                 statement.setTimestamp(
     828           4 :                     paramIndex, featReader.GetFieldAsTimestamp(fieldIndex));
     829           4 :                 break;
     830             :         }
     831             :     }
     832             : 
     833         268 :     for (std::size_t i = 0; i < geomColumns_.size(); ++i)
     834             :     {
     835         132 :         ++paramIndex;
     836         132 :         Binary wkb{nullptr, 0};
     837         132 :         OGRErr err = GetGeometryWkb(feature, static_cast<int>(i), wkb);
     838         132 :         if (OGRERR_NONE != err)
     839           0 :             return err;
     840         132 :         statement.setBytes(paramIndex, wkb.data, wkb.size);
     841             :     }
     842             : 
     843         136 :     if (!newFeature)
     844             :     {
     845          13 :         ++paramIndex;
     846             : 
     847          13 :         statement.setLong(paramIndex, odbc::Long(static_cast<std::int64_t>(
     848          13 :                                           feature->GetFID())));
     849             :     }
     850             : 
     851         136 :     return OGRERR_NONE;
     852             : }
     853             : 
     854             : /* -------------------------------------------------------------------- */
     855             : /*                            DropTable()                               */
     856             : /* -------------------------------------------------------------------- */
     857             : 
     858           0 : OGRErr OGRHanaTableLayer::DropTable()
     859             : {
     860             :     CPLString sql =
     861           0 :         "DROP TABLE " + GetFullTableNameQuoted(schemaName_, tableName_);
     862             :     try
     863             :     {
     864           0 :         dataSource_->ExecuteSQL(sql.c_str());
     865           0 :         CPLDebug("HANA", "Dropped table %s.", GetName());
     866             :     }
     867           0 :     catch (const odbc::Exception &ex)
     868             :     {
     869           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unable to delete layer '%s': %s",
     870           0 :                  tableName_.c_str(), ex.what());
     871           0 :         return OGRERR_FAILURE;
     872             :     }
     873             : 
     874           0 :     return OGRERR_NONE;
     875             : }
     876             : 
     877             : /* -------------------------------------------------------------------- */
     878             : /*                        ExecutePendingBatches()                       */
     879             : /* -------------------------------------------------------------------- */
     880             : 
     881         148 : OGRErr OGRHanaTableLayer::ExecutePendingBatches(BatchOperation op)
     882             : {
     883           8 :     auto hasFlag = [op](BatchOperation flag) { return (op & flag) == flag; };
     884             : 
     885             :     try
     886             :     {
     887         148 :         if (!deleteFeatureStmt_.isNull() &&
     888         150 :             deleteFeatureStmt_->getBatchDataSize() > 0 &&
     889           2 :             hasFlag(BatchOperation::DELETE))
     890           2 :             deleteFeatureStmt_->executeBatch();
     891         148 :         if (!insertFeatureStmtWithFID_.isNull() &&
     892         153 :             insertFeatureStmtWithFID_->getBatchDataSize() > 0 &&
     893           5 :             hasFlag(BatchOperation::INSERT))
     894           3 :             insertFeatureStmtWithFID_->executeBatch();
     895         148 :         if (!insertFeatureStmtWithoutFID_.isNull() &&
     896         148 :             insertFeatureStmtWithoutFID_->getBatchDataSize() > 0 &&
     897           0 :             hasFlag(BatchOperation::INSERT))
     898           0 :             insertFeatureStmtWithoutFID_->executeBatch();
     899         148 :         if (!updateFeatureStmt_.isNull() &&
     900         149 :             updateFeatureStmt_->getBatchDataSize() > 0 &&
     901           1 :             hasFlag(BatchOperation::UPDATE))
     902           1 :             updateFeatureStmt_->executeBatch();
     903             : 
     904         148 :         return OGRERR_NONE;
     905             :     }
     906           0 :     catch (const odbc::Exception &ex)
     907             :     {
     908           0 :         ClearBatches();
     909             : 
     910           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     911           0 :                  "Failed to execute batch commands: %s", ex.what());
     912           0 :         return OGRERR_FAILURE;
     913             :     }
     914             : }
     915             : 
     916             : /* -------------------------------------------------------------------- */
     917             : /*                        FlushPendingBatches()                         */
     918             : /* -------------------------------------------------------------------- */
     919             : 
     920         943 : void OGRHanaTableLayer::FlushPendingBatches(bool commit)
     921             : {
     922         943 :     if (!HasPendingBatches())
     923         939 :         return;
     924             : 
     925           4 :     OGRErr err = ExecutePendingBatches(BatchOperation::ALL);
     926           4 :     if (OGRERR_NONE == err && commit && !dataSource_->IsTransactionStarted())
     927           0 :         dataSource_->Commit();
     928             : }
     929             : 
     930             : /* -------------------------------------------------------------------- */
     931             : /*                        HasPendingBatches()                           */
     932             : /* -------------------------------------------------------------------- */
     933             : 
     934         950 : bool OGRHanaTableLayer::HasPendingBatches() const
     935             : {
     936         950 :     return (!deleteFeatureStmt_.isNull() &&
     937          73 :             deleteFeatureStmt_->getBatchDataSize() > 0) ||
     938         949 :            (!insertFeatureStmtWithFID_.isNull() &&
     939         123 :             insertFeatureStmtWithFID_->getBatchDataSize() > 0) ||
     940         947 :            (!insertFeatureStmtWithoutFID_.isNull() &&
     941        1927 :             insertFeatureStmtWithoutFID_->getBatchDataSize() > 0) ||
     942         947 :            (!updateFeatureStmt_.isNull() &&
     943        1021 :             updateFeatureStmt_->getBatchDataSize() > 0);
     944             : }
     945             : 
     946             : /* -------------------------------------------------------------------- */
     947             : /*                            GetColumnTypeInfo()                       */
     948             : /* -------------------------------------------------------------------- */
     949             : 
     950             : ColumnTypeInfo
     951          65 : OGRHanaTableLayer::GetColumnTypeInfo(const OGRFieldDefn &field) const
     952             : {
     953          68 :     for (const auto &clmType : customColumnDefs_)
     954             :     {
     955           6 :         if (EQUAL(clmType.name.c_str(), field.GetNameRef()))
     956           3 :             return ParseColumnTypeInfo(clmType.typeDef);
     957             :     }
     958             : 
     959          62 :     switch (field.GetType())
     960             :     {
     961           6 :         case OFTInteger:
     962           6 :             if (preservePrecision_ && field.GetWidth() > 10)
     963             :             {
     964           0 :                 return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
     965           0 :                         0};
     966             :             }
     967             :             else
     968             :             {
     969           6 :                 if (field.GetSubType() == OFSTBoolean)
     970             :                     return {"BOOLEAN", QGRHanaDataTypes::Boolean,
     971           1 :                             field.GetWidth(), 0};
     972           5 :                 else if (field.GetSubType() == OFSTInt16)
     973             :                     return {"SMALLINT", QGRHanaDataTypes::SmallInt,
     974           1 :                             field.GetWidth(), 0};
     975             :                 else
     976             :                     return {"INTEGER", QGRHanaDataTypes::Integer,
     977           4 :                             field.GetWidth(), 0};
     978             :             }
     979             :             break;
     980          11 :         case OFTInteger64:
     981          11 :             if (preservePrecision_ && field.GetWidth() > 20)
     982             :             {
     983           0 :                 return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
     984           0 :                         0};
     985             :             }
     986             :             else
     987          11 :                 return {"BIGINT", QGRHanaDataTypes::BigInt, field.GetWidth(),
     988          11 :                         0};
     989             :             break;
     990          13 :         case OFTReal:
     991          13 :             if (preservePrecision_ && field.GetWidth() != 0)
     992             :             {
     993           1 :                 return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
     994           1 :                         field.GetPrecision()};
     995             :             }
     996             :             else
     997             :             {
     998          12 :                 if (field.GetSubType() == OFSTFloat32)
     999           1 :                     return {"REAL", QGRHanaDataTypes::Real, field.GetWidth(),
    1000           1 :                             field.GetPrecision()};
    1001             :                 else
    1002             :                     return {"DOUBLE", QGRHanaDataTypes::Double,
    1003          11 :                             field.GetWidth(), field.GetPrecision()};
    1004             :             }
    1005          23 :         case OFTString:
    1006          23 :             if (field.GetWidth() == 0 || !preservePrecision_)
    1007             :             {
    1008          13 :                 int width = static_cast<int>(defaultStringSize_);
    1009          13 :                 return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar, width, 0};
    1010             :             }
    1011             :             else
    1012             :             {
    1013          10 :                 if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
    1014             :                     return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar,
    1015          10 :                             field.GetWidth(), 0};
    1016             :                 else
    1017           0 :                     return {"NCLOB", QGRHanaDataTypes::WLongVarChar, 0, 0};
    1018             :             }
    1019           1 :         case OFTBinary:
    1020           1 :             if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
    1021             :                 return {"VARBINARY", QGRHanaDataTypes::VarBinary,
    1022           1 :                         field.GetWidth(), 0};
    1023             :             else
    1024             :                 return {"BLOB", QGRHanaDataTypes::LongVarBinary,
    1025           0 :                         field.GetWidth(), 0};
    1026           1 :         case OFTDate:
    1027           1 :             return {"DATE", QGRHanaDataTypes::TypeDate, field.GetWidth(), 0};
    1028           1 :         case OFTTime:
    1029           1 :             return {"TIME", QGRHanaDataTypes::TypeTime, field.GetWidth(), 0};
    1030           2 :         case OFTDateTime:
    1031             :             return {"TIMESTAMP", QGRHanaDataTypes::TypeTimestamp,
    1032           2 :                     field.GetWidth(), 0};
    1033           1 :         case OFTIntegerList:
    1034           1 :             if (field.GetSubType() == OGRFieldSubType::OFSTInt16)
    1035           0 :                 return {"ARRAY", QGRHanaDataTypes::SmallInt, field.GetWidth(),
    1036           0 :                         0};
    1037             :             else
    1038           1 :                 return {"ARRAY", QGRHanaDataTypes::Integer, field.GetWidth(),
    1039           1 :                         0};
    1040           1 :         case OFTInteger64List:
    1041           1 :             return {"ARRAY", QGRHanaDataTypes::BigInt, field.GetWidth(), 0};
    1042           1 :         case OFTRealList:
    1043           1 :             if (field.GetSubType() == OGRFieldSubType::OFSTFloat32)
    1044           0 :                 return {"ARRAY", QGRHanaDataTypes::Real, field.GetWidth(), 0};
    1045             :             else
    1046           1 :                 return {"ARRAY", QGRHanaDataTypes::Double, field.GetWidth(), 0};
    1047             :             break;
    1048           1 :         case OFTStringList:
    1049           1 :             return {"ARRAY", QGRHanaDataTypes::WVarChar, 512, 0};
    1050           0 :         default:
    1051           0 :             break;
    1052             :     }
    1053             : 
    1054           0 :     return {"", QGRHanaDataTypes::Unknown, 0, 0};
    1055             : }
    1056             : 
    1057             : /* -------------------------------------------------------------------- */
    1058             : /*                           GetGeometryWkb()                           */
    1059             : /* -------------------------------------------------------------------- */
    1060         132 : OGRErr OGRHanaTableLayer::GetGeometryWkb(OGRFeature *feature, int fieldIndex,
    1061             :                                          Binary &binary)
    1062             : {
    1063         132 :     OGRGeometry *geom = feature->GetGeomFieldRef(fieldIndex);
    1064         132 :     if (geom == nullptr || !IsGeometryTypeSupported(geom->getIsoGeometryType()))
    1065          10 :         return OGRERR_NONE;
    1066             : 
    1067             :     // Rings must be closed, otherwise HANA throws an exception
    1068         122 :     geom->closeRings();
    1069         122 :     std::size_t size = static_cast<std::size_t>(geom->WkbSize());
    1070         122 :     EnsureBufferCapacity(size);
    1071         122 :     unsigned char *data = reinterpret_cast<unsigned char *>(dataBuffer_.data());
    1072         122 :     OGRErr err = geom->exportToWkb(OGRwkbByteOrder::wkbNDR, data,
    1073             :                                    OGRwkbVariant::wkbVariantIso);
    1074         122 :     if (OGRERR_NONE == err)
    1075             :     {
    1076         122 :         binary.data = data;
    1077         122 :         binary.size = size;
    1078             :     }
    1079         122 :     return err;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                                IGetExtent()                          */
    1084             : /************************************************************************/
    1085             : 
    1086           8 : OGRErr OGRHanaTableLayer::IGetExtent(int geomField, OGREnvelope *extent,
    1087             :                                      bool force)
    1088             : {
    1089           8 :     if (geomField >= 0 && geomField < GetLayerDefn()->GetGeomFieldCount())
    1090             :     {
    1091           8 :         FlushPendingBatches(false);
    1092             :     }
    1093             : 
    1094           8 :     return OGRHanaLayer::IGetExtent(geomField, extent, force);
    1095             : }
    1096             : 
    1097             : /************************************************************************/
    1098             : /*                              GetFeatureCount()                       */
    1099             : /************************************************************************/
    1100             : 
    1101          41 : GIntBig OGRHanaTableLayer::GetFeatureCount(int force)
    1102             : {
    1103          41 :     FlushPendingBatches(false);
    1104             : 
    1105          41 :     return OGRHanaLayer::GetFeatureCount(force);
    1106             : }
    1107             : 
    1108             : /************************************************************************/
    1109             : /*                              ResetReading()                          */
    1110             : /************************************************************************/
    1111             : 
    1112         223 : void OGRHanaTableLayer::ResetReading()
    1113             : {
    1114         223 :     FlushPendingBatches(false);
    1115             : 
    1116         223 :     if (OGRNullFID != fidFieldIndex_ && nextFeatureId_ > 0)
    1117          69 :         allowAutoFIDOnCreateFeature_ = false;
    1118             : 
    1119         223 :     OGRHanaLayer::ResetReading();
    1120         223 : }
    1121             : 
    1122             : /************************************************************************/
    1123             : /*                            TestCapability()                          */
    1124             : /************************************************************************/
    1125             : 
    1126         150 : int OGRHanaTableLayer::TestCapability(const char *capabilities)
    1127             : {
    1128         150 :     if (EQUAL(capabilities, OLCRandomRead))
    1129             :     {
    1130           2 :         EnsureInitialized();
    1131           2 :         return fidFieldIndex_ != OGRNullFID;
    1132             :     }
    1133         148 :     if (EQUAL(capabilities, OLCFastFeatureCount))
    1134          10 :         return TRUE;
    1135         138 :     if (EQUAL(capabilities, OLCFastSpatialFilter))
    1136             :     {
    1137          10 :         EnsureInitialized();
    1138          10 :         return !geomColumns_.empty();
    1139             :     }
    1140         128 :     if (EQUAL(capabilities, OLCFastGetExtent))
    1141             :     {
    1142          12 :         EnsureInitialized();
    1143          12 :         return IsFastExtentAvailable();
    1144             :     }
    1145         116 :     if (EQUAL(capabilities, OLCCreateField))
    1146          12 :         return updateMode_;
    1147         104 :     if (EQUAL(capabilities, OLCCreateGeomField) ||
    1148          94 :         EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
    1149          10 :         return updateMode_;
    1150          94 :     if (EQUAL(capabilities, OLCDeleteField))
    1151           2 :         return updateMode_;
    1152          92 :     if (EQUAL(capabilities, OLCDeleteFeature))
    1153             :     {
    1154          12 :         EnsureInitialized();
    1155          12 :         return updateMode_ && fidFieldIndex_ != OGRNullFID;
    1156             :     }
    1157          80 :     if (EQUAL(capabilities, OLCAlterFieldDefn))
    1158          12 :         return updateMode_;
    1159          68 :     if (EQUAL(capabilities, OLCRandomWrite))
    1160          13 :         return updateMode_;
    1161          55 :     if (EQUAL(capabilities, OLCMeasuredGeometries))
    1162          18 :         return TRUE;
    1163          37 :     if (EQUAL(capabilities, OLCSequentialWrite))
    1164           1 :         return updateMode_;
    1165          36 :     if (EQUAL(capabilities, OLCTransactions))
    1166          11 :         return updateMode_;
    1167          25 :     if (EQUAL(capabilities, OLCStringsAsUTF8))
    1168           1 :         return TRUE;
    1169             : 
    1170          24 :     return FALSE;
    1171             : }
    1172             : 
    1173             : /************************************************************************/
    1174             : /*                          ICreateFeature()                            */
    1175             : /************************************************************************/
    1176             : 
    1177         123 : OGRErr OGRHanaTableLayer::ICreateFeature(OGRFeature *feature)
    1178             : {
    1179         123 :     if (!updateMode_)
    1180             :     {
    1181           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1182             :                  "CreateFeature");
    1183           0 :         return OGRERR_FAILURE;
    1184             :     }
    1185             : 
    1186         123 :     if (nullptr == feature)
    1187             :     {
    1188           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1189             :                  "NULL pointer to OGRFeature passed to CreateFeature().");
    1190           0 :         return OGRERR_FAILURE;
    1191             :     }
    1192             : 
    1193         123 :     EnsureInitialized();
    1194             : 
    1195             :     OGRErr err =
    1196         123 :         ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::UPDATE);
    1197         123 :     if (OGRERR_NONE != err)
    1198           0 :         return err;
    1199             : 
    1200         123 :     bool hasFID = feature->GetFID() != OGRNullFID;
    1201         123 :     if (hasFID && OGRNullFID != fidFieldIndex_)
    1202           7 :         allowAutoFIDOnCreateFeature_ = false;
    1203             : 
    1204         123 :     if (!hasFID && allowAutoFIDOnCreateFeature_)
    1205             :     {
    1206         108 :         feature->SetFID(nextFeatureId_++);
    1207         108 :         hasFID = true;
    1208             :     }
    1209             : 
    1210         123 :     bool useBatch = hasFID && dataSource_->IsTransactionStarted();
    1211             : 
    1212             :     try
    1213             :     {
    1214         123 :         odbc::PreparedStatementRef &stmt =
    1215             :             hasFID ? insertFeatureStmtWithFID_ : insertFeatureStmtWithoutFID_;
    1216             : 
    1217         123 :         if (stmt.isNull())
    1218             :         {
    1219          16 :             stmt = CreateInsertFeatureStatement(hasFID);
    1220          16 :             if (stmt.isNull())
    1221           0 :                 return OGRERR_FAILURE;
    1222             :         }
    1223             : 
    1224         123 :         err = SetStatementParameters(*stmt, feature, true, hasFID,
    1225             :                                      "CreateFeature");
    1226         123 :         if (OGRERR_NONE != err)
    1227           0 :             return err;
    1228             : 
    1229         123 :         if (useBatch)
    1230           5 :             stmt->addBatch();
    1231             : 
    1232         123 :         auto ret = ExecuteUpdate(*stmt, useBatch, "CreateFeature");
    1233             : 
    1234         123 :         err = ret.first;
    1235         123 :         if (OGRERR_NONE != err)
    1236           0 :             return err;
    1237             : 
    1238         123 :         if (!hasFID && OGRNullFID != fidFieldIndex_)
    1239             :         {
    1240           8 :             const CPLString sql = CPLString().Printf(
    1241             :                 "SELECT CURRENT_IDENTITY_VALUE() \"current identity value\" "
    1242             :                 "FROM %s",
    1243          16 :                 GetFullTableNameQuoted(schemaName_, tableName_).c_str());
    1244             : 
    1245           8 :             if (currentIdentityValueStmt_.isNull())
    1246             :             {
    1247             :                 currentIdentityValueStmt_ =
    1248           2 :                     dataSource_->PrepareStatement(sql.c_str());
    1249           2 :                 if (currentIdentityValueStmt_.isNull())
    1250           0 :                     return OGRERR_FAILURE;
    1251             :             }
    1252             : 
    1253             :             odbc::ResultSetRef rsIdentity =
    1254          16 :                 currentIdentityValueStmt_->executeQuery();
    1255           8 :             if (rsIdentity->next())
    1256             :             {
    1257           8 :                 odbc::Long id = rsIdentity->getLong(1);
    1258           8 :                 if (!id.isNull())
    1259           8 :                     feature->SetFID(static_cast<GIntBig>(*id));
    1260             :             }
    1261           8 :             rsIdentity->close();
    1262             :         }
    1263             : 
    1264         123 :         return err;
    1265             :     }
    1266           0 :     catch (const std::exception &ex)
    1267             :     {
    1268           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
    1269           0 :                  ex.what());
    1270           0 :         return OGRERR_FAILURE;
    1271             :     }
    1272             :     return OGRERR_NONE;
    1273             : }
    1274             : 
    1275             : /************************************************************************/
    1276             : /*                           DeleteFeature()                            */
    1277             : /************************************************************************/
    1278             : 
    1279           8 : OGRErr OGRHanaTableLayer::DeleteFeature(GIntBig nFID)
    1280             : {
    1281           8 :     if (!updateMode_)
    1282             :     {
    1283           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1284             :                  "DeleteFeature");
    1285           0 :         return OGRERR_FAILURE;
    1286             :     }
    1287             : 
    1288           8 :     if (nFID == OGRNullFID)
    1289             :     {
    1290           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1291             :                  "DeleteFeature(" CPL_FRMT_GIB
    1292             :                  ") failed.  Unable to delete features "
    1293             :                  "in tables without\n a recognised FID column.",
    1294             :                  nFID);
    1295           0 :         return OGRERR_FAILURE;
    1296             :     }
    1297             : 
    1298           8 :     if (OGRNullFID == fidFieldIndex_)
    1299             :     {
    1300           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1301             :                  "DeleteFeature(" CPL_FRMT_GIB
    1302             :                  ") failed.  Unable to delete features "
    1303             :                  "in tables without\n a recognised FID column.",
    1304             :                  nFID);
    1305           0 :         return OGRERR_FAILURE;
    1306             :     }
    1307             : 
    1308           8 :     EnsureInitialized();
    1309             : 
    1310           8 :     if (deleteFeatureStmt_.isNull())
    1311             :     {
    1312           3 :         deleteFeatureStmt_ = CreateDeleteFeatureStatement();
    1313           3 :         if (deleteFeatureStmt_.isNull())
    1314           0 :             return OGRERR_FAILURE;
    1315             :     }
    1316             : 
    1317             :     OGRErr err =
    1318           8 :         ExecutePendingBatches(BatchOperation::INSERT | BatchOperation::UPDATE);
    1319           8 :     if (OGRERR_NONE != err)
    1320           0 :         return err;
    1321             : 
    1322           8 :     deleteFeatureStmt_->setLong(1, odbc::Long(static_cast<std::int64_t>(nFID)));
    1323           8 :     bool withBatch = dataSource_->IsTransactionStarted();
    1324           8 :     if (withBatch)
    1325           2 :         deleteFeatureStmt_->addBatch();
    1326             : 
    1327           8 :     auto ret = ExecuteUpdate(*deleteFeatureStmt_, withBatch, "DeleteFeature");
    1328           8 :     return (OGRERR_NONE == ret.first && ret.second != 1)
    1329          16 :                ? OGRERR_NON_EXISTING_FEATURE
    1330           8 :                : ret.first;
    1331             : }
    1332             : 
    1333             : /************************************************************************/
    1334             : /*                             ISetFeature()                            */
    1335             : /************************************************************************/
    1336             : 
    1337          13 : OGRErr OGRHanaTableLayer::ISetFeature(OGRFeature *feature)
    1338             : {
    1339          13 :     if (!updateMode_)
    1340             :     {
    1341           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1342             :                  "SetFeature");
    1343           0 :         return OGRERR_FAILURE;
    1344             :     }
    1345             : 
    1346          13 :     if (nullptr == feature)
    1347             :     {
    1348           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1349             :                  "NULL pointer to OGRFeature passed to SetFeature().");
    1350           0 :         return OGRERR_FAILURE;
    1351             :     }
    1352             : 
    1353          13 :     if (feature->GetFID() == OGRNullFID)
    1354             :     {
    1355           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1356             :                  "FID required on features given to SetFeature().");
    1357           0 :         return OGRERR_FAILURE;
    1358             :     }
    1359             : 
    1360          13 :     if (OGRNullFID == fidFieldIndex_)
    1361             :     {
    1362           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1363             :                  "Unable to update features in tables without\n"
    1364             :                  "a recognised FID column.");
    1365           0 :         return OGRERR_FAILURE;
    1366             :     }
    1367             : 
    1368          13 :     EnsureInitialized();
    1369             : 
    1370          13 :     if (updateFeatureStmt_.isNull())
    1371             :     {
    1372           4 :         updateFeatureStmt_ = CreateUpdateFeatureStatement();
    1373           4 :         if (updateFeatureStmt_.isNull())
    1374           0 :             return OGRERR_FAILURE;
    1375             :     }
    1376             : 
    1377          13 :     if (OGRNullFID != fidFieldIndex_)
    1378          13 :         allowAutoFIDOnCreateFeature_ = false;
    1379             : 
    1380             :     OGRErr err =
    1381          13 :         ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::INSERT);
    1382          13 :     if (OGRERR_NONE != err)
    1383           0 :         return err;
    1384             : 
    1385             :     try
    1386             :     {
    1387          13 :         err = SetStatementParameters(*updateFeatureStmt_, feature, false, false,
    1388             :                                      "SetFeature");
    1389             : 
    1390          13 :         if (OGRERR_NONE != err)
    1391           0 :             return err;
    1392             : 
    1393          13 :         bool withBatch = dataSource_->IsTransactionStarted();
    1394          13 :         if (withBatch)
    1395           1 :             updateFeatureStmt_->addBatch();
    1396             : 
    1397          13 :         auto ret = ExecuteUpdate(*updateFeatureStmt_, withBatch, "SetFeature");
    1398          13 :         return (OGRERR_NONE == ret.first && ret.second != 1)
    1399          26 :                    ? OGRERR_NON_EXISTING_FEATURE
    1400          13 :                    : ret.first;
    1401             :     }
    1402           0 :     catch (const std::exception &ex)
    1403             :     {
    1404           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
    1405           0 :                  ex.what());
    1406           0 :         return OGRERR_FAILURE;
    1407             :     }
    1408             : }
    1409             : 
    1410             : /************************************************************************/
    1411             : /*                            CreateField()                             */
    1412             : /************************************************************************/
    1413             : 
    1414          66 : OGRErr OGRHanaTableLayer::CreateField(const OGRFieldDefn *srsField,
    1415             :                                       int approxOK)
    1416             : {
    1417          66 :     if (!updateMode_)
    1418             :     {
    1419           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1420             :                  "CreateField");
    1421           0 :         return OGRERR_FAILURE;
    1422             :     }
    1423             : 
    1424          66 :     if (srsField->GetNameRef() == nullptr)
    1425             :     {
    1426           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Field name cannot be NULL");
    1427           0 :         return OGRERR_FAILURE;
    1428             :     }
    1429             : 
    1430          66 :     EnsureInitialized();
    1431             : 
    1432         132 :     OGRFieldDefn dstField(srsField);
    1433             : 
    1434          66 :     if (launderColumnNames_)
    1435             :     {
    1436          63 :         auto nameRes = dataSource_->LaunderName(dstField.GetNameRef());
    1437          63 :         if (nameRes.first != OGRERR_NONE)
    1438           0 :             return nameRes.first;
    1439          63 :         dstField.SetName(nameRes.second.c_str());
    1440             :     }
    1441             : 
    1442         197 :     if (fidFieldIndex_ != OGRNullFID &&
    1443          67 :         EQUAL(dstField.GetNameRef(), GetFIDColumn()) &&
    1444         133 :         dstField.GetType() != OFTInteger && dstField.GetType() != OFTInteger64)
    1445             :     {
    1446           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1447             :                  dstField.GetNameRef());
    1448           1 :         return OGRERR_FAILURE;
    1449             :     }
    1450             : 
    1451         130 :     ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(dstField);
    1452         130 :     CPLString columnDef = GetColumnDefinition(columnTypeInfo);
    1453             : 
    1454          65 :     if (columnTypeInfo.type == QGRHanaDataTypes::Unknown)
    1455             :     {
    1456           0 :         if (columnTypeInfo.name.empty())
    1457           0 :             return OGRERR_FAILURE;
    1458             : 
    1459           0 :         if (approxOK)
    1460             :         {
    1461           0 :             dstField.SetDefault(nullptr);
    1462           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1463             :                      "Unable to create field %s with type %s on HANA layers. "
    1464             :                      "Creating as VARCHAR.",
    1465             :                      dstField.GetNameRef(),
    1466             :                      OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
    1467           0 :             columnTypeInfo.name = "VARCHAR";
    1468           0 :             columnTypeInfo.width = static_cast<int>(defaultStringSize_);
    1469           0 :             columnDef = "VARCHAR(" + std::to_string(defaultStringSize_) + ")";
    1470             :         }
    1471             :         else
    1472             :         {
    1473           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1474             :                      "Unable to create field %s with type %s on HANA layers.",
    1475             :                      dstField.GetNameRef(),
    1476             :                      OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
    1477             : 
    1478           0 :             return OGRERR_FAILURE;
    1479             :         }
    1480             :     }
    1481             : 
    1482             :     CPLString clmClause =
    1483         195 :         QuotedIdentifier(dstField.GetNameRef()) + " " + columnDef;
    1484          65 :     if (!dstField.IsNullable())
    1485           0 :         clmClause += " NOT NULL";
    1486          65 :     if (dstField.GetDefault() != nullptr && !dstField.IsDefaultDriverSpecific())
    1487             :     {
    1488          12 :         if (IsArrayField(dstField.GetType()) ||
    1489          24 :             columnTypeInfo.type == QGRHanaDataTypes::LongVarBinary ||
    1490          12 :             columnTypeInfo.type == QGRHanaDataTypes::RealVector)
    1491             :         {
    1492           0 :             CPLError(
    1493             :                 CE_Failure, CPLE_NotSupported,
    1494             :                 "Default value cannot be created on column of data type %s: "
    1495             :                 "%s.",
    1496             :                 columnTypeInfo.name.c_str(), dstField.GetNameRef());
    1497             : 
    1498           0 :             return OGRERR_FAILURE;
    1499             :         }
    1500             : 
    1501             :         clmClause +=
    1502          12 :             CPLString().Printf(" DEFAULT %s", GetColumnDefaultValue(dstField));
    1503             :     }
    1504             : 
    1505          65 :     FlushPendingBatches(false);
    1506             : 
    1507          65 :     const CPLString sql = CPLString().Printf(
    1508             :         "ALTER TABLE %s ADD(%s)",
    1509         130 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1510         195 :         clmClause.c_str());
    1511             : 
    1512             :     try
    1513             :     {
    1514          66 :         dataSource_->ExecuteSQL(sql.c_str());
    1515             :     }
    1516           1 :     catch (const odbc::Exception &ex)
    1517             :     {
    1518           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1519             :                  "Failed to execute create attribute field %s: %s",
    1520           1 :                  dstField.GetNameRef(), ex.what());
    1521           1 :         return OGRERR_FAILURE;
    1522             :     }
    1523             : 
    1524             :     // columnTypeInfo might contain a different definition due to custom column
    1525             :     // types
    1526          64 :     SetFieldDefn(dstField, columnTypeInfo);
    1527             : 
    1528          64 :     AttributeColumnDescription clmDesc;
    1529          64 :     clmDesc.name = dstField.GetNameRef();
    1530          64 :     clmDesc.type = columnTypeInfo.type;
    1531          64 :     clmDesc.typeName = columnTypeInfo.name;
    1532          64 :     clmDesc.isArray = IsArrayField(dstField.GetType());
    1533          64 :     clmDesc.length = columnTypeInfo.width;
    1534          64 :     clmDesc.isNullable = dstField.IsNullable();
    1535          64 :     clmDesc.isAutoIncrement = false;  // TODO
    1536          64 :     clmDesc.scale = static_cast<unsigned short>(columnTypeInfo.precision);
    1537          64 :     clmDesc.precision = static_cast<unsigned short>(columnTypeInfo.width);
    1538          64 :     if (dstField.GetDefault() != nullptr)
    1539          12 :         clmDesc.defaultValue = dstField.GetDefault();
    1540             : 
    1541          64 :     attrColumns_.push_back(std::move(clmDesc));
    1542          64 :     featureDefn_->AddFieldDefn(&dstField);
    1543             : 
    1544          64 :     ColumnsChanged();
    1545             : 
    1546          64 :     return OGRERR_NONE;
    1547             : }
    1548             : 
    1549             : /************************************************************************/
    1550             : /*                          CreateGeomField()                           */
    1551             : /************************************************************************/
    1552             : 
    1553           5 : OGRErr OGRHanaTableLayer::CreateGeomField(const OGRGeomFieldDefn *geomField,
    1554             :                                           int)
    1555             : {
    1556           5 :     if (!updateMode_)
    1557             :     {
    1558           1 :         CPLError(CE_Failure, CPLE_AppDefined, UNSUPPORTED_OP_READ_ONLY,
    1559             :                  "CreateGeomField");
    1560           1 :         return OGRERR_FAILURE;
    1561             :     }
    1562             : 
    1563           4 :     if (!IsGeometryTypeSupported(geomField->GetType()))
    1564             :     {
    1565           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1566             :                  "Geometry field '%s' in layer '%s' has unsupported type %s",
    1567             :                  geomField->GetNameRef(), tableName_.c_str(),
    1568             :                  OGRGeometryTypeToName(geomField->GetType()));
    1569           1 :         return OGRERR_FAILURE;
    1570             :     }
    1571             : 
    1572           3 :     EnsureInitialized();
    1573             : 
    1574           3 :     if (featureDefn_->GetGeomFieldIndex(geomField->GetNameRef()) >= 0)
    1575             :     {
    1576           1 :         CPLError(
    1577             :             CE_Failure, CPLE_AppDefined,
    1578             :             "CreateGeomField() called with an already existing field name: %s",
    1579             :             geomField->GetNameRef());
    1580           1 :         return OGRERR_FAILURE;
    1581             :     }
    1582             : 
    1583           2 :     FlushPendingBatches(false);
    1584             : 
    1585           2 :     int srid = dataSource_->GetSrsId(geomField->GetSpatialRef());
    1586           2 :     if (srid == UNDETERMINED_SRID)
    1587             :     {
    1588           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1589             :                  "Unable to determine the srs-id for field name: %s",
    1590             :                  geomField->GetNameRef());
    1591           1 :         return OGRERR_FAILURE;
    1592             :     }
    1593             : 
    1594           2 :     CPLString clmName(geomField->GetNameRef());
    1595           1 :     if (launderColumnNames_)
    1596             :     {
    1597           1 :         auto nameRes = dataSource_->LaunderName(geomField->GetNameRef());
    1598           1 :         if (nameRes.first != OGRERR_NONE)
    1599           0 :             return nameRes.first;
    1600           1 :         clmName.swap(nameRes.second);
    1601             :     }
    1602             : 
    1603           1 :     if (clmName.empty())
    1604           1 :         clmName = FindGeomFieldName(*featureDefn_);
    1605             : 
    1606           1 :     CPLString sql = CPLString().Printf(
    1607             :         "ALTER TABLE %s ADD(%s ST_GEOMETRY(%d))",
    1608           2 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1609           4 :         QuotedIdentifier(clmName).c_str(), srid);
    1610             : 
    1611             :     try
    1612             :     {
    1613           1 :         dataSource_->ExecuteSQL(sql.c_str());
    1614             :     }
    1615           0 :     catch (const odbc::Exception &ex)
    1616             :     {
    1617           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1618             :                  "Failed to execute CreateGeomField() with field name '%s': %s",
    1619           0 :                  geomField->GetNameRef(), ex.what());
    1620           0 :         return OGRERR_FAILURE;
    1621             :     }
    1622             : 
    1623             :     auto newGeomField = std::make_unique<OGRGeomFieldDefn>(
    1624           1 :         clmName.c_str(), geomField->GetType());
    1625           1 :     newGeomField->SetNullable(geomField->IsNullable());
    1626           1 :     newGeomField->SetSpatialRef(geomField->GetSpatialRef());
    1627           2 :     geomColumns_.push_back({newGeomField->GetNameRef(), newGeomField->GetType(),
    1628           1 :                             srid, newGeomField->IsNullable() == TRUE});
    1629           1 :     featureDefn_->AddGeomFieldDefn(std::move(newGeomField));
    1630             : 
    1631           1 :     ColumnsChanged();
    1632             : 
    1633           1 :     return OGRERR_NONE;
    1634             : }
    1635             : 
    1636             : /************************************************************************/
    1637             : /*                            DeleteField()                             */
    1638             : /************************************************************************/
    1639             : 
    1640           0 : OGRErr OGRHanaTableLayer::DeleteField(int field)
    1641             : {
    1642           0 :     if (!updateMode_)
    1643             :     {
    1644           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1645             :                  "DeleteField");
    1646           0 :         return OGRERR_FAILURE;
    1647             :     }
    1648             : 
    1649           0 :     if (field < 0 || field >= featureDefn_->GetFieldCount())
    1650             :     {
    1651           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
    1652           0 :         return OGRERR_FAILURE;
    1653             :     }
    1654             : 
    1655           0 :     EnsureInitialized();
    1656             : 
    1657           0 :     FlushPendingBatches(false);
    1658             : 
    1659           0 :     CPLString clmName = featureDefn_->GetFieldDefn(field)->GetNameRef();
    1660           0 :     CPLString sql = CPLString().Printf(
    1661             :         "ALTER TABLE %s DROP (%s)",
    1662           0 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1663           0 :         QuotedIdentifier(clmName).c_str());
    1664             : 
    1665             :     try
    1666             :     {
    1667           0 :         dataSource_->ExecuteSQL(sql.c_str());
    1668             :     }
    1669           0 :     catch (const odbc::Exception &ex)
    1670             :     {
    1671           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to drop column %s: %s",
    1672           0 :                  clmName.c_str(), ex.what());
    1673           0 :         return OGRERR_FAILURE;
    1674             :     }
    1675             : 
    1676             :     auto it = std::find_if(attrColumns_.begin(), attrColumns_.end(),
    1677           0 :                            [&](const AttributeColumnDescription &cd)
    1678           0 :                            { return cd.name == clmName; });
    1679           0 :     attrColumns_.erase(it);
    1680           0 :     OGRErr ret = featureDefn_->DeleteFieldDefn(field);
    1681             : 
    1682           0 :     ColumnsChanged();
    1683             : 
    1684           0 :     return ret;
    1685             : }
    1686             : 
    1687             : /************************************************************************/
    1688             : /*                            AlterFieldDefn()                          */
    1689             : /************************************************************************/
    1690             : 
    1691           0 : OGRErr OGRHanaTableLayer::AlterFieldDefn(int field, OGRFieldDefn *newFieldDefn,
    1692             :                                          int flagsIn)
    1693             : {
    1694           0 :     if (!updateMode_)
    1695             :     {
    1696           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1697             :                  "AlterFieldDefn");
    1698           0 :         return OGRERR_FAILURE;
    1699             :     }
    1700             : 
    1701           0 :     EnsureInitialized();
    1702             : 
    1703           0 :     if (field < 0 || field >= featureDefn_->GetFieldCount())
    1704             :     {
    1705           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
    1706           0 :         return OGRERR_FAILURE;
    1707             :     }
    1708             : 
    1709           0 :     OGRFieldDefn *fieldDefn = featureDefn_->GetFieldDefn(field);
    1710             : 
    1711           0 :     int columnDescIdx = -1;
    1712           0 :     for (size_t i = 0; i < attrColumns_.size(); ++i)
    1713             :     {
    1714           0 :         if (EQUAL(attrColumns_[i].name.c_str(), fieldDefn->GetNameRef()))
    1715             :         {
    1716           0 :             columnDescIdx = static_cast<int>(i);
    1717           0 :             break;
    1718             :         }
    1719             :     }
    1720             : 
    1721           0 :     if (columnDescIdx < 0)
    1722             :     {
    1723           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1724             :                  "Column description cannot be found");
    1725           0 :         return OGRERR_FAILURE;
    1726             :     }
    1727             : 
    1728           0 :     CPLString clmName(newFieldDefn->GetNameRef());
    1729             : 
    1730           0 :     if (launderColumnNames_)
    1731             :     {
    1732           0 :         auto nameRes = dataSource_->LaunderName(newFieldDefn->GetNameRef());
    1733           0 :         if (nameRes.first != OGRERR_NONE)
    1734           0 :             return nameRes.first;
    1735           0 :         clmName.swap(nameRes.second);
    1736             :     }
    1737             : 
    1738           0 :     FlushPendingBatches(false);
    1739             : 
    1740           0 :     ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(*newFieldDefn);
    1741             : 
    1742             :     try
    1743             :     {
    1744           0 :         if ((flagsIn & ALTER_NAME_FLAG) &&
    1745           0 :             (strcmp(fieldDefn->GetNameRef(), newFieldDefn->GetNameRef()) != 0))
    1746             :         {
    1747           0 :             CPLString sql = CPLString().Printf(
    1748             :                 "RENAME COLUMN %s TO %s",
    1749           0 :                 GetFullColumnNameQuoted(schemaName_, tableName_,
    1750             :                                         fieldDefn->GetNameRef())
    1751             :                     .c_str(),
    1752           0 :                 QuotedIdentifier(clmName).c_str());
    1753           0 :             dataSource_->ExecuteSQL(sql.c_str());
    1754             :         }
    1755             : 
    1756           0 :         if ((flagsIn & ALTER_TYPE_FLAG) ||
    1757           0 :             (flagsIn & ALTER_WIDTH_PRECISION_FLAG) ||
    1758           0 :             (flagsIn & ALTER_NULLABLE_FLAG) || (flagsIn & ALTER_DEFAULT_FLAG))
    1759             :         {
    1760           0 :             CPLString fieldTypeDef = GetColumnDefinition(columnTypeInfo);
    1761           0 :             if ((flagsIn & ALTER_NULLABLE_FLAG) &&
    1762           0 :                 fieldDefn->IsNullable() != newFieldDefn->IsNullable())
    1763             :             {
    1764           0 :                 if (fieldDefn->IsNullable())
    1765           0 :                     fieldTypeDef += " NULL";
    1766             :                 else
    1767           0 :                     fieldTypeDef += " NOT NULL";
    1768             :             }
    1769             : 
    1770           0 :             if ((flagsIn & ALTER_DEFAULT_FLAG) &&
    1771           0 :                 ((fieldDefn->GetDefault() == nullptr &&
    1772           0 :                   newFieldDefn->GetDefault() != nullptr) ||
    1773           0 :                  (fieldDefn->GetDefault() != nullptr &&
    1774           0 :                   newFieldDefn->GetDefault() == nullptr) ||
    1775           0 :                  (fieldDefn->GetDefault() != nullptr &&
    1776           0 :                   newFieldDefn->GetDefault() != nullptr &&
    1777           0 :                   strcmp(fieldDefn->GetDefault(), newFieldDefn->GetDefault()) !=
    1778             :                       0)))
    1779             :             {
    1780             :                 fieldTypeDef +=
    1781           0 :                     " DEFAULT " + ((fieldDefn->GetType() == OFTString)
    1782           0 :                                        ? Literal(newFieldDefn->GetDefault())
    1783           0 :                                        : CPLString(newFieldDefn->GetDefault()));
    1784             :             }
    1785             : 
    1786           0 :             CPLString sql = CPLString().Printf(
    1787             :                 "ALTER TABLE %s ALTER(%s %s)",
    1788           0 :                 GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1789           0 :                 QuotedIdentifier(clmName).c_str(), fieldTypeDef.c_str());
    1790             : 
    1791           0 :             dataSource_->ExecuteSQL(sql.c_str());
    1792             :         }
    1793             :     }
    1794           0 :     catch (const odbc::Exception &ex)
    1795             :     {
    1796           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to alter field %s: %s",
    1797           0 :                  fieldDefn->GetNameRef(), ex.what());
    1798           0 :         return OGRERR_FAILURE;
    1799             :     }
    1800             : 
    1801             :     // Update field definition and column description
    1802             : 
    1803           0 :     AttributeColumnDescription &attrClmDesc = attrColumns_[columnDescIdx];
    1804             : 
    1805           0 :     if (flagsIn & ALTER_NAME_FLAG)
    1806             :     {
    1807           0 :         fieldDefn->SetName(clmName.c_str());
    1808           0 :         attrClmDesc.name.assign(clmName.c_str());
    1809             :     }
    1810             : 
    1811           0 :     if (flagsIn & ALTER_TYPE_FLAG)
    1812             :     {
    1813           0 :         fieldDefn->SetSubType(OFSTNone);
    1814           0 :         fieldDefn->SetType(newFieldDefn->GetType());
    1815           0 :         fieldDefn->SetSubType(newFieldDefn->GetSubType());
    1816           0 :         attrClmDesc.isArray = IsArrayField(newFieldDefn->GetType());
    1817           0 :         attrClmDesc.type = columnTypeInfo.type;
    1818           0 :         attrClmDesc.typeName = columnTypeInfo.name;
    1819             :     }
    1820             : 
    1821           0 :     if (flagsIn & ALTER_WIDTH_PRECISION_FLAG)
    1822             :     {
    1823           0 :         fieldDefn->SetWidth(newFieldDefn->GetWidth());
    1824           0 :         fieldDefn->SetPrecision(newFieldDefn->GetPrecision());
    1825           0 :         attrClmDesc.length = newFieldDefn->GetWidth();
    1826           0 :         attrClmDesc.scale = newFieldDefn->GetWidth();
    1827           0 :         attrClmDesc.precision = newFieldDefn->GetPrecision();
    1828             :     }
    1829             : 
    1830           0 :     if (flagsIn & ALTER_NULLABLE_FLAG)
    1831             :     {
    1832           0 :         fieldDefn->SetNullable(newFieldDefn->IsNullable());
    1833           0 :         attrClmDesc.isNullable = newFieldDefn->IsNullable();
    1834             :     }
    1835             : 
    1836           0 :     if (flagsIn & ALTER_DEFAULT_FLAG)
    1837             :     {
    1838           0 :         fieldDefn->SetDefault(newFieldDefn->GetDefault());
    1839           0 :         attrClmDesc.name.assign(newFieldDefn->GetDefault());
    1840             :     }
    1841             : 
    1842           0 :     ColumnsChanged();
    1843             : 
    1844           0 :     return OGRERR_NONE;
    1845             : }
    1846             : 
    1847             : /************************************************************************/
    1848             : /*                          ClearBatches()                              */
    1849             : /************************************************************************/
    1850             : 
    1851           4 : void OGRHanaTableLayer::ClearBatches()
    1852             : {
    1853           4 :     if (!insertFeatureStmtWithFID_.isNull())
    1854           4 :         insertFeatureStmtWithFID_->clearBatch();
    1855           4 :     if (!insertFeatureStmtWithoutFID_.isNull())
    1856           1 :         insertFeatureStmtWithoutFID_->clearBatch();
    1857           4 :     if (!updateFeatureStmt_.isNull())
    1858           2 :         updateFeatureStmt_->clearBatch();
    1859           4 : }
    1860             : 
    1861             : /************************************************************************/
    1862             : /*                          ColumnsChanged()                            */
    1863             : /************************************************************************/
    1864             : 
    1865          65 : void OGRHanaTableLayer::ColumnsChanged()
    1866             : {
    1867          65 :     ClearQueryStatement();
    1868          65 :     ResetReading();
    1869          65 :     ResetPreparedStatements();
    1870          65 : }
    1871             : 
    1872             : /************************************************************************/
    1873             : /*                          SetCustomColumnTypes()                      */
    1874             : /************************************************************************/
    1875             : 
    1876          18 : void OGRHanaTableLayer::SetCustomColumnTypes(const char *columnTypes)
    1877             : {
    1878          18 :     if (columnTypes == nullptr)
    1879          17 :         return;
    1880             : 
    1881           1 :     const char *ptr = columnTypes;
    1882           1 :     const char *start = ptr;
    1883          42 :     while (*ptr != '\0')
    1884             :     {
    1885          41 :         if (*ptr == '(')
    1886             :         {
    1887             :             // Skip commas inside brackets, for example decimal(20,5)
    1888          12 :             while (*ptr != '\0' && *ptr != ')')
    1889             :             {
    1890          10 :                 ++ptr;
    1891             :             }
    1892             :         }
    1893             : 
    1894          41 :         ++ptr;
    1895             : 
    1896          41 :         if (*ptr == ',' || *ptr == '\0')
    1897             :         {
    1898           3 :             std::size_t len = static_cast<std::size_t>(ptr - start);
    1899           3 :             const char *sep = std::find(start, start + len, '=');
    1900           3 :             if (sep != nullptr)
    1901             :             {
    1902           3 :                 std::size_t pos = static_cast<std::size_t>(sep - start);
    1903           6 :                 customColumnDefs_.push_back(
    1904             :                     {CPLString(start, pos),
    1905           3 :                      CPLString(start + pos + 1, len - pos - 1)});
    1906             :             }
    1907             : 
    1908           3 :             start = ptr + 1;
    1909             :         }
    1910             :     }
    1911             : }
    1912             : 
    1913             : /************************************************************************/
    1914             : /*                          StartTransaction()                          */
    1915             : /************************************************************************/
    1916             : 
    1917          11 : OGRErr OGRHanaTableLayer::StartTransaction()
    1918             : {
    1919          11 :     return dataSource_->StartTransaction();
    1920             : }
    1921             : 
    1922             : /************************************************************************/
    1923             : /*                          CommitTransaction()                         */
    1924             : /************************************************************************/
    1925             : 
    1926           7 : OGRErr OGRHanaTableLayer::CommitTransaction()
    1927             : {
    1928           7 :     if (HasPendingBatches())
    1929             :     {
    1930           0 :         OGRErr err = ExecutePendingBatches(BatchOperation::ALL);
    1931           0 :         if (OGRERR_NONE != err)
    1932           0 :             return err;
    1933             :     }
    1934             : 
    1935           7 :     return dataSource_->CommitTransaction();
    1936             : }
    1937             : 
    1938             : /************************************************************************/
    1939             : /*                          RollbackTransaction()                       */
    1940             : /************************************************************************/
    1941             : 
    1942           4 : OGRErr OGRHanaTableLayer::RollbackTransaction()
    1943             : {
    1944           4 :     ClearBatches();
    1945           4 :     return dataSource_->RollbackTransaction();
    1946             : }
    1947             : 
    1948             : }  // namespace OGRHANA

Generated by: LCOV version 1.14