LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/hana - ogrhanatablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 672 1033 65.1 %
Date: 2025-09-10 17:48:50 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         945 : void OGRHanaTableLayer::FlushPendingBatches(bool commit)
     921             : {
     922         945 :     if (!HasPendingBatches())
     923         941 :         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         952 : bool OGRHanaTableLayer::HasPendingBatches() const
     935             : {
     936         952 :     return (!deleteFeatureStmt_.isNull() &&
     937          75 :             deleteFeatureStmt_->getBatchDataSize() > 0) ||
     938         951 :            (!insertFeatureStmtWithFID_.isNull() &&
     939         125 :             insertFeatureStmtWithFID_->getBatchDataSize() > 0) ||
     940         949 :            (!insertFeatureStmtWithoutFID_.isNull() &&
     941        1933 :             insertFeatureStmtWithoutFID_->getBatchDataSize() > 0) ||
     942         949 :            (!updateFeatureStmt_.isNull() &&
     943        1025 :             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             :             }
     976           4 :             return {"INTEGER", QGRHanaDataTypes::Integer, field.GetWidth(), 0};
     977             : 
     978          11 :         case OFTInteger64:
     979          11 :             if (preservePrecision_ && field.GetWidth() > 20)
     980             :             {
     981           0 :                 return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
     982           0 :                         0};
     983             :             }
     984          11 :             return {"BIGINT", QGRHanaDataTypes::BigInt, field.GetWidth(), 0};
     985             : 
     986          13 :         case OFTReal:
     987          13 :             if (preservePrecision_ && field.GetWidth() != 0)
     988             :             {
     989           1 :                 return {"DECIMAL", QGRHanaDataTypes::Decimal, field.GetWidth(),
     990           1 :                         field.GetPrecision()};
     991             :             }
     992             :             else
     993             :             {
     994          12 :                 if (field.GetSubType() == OFSTFloat32)
     995           1 :                     return {"REAL", QGRHanaDataTypes::Real, field.GetWidth(),
     996           1 :                             field.GetPrecision()};
     997             :             }
     998          11 :             return {"DOUBLE", QGRHanaDataTypes::Double, field.GetWidth(),
     999          11 :                     field.GetPrecision()};
    1000          23 :         case OFTString:
    1001          23 :             if (field.GetWidth() == 0 || !preservePrecision_)
    1002             :             {
    1003          13 :                 int width = static_cast<int>(defaultStringSize_);
    1004          13 :                 return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar, width, 0};
    1005             :             }
    1006             :             else
    1007             :             {
    1008          10 :                 if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
    1009             :                     return {"NVARCHAR", QGRHanaDataTypes::WLongVarChar,
    1010          10 :                             field.GetWidth(), 0};
    1011             :             }
    1012           0 :             return {"NCLOB", QGRHanaDataTypes::WLongVarChar, 0, 0};
    1013           1 :         case OFTBinary:
    1014           1 :             if (field.GetWidth() >= 1 && field.GetWidth() <= 5000)
    1015             :                 return {"VARBINARY", QGRHanaDataTypes::VarBinary,
    1016           1 :                         field.GetWidth(), 0};
    1017           0 :             return {"BLOB", QGRHanaDataTypes::LongVarBinary, field.GetWidth(),
    1018           0 :                     0};
    1019           1 :         case OFTDate:
    1020           1 :             return {"DATE", QGRHanaDataTypes::TypeDate, field.GetWidth(), 0};
    1021           1 :         case OFTTime:
    1022           1 :             return {"TIME", QGRHanaDataTypes::TypeTime, field.GetWidth(), 0};
    1023           2 :         case OFTDateTime:
    1024             :             return {"TIMESTAMP", QGRHanaDataTypes::TypeTimestamp,
    1025           2 :                     field.GetWidth(), 0};
    1026           1 :         case OFTIntegerList:
    1027           1 :             if (field.GetSubType() == OGRFieldSubType::OFSTInt16)
    1028           0 :                 return {"ARRAY", QGRHanaDataTypes::SmallInt, field.GetWidth(),
    1029           0 :                         0};
    1030           1 :             return {"ARRAY", QGRHanaDataTypes::Integer, field.GetWidth(), 0};
    1031           1 :         case OFTInteger64List:
    1032           1 :             return {"ARRAY", QGRHanaDataTypes::BigInt, field.GetWidth(), 0};
    1033           1 :         case OFTRealList:
    1034           1 :             if (field.GetSubType() == OGRFieldSubType::OFSTFloat32)
    1035           0 :                 return {"ARRAY", QGRHanaDataTypes::Real, field.GetWidth(), 0};
    1036           1 :             return {"ARRAY", QGRHanaDataTypes::Double, field.GetWidth(), 0};
    1037           1 :         case OFTStringList:
    1038           1 :             return {"ARRAY", QGRHanaDataTypes::WVarChar, 512, 0};
    1039           0 :         default:
    1040           0 :             break;
    1041             :     }
    1042             : 
    1043           0 :     return {"", QGRHanaDataTypes::Unknown, 0, 0};
    1044             : }
    1045             : 
    1046             : /* -------------------------------------------------------------------- */
    1047             : /*                           GetGeometryWkb()                           */
    1048             : /* -------------------------------------------------------------------- */
    1049         132 : OGRErr OGRHanaTableLayer::GetGeometryWkb(OGRFeature *feature, int fieldIndex,
    1050             :                                          Binary &binary)
    1051             : {
    1052         132 :     OGRGeometry *geom = feature->GetGeomFieldRef(fieldIndex);
    1053         132 :     if (geom == nullptr || !IsGeometryTypeSupported(geom->getIsoGeometryType()))
    1054          10 :         return OGRERR_NONE;
    1055             : 
    1056             :     // Rings must be closed, otherwise HANA throws an exception
    1057         122 :     geom->closeRings();
    1058         122 :     std::size_t size = static_cast<std::size_t>(geom->WkbSize());
    1059         122 :     EnsureBufferCapacity(size);
    1060         122 :     unsigned char *data = reinterpret_cast<unsigned char *>(dataBuffer_.data());
    1061         122 :     OGRErr err = geom->exportToWkb(OGRwkbByteOrder::wkbNDR, data,
    1062             :                                    OGRwkbVariant::wkbVariantIso);
    1063         122 :     if (OGRERR_NONE == err)
    1064             :     {
    1065         122 :         binary.data = data;
    1066         122 :         binary.size = size;
    1067             :     }
    1068         122 :     return err;
    1069             : }
    1070             : 
    1071             : /************************************************************************/
    1072             : /*                                IGetExtent()                          */
    1073             : /************************************************************************/
    1074             : 
    1075           8 : OGRErr OGRHanaTableLayer::IGetExtent(int geomField, OGREnvelope *extent,
    1076             :                                      bool force)
    1077             : {
    1078           8 :     if (geomField >= 0 && geomField < GetLayerDefn()->GetGeomFieldCount())
    1079             :     {
    1080           8 :         FlushPendingBatches(false);
    1081             :     }
    1082             : 
    1083           8 :     return OGRHanaLayer::IGetExtent(geomField, extent, force);
    1084             : }
    1085             : 
    1086             : /************************************************************************/
    1087             : /*                              GetFeatureCount()                       */
    1088             : /************************************************************************/
    1089             : 
    1090          41 : GIntBig OGRHanaTableLayer::GetFeatureCount(int force)
    1091             : {
    1092          41 :     FlushPendingBatches(false);
    1093             : 
    1094          41 :     return OGRHanaLayer::GetFeatureCount(force);
    1095             : }
    1096             : 
    1097             : /************************************************************************/
    1098             : /*                              ResetReading()                          */
    1099             : /************************************************************************/
    1100             : 
    1101         225 : void OGRHanaTableLayer::ResetReading()
    1102             : {
    1103         225 :     FlushPendingBatches(false);
    1104             : 
    1105         225 :     if (OGRNullFID != fidFieldIndex_ && nextFeatureId_ > 0)
    1106          71 :         allowAutoFIDOnCreateFeature_ = false;
    1107             : 
    1108         225 :     OGRHanaLayer::ResetReading();
    1109         225 : }
    1110             : 
    1111             : /************************************************************************/
    1112             : /*                            TestCapability()                          */
    1113             : /************************************************************************/
    1114             : 
    1115         151 : int OGRHanaTableLayer::TestCapability(const char *capabilities) const
    1116             : {
    1117         151 :     if (EQUAL(capabilities, OLCRandomRead))
    1118             :     {
    1119           2 :         const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
    1120           2 :         return fidFieldIndex_ != OGRNullFID;
    1121             :     }
    1122         149 :     if (EQUAL(capabilities, OLCFastFeatureCount))
    1123          10 :         return TRUE;
    1124         139 :     if (EQUAL(capabilities, OLCFastSpatialFilter))
    1125             :     {
    1126          10 :         const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
    1127          10 :         return !geomColumns_.empty();
    1128             :     }
    1129         129 :     if (EQUAL(capabilities, OLCFastGetExtent))
    1130             :     {
    1131          12 :         const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
    1132          12 :         return IsFastExtentAvailable();
    1133             :     }
    1134         117 :     if (EQUAL(capabilities, OLCCreateField))
    1135          12 :         return updateMode_;
    1136         105 :     if (EQUAL(capabilities, OLCCreateGeomField) ||
    1137          95 :         EQUAL(capabilities, ODsCCreateGeomFieldAfterCreateLayer))
    1138          10 :         return updateMode_;
    1139          95 :     if (EQUAL(capabilities, OLCDeleteField))
    1140           2 :         return updateMode_;
    1141          93 :     if (EQUAL(capabilities, OLCDeleteFeature))
    1142             :     {
    1143          12 :         const_cast<OGRHanaTableLayer *>(this)->EnsureInitialized();
    1144          12 :         return updateMode_ && fidFieldIndex_ != OGRNullFID;
    1145             :     }
    1146          81 :     if (EQUAL(capabilities, OLCAlterFieldDefn))
    1147          12 :         return updateMode_;
    1148          69 :     if (EQUAL(capabilities, OLCRandomWrite))
    1149          13 :         return updateMode_;
    1150          56 :     if (EQUAL(capabilities, OLCMeasuredGeometries))
    1151          18 :         return TRUE;
    1152          38 :     if (EQUAL(capabilities, OLCSequentialWrite))
    1153           1 :         return updateMode_;
    1154          37 :     if (EQUAL(capabilities, OLCTransactions))
    1155          11 :         return updateMode_;
    1156          26 :     if (EQUAL(capabilities, OLCStringsAsUTF8))
    1157           1 :         return TRUE;
    1158             : 
    1159          25 :     return FALSE;
    1160             : }
    1161             : 
    1162             : /************************************************************************/
    1163             : /*                          ICreateFeature()                            */
    1164             : /************************************************************************/
    1165             : 
    1166         123 : OGRErr OGRHanaTableLayer::ICreateFeature(OGRFeature *feature)
    1167             : {
    1168         123 :     if (!updateMode_)
    1169             :     {
    1170           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1171             :                  "CreateFeature");
    1172           0 :         return OGRERR_FAILURE;
    1173             :     }
    1174             : 
    1175         123 :     if (nullptr == feature)
    1176             :     {
    1177           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1178             :                  "NULL pointer to OGRFeature passed to CreateFeature().");
    1179           0 :         return OGRERR_FAILURE;
    1180             :     }
    1181             : 
    1182         123 :     EnsureInitialized();
    1183             : 
    1184             :     OGRErr err =
    1185         123 :         ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::UPDATE);
    1186         123 :     if (OGRERR_NONE != err)
    1187           0 :         return err;
    1188             : 
    1189         123 :     bool hasFID = feature->GetFID() != OGRNullFID;
    1190         123 :     if (hasFID && OGRNullFID != fidFieldIndex_)
    1191           7 :         allowAutoFIDOnCreateFeature_ = false;
    1192             : 
    1193         123 :     if (!hasFID && allowAutoFIDOnCreateFeature_)
    1194             :     {
    1195         108 :         feature->SetFID(nextFeatureId_++);
    1196         108 :         hasFID = true;
    1197             :     }
    1198             : 
    1199         123 :     bool useBatch = hasFID && dataSource_->IsTransactionStarted();
    1200             : 
    1201             :     try
    1202             :     {
    1203         123 :         odbc::PreparedStatementRef &stmt =
    1204             :             hasFID ? insertFeatureStmtWithFID_ : insertFeatureStmtWithoutFID_;
    1205             : 
    1206         123 :         if (stmt.isNull())
    1207             :         {
    1208          16 :             stmt = CreateInsertFeatureStatement(hasFID);
    1209          16 :             if (stmt.isNull())
    1210           0 :                 return OGRERR_FAILURE;
    1211             :         }
    1212             : 
    1213         123 :         err = SetStatementParameters(*stmt, feature, true, hasFID,
    1214             :                                      "CreateFeature");
    1215         123 :         if (OGRERR_NONE != err)
    1216           0 :             return err;
    1217             : 
    1218         123 :         if (useBatch)
    1219           5 :             stmt->addBatch();
    1220             : 
    1221         123 :         auto ret = ExecuteUpdate(*stmt, useBatch, "CreateFeature");
    1222             : 
    1223         123 :         err = ret.first;
    1224         123 :         if (OGRERR_NONE != err)
    1225           0 :             return err;
    1226             : 
    1227         123 :         if (!hasFID && OGRNullFID != fidFieldIndex_)
    1228             :         {
    1229           8 :             const CPLString sql = CPLString().Printf(
    1230             :                 "SELECT CURRENT_IDENTITY_VALUE() \"current identity value\" "
    1231             :                 "FROM %s",
    1232          16 :                 GetFullTableNameQuoted(schemaName_, tableName_).c_str());
    1233             : 
    1234           8 :             if (currentIdentityValueStmt_.isNull())
    1235             :             {
    1236             :                 currentIdentityValueStmt_ =
    1237           2 :                     dataSource_->PrepareStatement(sql.c_str());
    1238           2 :                 if (currentIdentityValueStmt_.isNull())
    1239           0 :                     return OGRERR_FAILURE;
    1240             :             }
    1241             : 
    1242             :             odbc::ResultSetRef rsIdentity =
    1243          16 :                 currentIdentityValueStmt_->executeQuery();
    1244           8 :             if (rsIdentity->next())
    1245             :             {
    1246           8 :                 odbc::Long id = rsIdentity->getLong(1);
    1247           8 :                 if (!id.isNull())
    1248           8 :                     feature->SetFID(static_cast<GIntBig>(*id));
    1249             :             }
    1250           8 :             rsIdentity->close();
    1251             :         }
    1252             : 
    1253         123 :         return err;
    1254             :     }
    1255           0 :     catch (const std::exception &ex)
    1256             :     {
    1257           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
    1258           0 :                  ex.what());
    1259           0 :         return OGRERR_FAILURE;
    1260             :     }
    1261             :     return OGRERR_NONE;
    1262             : }
    1263             : 
    1264             : /************************************************************************/
    1265             : /*                           DeleteFeature()                            */
    1266             : /************************************************************************/
    1267             : 
    1268           8 : OGRErr OGRHanaTableLayer::DeleteFeature(GIntBig nFID)
    1269             : {
    1270           8 :     if (!updateMode_)
    1271             :     {
    1272           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1273             :                  "DeleteFeature");
    1274           0 :         return OGRERR_FAILURE;
    1275             :     }
    1276             : 
    1277           8 :     if (nFID == OGRNullFID)
    1278             :     {
    1279           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1280             :                  "DeleteFeature(" CPL_FRMT_GIB
    1281             :                  ") failed.  Unable to delete features "
    1282             :                  "in tables without\n a recognised FID column.",
    1283             :                  nFID);
    1284           0 :         return OGRERR_FAILURE;
    1285             :     }
    1286             : 
    1287           8 :     if (OGRNullFID == fidFieldIndex_)
    1288             :     {
    1289           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1290             :                  "DeleteFeature(" CPL_FRMT_GIB
    1291             :                  ") failed.  Unable to delete features "
    1292             :                  "in tables without\n a recognised FID column.",
    1293             :                  nFID);
    1294           0 :         return OGRERR_FAILURE;
    1295             :     }
    1296             : 
    1297           8 :     EnsureInitialized();
    1298             : 
    1299           8 :     if (deleteFeatureStmt_.isNull())
    1300             :     {
    1301           3 :         deleteFeatureStmt_ = CreateDeleteFeatureStatement();
    1302           3 :         if (deleteFeatureStmt_.isNull())
    1303           0 :             return OGRERR_FAILURE;
    1304             :     }
    1305             : 
    1306             :     OGRErr err =
    1307           8 :         ExecutePendingBatches(BatchOperation::INSERT | BatchOperation::UPDATE);
    1308           8 :     if (OGRERR_NONE != err)
    1309           0 :         return err;
    1310             : 
    1311           8 :     deleteFeatureStmt_->setLong(1, odbc::Long(static_cast<std::int64_t>(nFID)));
    1312           8 :     bool withBatch = dataSource_->IsTransactionStarted();
    1313           8 :     if (withBatch)
    1314           2 :         deleteFeatureStmt_->addBatch();
    1315             : 
    1316           8 :     auto ret = ExecuteUpdate(*deleteFeatureStmt_, withBatch, "DeleteFeature");
    1317           8 :     return (OGRERR_NONE == ret.first && ret.second != 1)
    1318          16 :                ? OGRERR_NON_EXISTING_FEATURE
    1319           8 :                : ret.first;
    1320             : }
    1321             : 
    1322             : /************************************************************************/
    1323             : /*                             ISetFeature()                            */
    1324             : /************************************************************************/
    1325             : 
    1326          13 : OGRErr OGRHanaTableLayer::ISetFeature(OGRFeature *feature)
    1327             : {
    1328          13 :     if (!updateMode_)
    1329             :     {
    1330           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1331             :                  "SetFeature");
    1332           0 :         return OGRERR_FAILURE;
    1333             :     }
    1334             : 
    1335          13 :     if (nullptr == feature)
    1336             :     {
    1337           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1338             :                  "NULL pointer to OGRFeature passed to SetFeature().");
    1339           0 :         return OGRERR_FAILURE;
    1340             :     }
    1341             : 
    1342          13 :     if (feature->GetFID() == OGRNullFID)
    1343             :     {
    1344           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1345             :                  "FID required on features given to SetFeature().");
    1346           0 :         return OGRERR_FAILURE;
    1347             :     }
    1348             : 
    1349          13 :     if (OGRNullFID == fidFieldIndex_)
    1350             :     {
    1351           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1352             :                  "Unable to update features in tables without\n"
    1353             :                  "a recognised FID column.");
    1354           0 :         return OGRERR_FAILURE;
    1355             :     }
    1356             : 
    1357          13 :     EnsureInitialized();
    1358             : 
    1359          13 :     if (updateFeatureStmt_.isNull())
    1360             :     {
    1361           4 :         updateFeatureStmt_ = CreateUpdateFeatureStatement();
    1362           4 :         if (updateFeatureStmt_.isNull())
    1363           0 :             return OGRERR_FAILURE;
    1364             :     }
    1365             : 
    1366          13 :     if (OGRNullFID != fidFieldIndex_)
    1367          13 :         allowAutoFIDOnCreateFeature_ = false;
    1368             : 
    1369             :     OGRErr err =
    1370          13 :         ExecutePendingBatches(BatchOperation::DELETE | BatchOperation::INSERT);
    1371          13 :     if (OGRERR_NONE != err)
    1372           0 :         return err;
    1373             : 
    1374             :     try
    1375             :     {
    1376          13 :         err = SetStatementParameters(*updateFeatureStmt_, feature, false, false,
    1377             :                                      "SetFeature");
    1378             : 
    1379          13 :         if (OGRERR_NONE != err)
    1380           0 :             return err;
    1381             : 
    1382          13 :         bool withBatch = dataSource_->IsTransactionStarted();
    1383          13 :         if (withBatch)
    1384           1 :             updateFeatureStmt_->addBatch();
    1385             : 
    1386          13 :         auto ret = ExecuteUpdate(*updateFeatureStmt_, withBatch, "SetFeature");
    1387          13 :         return (OGRERR_NONE == ret.first && ret.second != 1)
    1388          26 :                    ? OGRERR_NON_EXISTING_FEATURE
    1389          13 :                    : ret.first;
    1390             :     }
    1391           0 :     catch (const std::exception &ex)
    1392             :     {
    1393           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unable to create feature: %s",
    1394           0 :                  ex.what());
    1395           0 :         return OGRERR_FAILURE;
    1396             :     }
    1397             : }
    1398             : 
    1399             : /************************************************************************/
    1400             : /*                            CreateField()                             */
    1401             : /************************************************************************/
    1402             : 
    1403          66 : OGRErr OGRHanaTableLayer::CreateField(const OGRFieldDefn *srsField,
    1404             :                                       int approxOK)
    1405             : {
    1406          66 :     if (!updateMode_)
    1407             :     {
    1408           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1409             :                  "CreateField");
    1410           0 :         return OGRERR_FAILURE;
    1411             :     }
    1412             : 
    1413          66 :     if (srsField->GetNameRef() == nullptr)
    1414             :     {
    1415           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Field name cannot be NULL");
    1416           0 :         return OGRERR_FAILURE;
    1417             :     }
    1418             : 
    1419          66 :     EnsureInitialized();
    1420             : 
    1421         132 :     OGRFieldDefn dstField(srsField);
    1422             : 
    1423          66 :     if (launderColumnNames_)
    1424             :     {
    1425          63 :         auto nameRes = dataSource_->LaunderName(dstField.GetNameRef());
    1426          63 :         if (nameRes.first != OGRERR_NONE)
    1427           0 :             return nameRes.first;
    1428          63 :         dstField.SetName(nameRes.second.c_str());
    1429             :     }
    1430             : 
    1431         197 :     if (fidFieldIndex_ != OGRNullFID &&
    1432          67 :         EQUAL(dstField.GetNameRef(), GetFIDColumn()) &&
    1433         133 :         dstField.GetType() != OFTInteger && dstField.GetType() != OFTInteger64)
    1434             :     {
    1435           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1436             :                  dstField.GetNameRef());
    1437           1 :         return OGRERR_FAILURE;
    1438             :     }
    1439             : 
    1440         130 :     ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(dstField);
    1441         130 :     CPLString columnDef = GetColumnDefinition(columnTypeInfo);
    1442             : 
    1443          65 :     if (columnTypeInfo.type == QGRHanaDataTypes::Unknown)
    1444             :     {
    1445           0 :         if (columnTypeInfo.name.empty())
    1446           0 :             return OGRERR_FAILURE;
    1447             : 
    1448           0 :         if (approxOK)
    1449             :         {
    1450           0 :             dstField.SetDefault(nullptr);
    1451           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1452             :                      "Unable to create field %s with type %s on HANA layers. "
    1453             :                      "Creating as VARCHAR.",
    1454             :                      dstField.GetNameRef(),
    1455             :                      OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
    1456           0 :             columnTypeInfo.name = "VARCHAR";
    1457           0 :             columnTypeInfo.width = static_cast<int>(defaultStringSize_);
    1458           0 :             columnDef = "VARCHAR(" + std::to_string(defaultStringSize_) + ")";
    1459             :         }
    1460             :         else
    1461             :         {
    1462           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1463             :                      "Unable to create field %s with type %s on HANA layers.",
    1464             :                      dstField.GetNameRef(),
    1465             :                      OGRFieldDefn::GetFieldTypeName(dstField.GetType()));
    1466             : 
    1467           0 :             return OGRERR_FAILURE;
    1468             :         }
    1469             :     }
    1470             : 
    1471             :     CPLString clmClause =
    1472         195 :         QuotedIdentifier(dstField.GetNameRef()) + " " + columnDef;
    1473          65 :     if (!dstField.IsNullable())
    1474           0 :         clmClause += " NOT NULL";
    1475          65 :     if (dstField.GetDefault() != nullptr && !dstField.IsDefaultDriverSpecific())
    1476             :     {
    1477          12 :         if (IsArrayField(dstField.GetType()) ||
    1478          24 :             columnTypeInfo.type == QGRHanaDataTypes::LongVarBinary ||
    1479          12 :             columnTypeInfo.type == QGRHanaDataTypes::RealVector)
    1480             :         {
    1481           0 :             CPLError(
    1482             :                 CE_Failure, CPLE_NotSupported,
    1483             :                 "Default value cannot be created on column of data type %s: "
    1484             :                 "%s.",
    1485             :                 columnTypeInfo.name.c_str(), dstField.GetNameRef());
    1486             : 
    1487           0 :             return OGRERR_FAILURE;
    1488             :         }
    1489             : 
    1490             :         clmClause +=
    1491          12 :             CPLString().Printf(" DEFAULT %s", GetColumnDefaultValue(dstField));
    1492             :     }
    1493             : 
    1494          65 :     FlushPendingBatches(false);
    1495             : 
    1496          65 :     const CPLString sql = CPLString().Printf(
    1497             :         "ALTER TABLE %s ADD(%s)",
    1498         130 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1499         195 :         clmClause.c_str());
    1500             : 
    1501             :     try
    1502             :     {
    1503          66 :         dataSource_->ExecuteSQL(sql.c_str());
    1504             :     }
    1505           1 :     catch (const odbc::Exception &ex)
    1506             :     {
    1507           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1508             :                  "Failed to execute create attribute field %s: %s",
    1509           1 :                  dstField.GetNameRef(), ex.what());
    1510           1 :         return OGRERR_FAILURE;
    1511             :     }
    1512             : 
    1513             :     // columnTypeInfo might contain a different definition due to custom column
    1514             :     // types
    1515          64 :     SetFieldDefn(dstField, columnTypeInfo);
    1516             : 
    1517          64 :     AttributeColumnDescription clmDesc;
    1518          64 :     clmDesc.name = dstField.GetNameRef();
    1519          64 :     clmDesc.type = columnTypeInfo.type;
    1520          64 :     clmDesc.typeName = columnTypeInfo.name;
    1521          64 :     clmDesc.isArray = IsArrayField(dstField.GetType());
    1522          64 :     clmDesc.length = columnTypeInfo.width;
    1523          64 :     clmDesc.isNullable = dstField.IsNullable();
    1524          64 :     clmDesc.isAutoIncrement = false;  // TODO
    1525          64 :     clmDesc.scale = static_cast<unsigned short>(columnTypeInfo.precision);
    1526          64 :     clmDesc.precision = static_cast<unsigned short>(columnTypeInfo.width);
    1527          64 :     if (dstField.GetDefault() != nullptr)
    1528          12 :         clmDesc.defaultValue = dstField.GetDefault();
    1529             : 
    1530          64 :     attrColumns_.push_back(std::move(clmDesc));
    1531          64 :     featureDefn_->AddFieldDefn(&dstField);
    1532             : 
    1533          64 :     ColumnsChanged();
    1534             : 
    1535          64 :     return OGRERR_NONE;
    1536             : }
    1537             : 
    1538             : /************************************************************************/
    1539             : /*                          CreateGeomField()                           */
    1540             : /************************************************************************/
    1541             : 
    1542           5 : OGRErr OGRHanaTableLayer::CreateGeomField(const OGRGeomFieldDefn *geomField,
    1543             :                                           int)
    1544             : {
    1545           5 :     if (!updateMode_)
    1546             :     {
    1547           1 :         CPLError(CE_Failure, CPLE_AppDefined, UNSUPPORTED_OP_READ_ONLY,
    1548             :                  "CreateGeomField");
    1549           1 :         return OGRERR_FAILURE;
    1550             :     }
    1551             : 
    1552           4 :     if (!IsGeometryTypeSupported(geomField->GetType()))
    1553             :     {
    1554           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1555             :                  "Geometry field '%s' in layer '%s' has unsupported type %s",
    1556             :                  geomField->GetNameRef(), tableName_.c_str(),
    1557             :                  OGRGeometryTypeToName(geomField->GetType()));
    1558           1 :         return OGRERR_FAILURE;
    1559             :     }
    1560             : 
    1561           3 :     EnsureInitialized();
    1562             : 
    1563           3 :     if (featureDefn_->GetGeomFieldIndex(geomField->GetNameRef()) >= 0)
    1564             :     {
    1565           1 :         CPLError(
    1566             :             CE_Failure, CPLE_AppDefined,
    1567             :             "CreateGeomField() called with an already existing field name: %s",
    1568             :             geomField->GetNameRef());
    1569           1 :         return OGRERR_FAILURE;
    1570             :     }
    1571             : 
    1572           2 :     FlushPendingBatches(false);
    1573             : 
    1574           2 :     int srid = dataSource_->GetSrsId(geomField->GetSpatialRef());
    1575           2 :     if (srid == UNDETERMINED_SRID)
    1576             :     {
    1577           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1578             :                  "Unable to determine the srs-id for field name: %s",
    1579             :                  geomField->GetNameRef());
    1580           1 :         return OGRERR_FAILURE;
    1581             :     }
    1582             : 
    1583           2 :     CPLString clmName(geomField->GetNameRef());
    1584           1 :     if (launderColumnNames_)
    1585             :     {
    1586           1 :         auto nameRes = dataSource_->LaunderName(geomField->GetNameRef());
    1587           1 :         if (nameRes.first != OGRERR_NONE)
    1588           0 :             return nameRes.first;
    1589           1 :         clmName.swap(nameRes.second);
    1590             :     }
    1591             : 
    1592           1 :     if (clmName.empty())
    1593           1 :         clmName = FindGeomFieldName(*featureDefn_);
    1594             : 
    1595           1 :     CPLString sql = CPLString().Printf(
    1596             :         "ALTER TABLE %s ADD(%s ST_GEOMETRY(%d))",
    1597           2 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1598           4 :         QuotedIdentifier(clmName).c_str(), srid);
    1599             : 
    1600             :     try
    1601             :     {
    1602           1 :         dataSource_->ExecuteSQL(sql.c_str());
    1603             :     }
    1604           0 :     catch (const odbc::Exception &ex)
    1605             :     {
    1606           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1607             :                  "Failed to execute CreateGeomField() with field name '%s': %s",
    1608           0 :                  geomField->GetNameRef(), ex.what());
    1609           0 :         return OGRERR_FAILURE;
    1610             :     }
    1611             : 
    1612             :     auto newGeomField = std::make_unique<OGRGeomFieldDefn>(
    1613           1 :         clmName.c_str(), geomField->GetType());
    1614           1 :     newGeomField->SetNullable(geomField->IsNullable());
    1615           1 :     newGeomField->SetSpatialRef(geomField->GetSpatialRef());
    1616           2 :     geomColumns_.push_back({newGeomField->GetNameRef(), newGeomField->GetType(),
    1617           1 :                             srid, newGeomField->IsNullable() == TRUE});
    1618           1 :     featureDefn_->AddGeomFieldDefn(std::move(newGeomField));
    1619             : 
    1620           1 :     ColumnsChanged();
    1621             : 
    1622           1 :     return OGRERR_NONE;
    1623             : }
    1624             : 
    1625             : /************************************************************************/
    1626             : /*                            DeleteField()                             */
    1627             : /************************************************************************/
    1628             : 
    1629           0 : OGRErr OGRHanaTableLayer::DeleteField(int field)
    1630             : {
    1631           0 :     if (!updateMode_)
    1632             :     {
    1633           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1634             :                  "DeleteField");
    1635           0 :         return OGRERR_FAILURE;
    1636             :     }
    1637             : 
    1638           0 :     if (field < 0 || field >= featureDefn_->GetFieldCount())
    1639             :     {
    1640           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
    1641           0 :         return OGRERR_FAILURE;
    1642             :     }
    1643             : 
    1644           0 :     EnsureInitialized();
    1645             : 
    1646           0 :     FlushPendingBatches(false);
    1647             : 
    1648           0 :     CPLString clmName = featureDefn_->GetFieldDefn(field)->GetNameRef();
    1649           0 :     CPLString sql = CPLString().Printf(
    1650             :         "ALTER TABLE %s DROP (%s)",
    1651           0 :         GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1652           0 :         QuotedIdentifier(clmName).c_str());
    1653             : 
    1654             :     try
    1655             :     {
    1656           0 :         dataSource_->ExecuteSQL(sql.c_str());
    1657             :     }
    1658           0 :     catch (const odbc::Exception &ex)
    1659             :     {
    1660           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to drop column %s: %s",
    1661           0 :                  clmName.c_str(), ex.what());
    1662           0 :         return OGRERR_FAILURE;
    1663             :     }
    1664             : 
    1665             :     auto it = std::find_if(attrColumns_.begin(), attrColumns_.end(),
    1666           0 :                            [&](const AttributeColumnDescription &cd)
    1667           0 :                            { return cd.name == clmName; });
    1668           0 :     attrColumns_.erase(it);
    1669           0 :     OGRErr ret = featureDefn_->DeleteFieldDefn(field);
    1670             : 
    1671           0 :     ColumnsChanged();
    1672             : 
    1673           0 :     return ret;
    1674             : }
    1675             : 
    1676             : /************************************************************************/
    1677             : /*                            AlterFieldDefn()                          */
    1678             : /************************************************************************/
    1679             : 
    1680           0 : OGRErr OGRHanaTableLayer::AlterFieldDefn(int field, OGRFieldDefn *newFieldDefn,
    1681             :                                          int flagsIn)
    1682             : {
    1683           0 :     if (!updateMode_)
    1684             :     {
    1685           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1686             :                  "AlterFieldDefn");
    1687           0 :         return OGRERR_FAILURE;
    1688             :     }
    1689             : 
    1690           0 :     EnsureInitialized();
    1691             : 
    1692           0 :     if (field < 0 || field >= featureDefn_->GetFieldCount())
    1693             :     {
    1694           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Field index is out of range");
    1695           0 :         return OGRERR_FAILURE;
    1696             :     }
    1697             : 
    1698           0 :     OGRFieldDefn *fieldDefn = featureDefn_->GetFieldDefn(field);
    1699             : 
    1700           0 :     int columnDescIdx = -1;
    1701           0 :     for (size_t i = 0; i < attrColumns_.size(); ++i)
    1702             :     {
    1703           0 :         if (EQUAL(attrColumns_[i].name.c_str(), fieldDefn->GetNameRef()))
    1704             :         {
    1705           0 :             columnDescIdx = static_cast<int>(i);
    1706           0 :             break;
    1707             :         }
    1708             :     }
    1709             : 
    1710           0 :     if (columnDescIdx < 0)
    1711             :     {
    1712           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1713             :                  "Column description cannot be found");
    1714           0 :         return OGRERR_FAILURE;
    1715             :     }
    1716             : 
    1717           0 :     CPLString clmName(newFieldDefn->GetNameRef());
    1718             : 
    1719           0 :     if (launderColumnNames_)
    1720             :     {
    1721           0 :         auto nameRes = dataSource_->LaunderName(newFieldDefn->GetNameRef());
    1722           0 :         if (nameRes.first != OGRERR_NONE)
    1723           0 :             return nameRes.first;
    1724           0 :         clmName.swap(nameRes.second);
    1725             :     }
    1726             : 
    1727           0 :     FlushPendingBatches(false);
    1728             : 
    1729           0 :     ColumnTypeInfo columnTypeInfo = GetColumnTypeInfo(*newFieldDefn);
    1730             : 
    1731             :     try
    1732             :     {
    1733           0 :         if ((flagsIn & ALTER_NAME_FLAG) &&
    1734           0 :             (strcmp(fieldDefn->GetNameRef(), newFieldDefn->GetNameRef()) != 0))
    1735             :         {
    1736           0 :             CPLString sql = CPLString().Printf(
    1737             :                 "RENAME COLUMN %s TO %s",
    1738           0 :                 GetFullColumnNameQuoted(schemaName_, tableName_,
    1739             :                                         fieldDefn->GetNameRef())
    1740             :                     .c_str(),
    1741           0 :                 QuotedIdentifier(clmName).c_str());
    1742           0 :             dataSource_->ExecuteSQL(sql.c_str());
    1743             :         }
    1744             : 
    1745           0 :         if ((flagsIn & ALTER_TYPE_FLAG) ||
    1746           0 :             (flagsIn & ALTER_WIDTH_PRECISION_FLAG) ||
    1747           0 :             (flagsIn & ALTER_NULLABLE_FLAG) || (flagsIn & ALTER_DEFAULT_FLAG))
    1748             :         {
    1749           0 :             CPLString fieldTypeDef = GetColumnDefinition(columnTypeInfo);
    1750           0 :             if ((flagsIn & ALTER_NULLABLE_FLAG) &&
    1751           0 :                 fieldDefn->IsNullable() != newFieldDefn->IsNullable())
    1752             :             {
    1753           0 :                 if (fieldDefn->IsNullable())
    1754           0 :                     fieldTypeDef += " NULL";
    1755             :                 else
    1756           0 :                     fieldTypeDef += " NOT NULL";
    1757             :             }
    1758             : 
    1759           0 :             if ((flagsIn & ALTER_DEFAULT_FLAG) &&
    1760           0 :                 ((fieldDefn->GetDefault() == nullptr &&
    1761           0 :                   newFieldDefn->GetDefault() != nullptr) ||
    1762           0 :                  (fieldDefn->GetDefault() != nullptr &&
    1763           0 :                   newFieldDefn->GetDefault() == nullptr) ||
    1764           0 :                  (fieldDefn->GetDefault() != nullptr &&
    1765           0 :                   newFieldDefn->GetDefault() != nullptr &&
    1766           0 :                   strcmp(fieldDefn->GetDefault(), newFieldDefn->GetDefault()) !=
    1767             :                       0)))
    1768             :             {
    1769             :                 fieldTypeDef +=
    1770           0 :                     " DEFAULT " + ((fieldDefn->GetType() == OFTString)
    1771           0 :                                        ? Literal(newFieldDefn->GetDefault())
    1772           0 :                                        : CPLString(newFieldDefn->GetDefault()));
    1773             :             }
    1774             : 
    1775           0 :             CPLString sql = CPLString().Printf(
    1776             :                 "ALTER TABLE %s ALTER(%s %s)",
    1777           0 :                 GetFullTableNameQuoted(schemaName_, tableName_).c_str(),
    1778           0 :                 QuotedIdentifier(clmName).c_str(), fieldTypeDef.c_str());
    1779             : 
    1780           0 :             dataSource_->ExecuteSQL(sql.c_str());
    1781             :         }
    1782             :     }
    1783           0 :     catch (const odbc::Exception &ex)
    1784             :     {
    1785           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to alter field %s: %s",
    1786           0 :                  fieldDefn->GetNameRef(), ex.what());
    1787           0 :         return OGRERR_FAILURE;
    1788             :     }
    1789             : 
    1790             :     // Update field definition and column description
    1791             : 
    1792           0 :     AttributeColumnDescription &attrClmDesc = attrColumns_[columnDescIdx];
    1793             : 
    1794           0 :     if (flagsIn & ALTER_NAME_FLAG)
    1795             :     {
    1796           0 :         fieldDefn->SetName(clmName.c_str());
    1797           0 :         attrClmDesc.name.assign(clmName.c_str());
    1798             :     }
    1799             : 
    1800           0 :     if (flagsIn & ALTER_TYPE_FLAG)
    1801             :     {
    1802           0 :         fieldDefn->SetSubType(OFSTNone);
    1803           0 :         fieldDefn->SetType(newFieldDefn->GetType());
    1804           0 :         fieldDefn->SetSubType(newFieldDefn->GetSubType());
    1805           0 :         attrClmDesc.isArray = IsArrayField(newFieldDefn->GetType());
    1806           0 :         attrClmDesc.type = columnTypeInfo.type;
    1807           0 :         attrClmDesc.typeName = columnTypeInfo.name;
    1808             :     }
    1809             : 
    1810           0 :     if (flagsIn & ALTER_WIDTH_PRECISION_FLAG)
    1811             :     {
    1812           0 :         fieldDefn->SetWidth(newFieldDefn->GetWidth());
    1813           0 :         fieldDefn->SetPrecision(newFieldDefn->GetPrecision());
    1814           0 :         attrClmDesc.length = newFieldDefn->GetWidth();
    1815           0 :         attrClmDesc.scale = newFieldDefn->GetWidth();
    1816           0 :         attrClmDesc.precision = newFieldDefn->GetPrecision();
    1817             :     }
    1818             : 
    1819           0 :     if (flagsIn & ALTER_NULLABLE_FLAG)
    1820             :     {
    1821           0 :         fieldDefn->SetNullable(newFieldDefn->IsNullable());
    1822           0 :         attrClmDesc.isNullable = newFieldDefn->IsNullable();
    1823             :     }
    1824             : 
    1825           0 :     if (flagsIn & ALTER_DEFAULT_FLAG)
    1826             :     {
    1827           0 :         fieldDefn->SetDefault(newFieldDefn->GetDefault());
    1828           0 :         attrClmDesc.name.assign(newFieldDefn->GetDefault());
    1829             :     }
    1830             : 
    1831           0 :     ColumnsChanged();
    1832             : 
    1833           0 :     return OGRERR_NONE;
    1834             : }
    1835             : 
    1836             : /************************************************************************/
    1837             : /*                          ClearBatches()                              */
    1838             : /************************************************************************/
    1839             : 
    1840           4 : void OGRHanaTableLayer::ClearBatches()
    1841             : {
    1842           4 :     if (!insertFeatureStmtWithFID_.isNull())
    1843           4 :         insertFeatureStmtWithFID_->clearBatch();
    1844           4 :     if (!insertFeatureStmtWithoutFID_.isNull())
    1845           1 :         insertFeatureStmtWithoutFID_->clearBatch();
    1846           4 :     if (!updateFeatureStmt_.isNull())
    1847           2 :         updateFeatureStmt_->clearBatch();
    1848           4 : }
    1849             : 
    1850             : /************************************************************************/
    1851             : /*                          ColumnsChanged()                            */
    1852             : /************************************************************************/
    1853             : 
    1854          65 : void OGRHanaTableLayer::ColumnsChanged()
    1855             : {
    1856          65 :     ClearQueryStatement();
    1857          65 :     ResetReading();
    1858          65 :     ResetPreparedStatements();
    1859          65 : }
    1860             : 
    1861             : /************************************************************************/
    1862             : /*                          SetCustomColumnTypes()                      */
    1863             : /************************************************************************/
    1864             : 
    1865          18 : void OGRHanaTableLayer::SetCustomColumnTypes(const char *columnTypes)
    1866             : {
    1867          18 :     if (columnTypes == nullptr)
    1868          17 :         return;
    1869             : 
    1870           1 :     const char *ptr = columnTypes;
    1871           1 :     const char *start = ptr;
    1872          42 :     while (*ptr != '\0')
    1873             :     {
    1874          41 :         if (*ptr == '(')
    1875             :         {
    1876             :             // Skip commas inside brackets, for example decimal(20,5)
    1877          12 :             while (*ptr != '\0' && *ptr != ')')
    1878             :             {
    1879          10 :                 ++ptr;
    1880             :             }
    1881             :         }
    1882             : 
    1883          41 :         ++ptr;
    1884             : 
    1885          41 :         if (*ptr == ',' || *ptr == '\0')
    1886             :         {
    1887           3 :             std::size_t len = static_cast<std::size_t>(ptr - start);
    1888           3 :             const char *sep = std::find(start, start + len, '=');
    1889           3 :             if (sep != nullptr)
    1890             :             {
    1891           3 :                 std::size_t pos = static_cast<std::size_t>(sep - start);
    1892           6 :                 customColumnDefs_.push_back(
    1893             :                     {CPLString(start, pos),
    1894           3 :                      CPLString(start + pos + 1, len - pos - 1)});
    1895             :             }
    1896             : 
    1897           3 :             start = ptr + 1;
    1898             :         }
    1899             :     }
    1900             : }
    1901             : 
    1902             : /************************************************************************/
    1903             : /*                          StartTransaction()                          */
    1904             : /************************************************************************/
    1905             : 
    1906          11 : OGRErr OGRHanaTableLayer::StartTransaction()
    1907             : {
    1908          11 :     return dataSource_->StartTransaction();
    1909             : }
    1910             : 
    1911             : /************************************************************************/
    1912             : /*                          CommitTransaction()                         */
    1913             : /************************************************************************/
    1914             : 
    1915           7 : OGRErr OGRHanaTableLayer::CommitTransaction()
    1916             : {
    1917           7 :     if (HasPendingBatches())
    1918             :     {
    1919           0 :         OGRErr err = ExecutePendingBatches(BatchOperation::ALL);
    1920           0 :         if (OGRERR_NONE != err)
    1921           0 :             return err;
    1922             :     }
    1923             : 
    1924           7 :     return dataSource_->CommitTransaction();
    1925             : }
    1926             : 
    1927             : /************************************************************************/
    1928             : /*                          RollbackTransaction()                       */
    1929             : /************************************************************************/
    1930             : 
    1931           4 : OGRErr OGRHanaTableLayer::RollbackTransaction()
    1932             : {
    1933           4 :     ClearBatches();
    1934           4 :     return dataSource_->RollbackTransaction();
    1935             : }
    1936             : 
    1937             : }  // namespace OGRHANA

Generated by: LCOV version 1.14