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

Generated by: LCOV version 1.14