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

Generated by: LCOV version 1.14