LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/hana - ogrhanalayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 454 535 84.9 %
Date: 2024-11-21 22:18:42 Functions: 28 30 93.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  SAP HANA Spatial Driver
       4             :  * Purpose:  OGRHanaLayer class implementation
       5             :  * Author:   Maxim Rylov
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2020, SAP SE
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_hana.h"
      14             : #include "ogrhanafeaturewriter.h"
      15             : #include "ogrhanautils.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <cmath>
      19             : #include <limits>
      20             : #include <sstream>
      21             : #include <memory>
      22             : 
      23             : #include "odbc/Exception.h"
      24             : #include "odbc/PreparedStatement.h"
      25             : #include "odbc/ResultSet.h"
      26             : #include "odbc/Statement.h"
      27             : #include "odbc/Types.h"
      28             : 
      29             : namespace OGRHANA
      30             : {
      31             : namespace
      32             : {
      33             : /************************************************************************/
      34             : /*                          Helper methods                              */
      35             : /************************************************************************/
      36             : 
      37          12 : CPLString BuildQuery(const char *source, const char *columns, const char *where,
      38             :                      const char *orderBy, int limit)
      39             : {
      40          12 :     std::ostringstream os;
      41          12 :     os << "SELECT " << columns << " FROM (" << source << ")";
      42          12 :     if (where != nullptr && strlen(where) > 0)
      43           0 :         os << " WHERE " << where;
      44          12 :     if (orderBy != nullptr && strlen(orderBy) > 0)
      45           0 :         os << " ORDER BY " << orderBy;
      46          12 :     if (limit >= 0)
      47           0 :         os << " LIMIT " << std::to_string(limit);
      48          36 :     return os.str();
      49             : }
      50             : 
      51          12 : CPLString BuildQuery(const char *source, const char *columns)
      52             : {
      53          12 :     return BuildQuery(source, columns, nullptr, nullptr, -1);
      54             : }
      55             : 
      56          24 : CPLString BuildSpatialFilter(int dbVersion, const OGRGeometry &geom,
      57             :                              const CPLString &clmName, int srid)
      58             : {
      59          24 :     OGREnvelope env;
      60          24 :     geom.getEnvelope(&env);
      61             : 
      62          46 :     if ((std::isinf(env.MinX) || std::isinf(env.MinY) || std::isinf(env.MaxX) ||
      63          22 :          std::isinf(env.MaxY)))
      64           2 :         return "";
      65             : 
      66          88 :     auto clampValue = [](double v)
      67             :     {
      68          88 :         constexpr double MAX_VALUE = 1e+150;
      69          88 :         if (v < -MAX_VALUE)
      70           4 :             return -MAX_VALUE;
      71          84 :         else if (v > MAX_VALUE)
      72           4 :             return MAX_VALUE;
      73          80 :         return v;
      74             :     };
      75             : 
      76          22 :     double minX = clampValue(env.MinX);
      77          22 :     double minY = clampValue(env.MinY);
      78          22 :     double maxX = clampValue(env.MaxX);
      79          22 :     double maxY = clampValue(env.MaxY);
      80             : 
      81             :     // TODO: add support for non-rectangular filter, see m_bFilterIsEnvelope
      82             :     // flag.
      83          22 :     if (dbVersion == 1)
      84           0 :         return CPLString().Printf(
      85             :             "\"%s\".ST_IntersectsRect(ST_GeomFromText('POINT(%.17g %.17g)', "
      86             :             "%d), ST_GeomFromText('POINT(%.17g %.17g)', %d)) = 1",
      87           0 :             clmName.c_str(), minX, minY, srid, maxX, maxY, srid);
      88             :     else
      89          44 :         return CPLString().Printf(
      90             :             "\"%s\".ST_IntersectsRectPlanar(ST_GeomFromText('POINT(%.17g "
      91             :             "%.17g)', %d), ST_GeomFromText('POINT(%.17g %.17g)', %d)) = 1",
      92          22 :             clmName.c_str(), minX, minY, srid, maxX, maxY, srid);
      93             : }
      94             : 
      95             : std::unique_ptr<OGRFieldDefn>
      96         166 : CreateFieldDefn(const AttributeColumnDescription &columnDesc)
      97             : {
      98         166 :     bool setFieldSize = false;
      99         166 :     bool setFieldPrecision = false;
     100             : 
     101         166 :     OGRFieldType ogrFieldType = OFTString;
     102         166 :     OGRFieldSubType ogrFieldSubType = OGRFieldSubType::OFSTNone;
     103         166 :     int width = columnDesc.length;
     104             : 
     105         166 :     switch (columnDesc.type)
     106             :     {
     107           2 :         case QGRHanaDataTypes::Bit:
     108             :         case QGRHanaDataTypes::Boolean:
     109           2 :             ogrFieldType = columnDesc.isArray ? OFTIntegerList : OFTInteger;
     110           2 :             ogrFieldSubType = OGRFieldSubType::OFSTBoolean;
     111           2 :             break;
     112           3 :         case QGRHanaDataTypes::TinyInt:
     113             :         case QGRHanaDataTypes::SmallInt:
     114           3 :             ogrFieldType = columnDesc.isArray ? OFTIntegerList : OFTInteger;
     115           3 :             ogrFieldSubType = OGRFieldSubType::OFSTInt16;
     116           3 :             break;
     117          55 :         case QGRHanaDataTypes::Integer:
     118          55 :             ogrFieldType = columnDesc.isArray ? OFTIntegerList : OFTInteger;
     119          55 :             break;
     120          26 :         case QGRHanaDataTypes::BigInt:
     121          26 :             ogrFieldType = columnDesc.isArray ? OFTInteger64List : OFTInteger64;
     122          26 :             break;
     123          23 :         case QGRHanaDataTypes::Double:
     124             :         case QGRHanaDataTypes::Real:
     125             :         case QGRHanaDataTypes::Float:
     126          23 :             ogrFieldType = columnDesc.isArray ? OFTRealList : OFTReal;
     127          23 :             if (columnDesc.type != QGRHanaDataTypes::Double)
     128           1 :                 ogrFieldSubType = OGRFieldSubType::OFSTFloat32;
     129          23 :             break;
     130           3 :         case QGRHanaDataTypes::Decimal:
     131             :         case QGRHanaDataTypes::Numeric:
     132           3 :             ogrFieldType = columnDesc.isArray ? OFTRealList : OFTReal;
     133           3 :             setFieldPrecision = true;
     134           3 :             break;
     135          43 :         case QGRHanaDataTypes::Char:
     136             :         case QGRHanaDataTypes::VarChar:
     137             :         case QGRHanaDataTypes::LongVarChar:
     138             :         case QGRHanaDataTypes::WChar:
     139             :         case QGRHanaDataTypes::WVarChar:
     140             :         case QGRHanaDataTypes::WLongVarChar:
     141             :             // Note: OFTWideString is deprecated
     142          43 :             ogrFieldType = columnDesc.isArray ? OFTStringList : OFTString;
     143          43 :             setFieldSize = true;
     144          43 :             break;
     145           1 :         case QGRHanaDataTypes::Date:
     146             :         case QGRHanaDataTypes::TypeDate:
     147           1 :             ogrFieldType = OFTDate;
     148           1 :             break;
     149           1 :         case QGRHanaDataTypes::Time:
     150             :         case QGRHanaDataTypes::TypeTime:
     151           1 :             ogrFieldType = OFTTime;
     152           1 :             break;
     153           2 :         case QGRHanaDataTypes::Timestamp:
     154             :         case QGRHanaDataTypes::TypeTimestamp:
     155           2 :             ogrFieldType = OFTDateTime;
     156           2 :             break;
     157           1 :         case QGRHanaDataTypes::Binary:
     158             :         case QGRHanaDataTypes::VarBinary:
     159             :         case QGRHanaDataTypes::LongVarBinary:
     160           1 :             ogrFieldType = OFTBinary;
     161           1 :             setFieldSize = true;
     162           1 :             break;
     163           6 :         case QGRHanaDataTypes::RealVector:
     164           6 :             ogrFieldType = OFTBinary;
     165             :             // The .fvecs format is used for REAL_VECTOR with the dimension
     166             :             // lying in the range [1,65000].
     167          12 :             width = (columnDesc.precision == 0)
     168           6 :                         ? 65000
     169           3 :                         : 4 * (columnDesc.precision + 1);
     170           6 :             setFieldSize = true;
     171           6 :             break;
     172           0 :         default:
     173           0 :             break;
     174             :     }
     175             : 
     176         166 :     if (columnDesc.isArray && !IsArrayField(ogrFieldType))
     177           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     178             :                  "Array of type %s in column %s is not supported",
     179             :                  columnDesc.typeName.c_str(), columnDesc.name.c_str());
     180             : 
     181             :     auto field =
     182         166 :         std::make_unique<OGRFieldDefn>(columnDesc.name.c_str(), ogrFieldType);
     183         166 :     field->SetSubType(ogrFieldSubType);
     184         166 :     field->SetNullable(columnDesc.isNullable);
     185         166 :     if (!columnDesc.isArray)
     186             :     {
     187         155 :         if (setFieldSize)
     188          47 :             field->SetWidth(width);
     189         155 :         if (setFieldPrecision)
     190             :         {
     191           3 :             field->SetWidth(columnDesc.precision);
     192           3 :             field->SetPrecision(columnDesc.scale);
     193             :         }
     194             :     }
     195         166 :     if (columnDesc.defaultValue.empty())
     196         154 :         field->SetDefault(nullptr);
     197             :     else
     198          12 :         field->SetDefault(columnDesc.defaultValue.c_str());
     199         332 :     return field;
     200             : }
     201             : 
     202         568 : OGRGeometry *CreateGeometryFromWkb(const void *data, std::size_t size)
     203             : {
     204         568 :     if (size > static_cast<std::size_t>(std::numeric_limits<int>::max()))
     205           0 :         CPLError(CE_Failure, CPLE_AppDefined, "createFromWkb(): %s",
     206             :                  "Geometry size is larger than maximum integer value");
     207             : 
     208         568 :     int len = static_cast<int>(size);
     209             : 
     210         568 :     OGRGeometry *geom = nullptr;
     211         568 :     OGRErr err = OGRGeometryFactory::createFromWkb(data, nullptr, &geom, len);
     212             : 
     213         568 :     if (OGRERR_NONE == err)
     214         568 :         return geom;
     215             : 
     216           0 :     auto cplError = [](const char *message)
     217           0 :     { CPLError(CE_Failure, CPLE_AppDefined, "ReadFeature(): %s", message); };
     218             : 
     219           0 :     switch (err)
     220             :     {
     221           0 :         case OGRERR_NOT_ENOUGH_DATA:
     222           0 :             cplError("Not enough data to deserialize");
     223           0 :             return nullptr;
     224           0 :         case OGRERR_UNSUPPORTED_GEOMETRY_TYPE:
     225           0 :             cplError("Unsupported geometry type");
     226           0 :             return nullptr;
     227           0 :         case OGRERR_CORRUPT_DATA:
     228           0 :             cplError("Corrupt data");
     229           0 :             return nullptr;
     230           0 :         default:
     231           0 :             cplError("Unrecognized error");
     232           0 :             return nullptr;
     233             :     }
     234             : }
     235             : 
     236             : }  // anonymous namespace
     237             : 
     238             : /************************************************************************/
     239             : /*                           OGRHanaLayer()                             */
     240             : /************************************************************************/
     241             : 
     242         498 : OGRHanaLayer::OGRHanaLayer(OGRHanaDataSource *datasource)
     243             :     : dataSource_(datasource), rawQuery_(""), queryStatement_(""),
     244         498 :       whereClause_("")
     245             : {
     246         498 : }
     247             : 
     248             : /************************************************************************/
     249             : /*                          ~OGRHanaLayer()                             */
     250             : /************************************************************************/
     251             : 
     252         495 : OGRHanaLayer::~OGRHanaLayer()
     253             : {
     254         495 :     if (featureDefn_)
     255          52 :         featureDefn_->Release();
     256         495 : }
     257             : 
     258        1682 : void OGRHanaLayer::EnsureInitialized()
     259             : {
     260        1682 :     if (initialized_)
     261        1627 :         return;
     262             : 
     263          55 :     OGRErr err = Initialize();
     264          55 :     if (OGRERR_NONE != err)
     265             :     {
     266           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Failed to initialize layer: %s",
     267           0 :                  GetName());
     268             :     }
     269          55 :     initialized_ = (OGRERR_NONE == err);
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                         ClearQueryStatement()                        */
     274             : /************************************************************************/
     275             : 
     276         204 : void OGRHanaLayer::ClearQueryStatement()
     277             : {
     278         204 :     queryStatement_.clear();
     279         204 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                          GetQueryStatement()                         */
     283             : /************************************************************************/
     284             : 
     285         182 : const CPLString &OGRHanaLayer::GetQueryStatement()
     286             : {
     287         182 :     if (!queryStatement_.empty())
     288          59 :         return queryStatement_;
     289             : 
     290         123 :     EnsureInitialized();
     291             : 
     292         123 :     if (!geomColumns_.empty())
     293             :     {
     294         108 :         std::vector<CPLString> columns;
     295         216 :         for (const GeometryColumnDescription &geometryColumnDesc : geomColumns_)
     296         324 :             columns.push_back(QuotedIdentifier(geometryColumnDesc.name) +
     297         216 :                               ".ST_AsBinary() AS " +
     298         216 :                               QuotedIdentifier(geometryColumnDesc.name));
     299             : 
     300         550 :         for (const AttributeColumnDescription &attributeColumnDesc :
     301         658 :              attrColumns_)
     302         550 :             columns.push_back(QuotedIdentifier(attributeColumnDesc.name));
     303             : 
     304         216 :         queryStatement_ = CPLString().Printf(
     305         216 :             "SELECT %s FROM (%s) %s", JoinStrings(columns, ", ").c_str(),
     306         216 :             rawQuery_.c_str(), whereClause_.c_str());
     307             :     }
     308             :     else
     309             :     {
     310          15 :         if (whereClause_.empty())
     311          15 :             queryStatement_ = rawQuery_;
     312             :         else
     313             :             queryStatement_ =
     314           0 :                 CPLString().Printf("SELECT * FROM (%s) %s", rawQuery_.c_str(),
     315           0 :                                    whereClause_.c_str());
     316             :     }
     317             : 
     318         123 :     return queryStatement_;
     319             : }
     320             : 
     321             : /************************************************************************/
     322             : /*                         BuildWhereClause()                           */
     323             : /************************************************************************/
     324             : 
     325         139 : void OGRHanaLayer::BuildWhereClause()
     326             : {
     327         139 :     whereClause_ = "";
     328             : 
     329         278 :     CPLString spatialFilter;
     330         139 :     if (m_poFilterGeom != nullptr)
     331             :     {
     332          24 :         EnsureInitialized();
     333             : 
     334          24 :         OGRGeomFieldDefn *geomFieldDefn = nullptr;
     335          24 :         if (featureDefn_->GetGeomFieldCount() != 0)
     336          24 :             geomFieldDefn = featureDefn_->GetGeomFieldDefn(m_iGeomFieldFilter);
     337             : 
     338          24 :         if (geomFieldDefn != nullptr)
     339             :         {
     340             :             const GeometryColumnDescription &geomClmDesc =
     341          24 :                 geomColumns_[static_cast<std::size_t>(m_iGeomFieldFilter)];
     342          48 :             spatialFilter = BuildSpatialFilter(
     343          48 :                 dataSource_->GetHanaVersion().major(), *m_poFilterGeom,
     344          48 :                 geomClmDesc.name, geomClmDesc.srid);
     345             :         }
     346             :     }
     347             : 
     348         139 :     if (!attrFilter_.empty())
     349             :     {
     350          32 :         whereClause_ = " WHERE " + attrFilter_;
     351          32 :         if (!spatialFilter.empty())
     352           4 :             whereClause_ += " AND " + spatialFilter;
     353             :     }
     354         107 :     else if (!spatialFilter.empty())
     355          18 :         whereClause_ = " WHERE " + spatialFilter;
     356         139 : }
     357             : 
     358             : /************************************************************************/
     359             : /*                       EnsureBufferCapacity()                         */
     360             : /************************************************************************/
     361             : 
     362        1273 : void OGRHanaLayer::EnsureBufferCapacity(std::size_t size)
     363             : {
     364        1273 :     if (size > dataBuffer_.size())
     365          67 :         dataBuffer_.resize(size);
     366        1273 : }
     367             : 
     368             : /************************************************************************/
     369             : /*                         GetNextFeatureInternal()                     */
     370             : /************************************************************************/
     371             : 
     372         655 : OGRFeature *OGRHanaLayer::GetNextFeatureInternal()
     373             : {
     374         655 :     if (resultSet_.isNull())
     375             :     {
     376         127 :         const CPLString &queryStatement = GetQueryStatement();
     377         127 :         CPLAssert(!queryStatement.empty());
     378             : 
     379             :         try
     380             :         {
     381         127 :             odbc::StatementRef stmt = dataSource_->CreateStatement();
     382         127 :             resultSet_ = stmt->executeQuery(queryStatement.c_str());
     383             :         }
     384           0 :         catch (const odbc::Exception &ex)
     385             :         {
     386           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     387           0 :                      "Failed to execute query : %s", ex.what());
     388           0 :             return nullptr;
     389             :         }
     390             :     }
     391             : 
     392         655 :     return ReadFeature();
     393             : }
     394             : 
     395             : /************************************************************************/
     396             : /*                         GetGeometryColumnSrid()                      */
     397             : /************************************************************************/
     398             : 
     399          12 : int OGRHanaLayer::GetGeometryColumnSrid(int columnIndex) const
     400             : {
     401          24 :     if (columnIndex < 0 ||
     402          12 :         static_cast<std::size_t>(columnIndex) >= geomColumns_.size())
     403           0 :         return -1;
     404          12 :     return geomColumns_[static_cast<std::size_t>(columnIndex)].srid;
     405             : }
     406             : 
     407             : /************************************************************************/
     408             : /*                          ReadFeature()                               */
     409             : /************************************************************************/
     410             : 
     411         655 : OGRFeature *OGRHanaLayer::ReadFeature()
     412             : {
     413         655 :     if (!resultSet_->next())
     414          54 :         return nullptr;
     415             : 
     416        1202 :     auto feature = std::make_unique<OGRFeature>(featureDefn_);
     417         601 :     feature->SetFID(nextFeatureId_++);
     418             : 
     419         601 :     unsigned short paramIndex = 0;
     420             : 
     421             :     // Read geometries
     422        1174 :     for (std::size_t i = 0; i < geomColumns_.size(); ++i)
     423             :     {
     424         573 :         ++paramIndex;
     425         573 :         int geomIndex = static_cast<int>(i);
     426             : 
     427             :         OGRGeomFieldDefn *geomFieldDef =
     428         573 :             featureDefn_->GetGeomFieldDefn(geomIndex);
     429             : 
     430         573 :         if (geomFieldDef->IsIgnored())
     431           0 :             continue;
     432             : 
     433         573 :         std::size_t bufLength = resultSet_->getBinaryLength(paramIndex);
     434         573 :         if (bufLength == 0 || bufLength == odbc::ResultSet::NULL_DATA)
     435             :         {
     436           5 :             feature->SetGeomFieldDirectly(geomIndex, nullptr);
     437           5 :             continue;
     438             :         }
     439             : 
     440         568 :         OGRGeometry *geom = nullptr;
     441         568 :         if (bufLength != odbc::ResultSet::UNKNOWN_LENGTH)
     442             :         {
     443         568 :             EnsureBufferCapacity(bufLength);
     444         568 :             resultSet_->getBinaryData(paramIndex, dataBuffer_.data(),
     445             :                                       bufLength);
     446             :             geom =
     447         568 :                 CreateGeometryFromWkb(dataBuffer_.data(), dataBuffer_.size());
     448             :         }
     449             :         else
     450             :         {
     451           0 :             odbc::Binary wkb = resultSet_->getBinary(paramIndex);
     452           0 :             if (!wkb.isNull() && wkb->size() > 0)
     453           0 :                 geom = CreateGeometryFromWkb(
     454           0 :                     static_cast<const void *>(wkb->data()), wkb->size());
     455             :         }
     456             : 
     457         568 :         if (geom != nullptr)
     458         568 :             geom->assignSpatialReference(geomFieldDef->GetSpatialRef());
     459         568 :         feature->SetGeomFieldDirectly(geomIndex, geom);
     460             :     }
     461             : 
     462             :     // Read feature attributes
     463         601 :     OGRHanaFeatureWriter featWriter(*feature);
     464         601 :     int fieldIndex = -1;
     465        3524 :     for (const AttributeColumnDescription &clmDesc : attrColumns_)
     466             :     {
     467        2923 :         ++paramIndex;
     468             : 
     469        2923 :         if (clmDesc.isFeatureID)
     470             :         {
     471         586 :             if (clmDesc.type == QGRHanaDataTypes::Integer)
     472             :             {
     473         586 :                 odbc::Int val = resultSet_->getInt(paramIndex);
     474         586 :                 if (!val.isNull())
     475         586 :                     feature->SetFID(static_cast<GIntBig>(*val));
     476             :             }
     477           0 :             else if (clmDesc.type == QGRHanaDataTypes::BigInt)
     478             :             {
     479           0 :                 odbc::Long val = resultSet_->getLong(paramIndex);
     480           0 :                 if (!val.isNull())
     481           0 :                     feature->SetFID(static_cast<GIntBig>(*val));
     482             :             }
     483         586 :             continue;
     484             :         }
     485             : 
     486        2337 :         ++fieldIndex;
     487             : 
     488        2337 :         OGRFieldDefn *fieldDefn = featureDefn_->GetFieldDefn(fieldIndex);
     489        2337 :         if (fieldDefn->IsIgnored())
     490           0 :             continue;
     491             : 
     492        2337 :         if (clmDesc.isArray)
     493             :         {
     494          22 :             odbc::Binary val = resultSet_->getBinary(paramIndex);
     495          11 :             if (val.isNull())
     496             :             {
     497           0 :                 feature->SetFieldNull(fieldIndex);
     498           0 :                 continue;
     499             :             }
     500             : 
     501          11 :             switch (clmDesc.type)
     502             :             {
     503           1 :                 case QGRHanaDataTypes::Boolean:
     504           1 :                     featWriter.SetFieldValueAsArray<uint8_t, int32_t>(
     505             :                         fieldIndex, val);
     506           1 :                     break;
     507           0 :                 case QGRHanaDataTypes::TinyInt:
     508           0 :                     featWriter.SetFieldValueAsArray<uint8_t, int32_t>(
     509             :                         fieldIndex, val);
     510           0 :                     break;
     511           1 :                 case QGRHanaDataTypes::SmallInt:
     512           1 :                     featWriter.SetFieldValueAsArray<int16_t, int32_t>(
     513             :                         fieldIndex, val);
     514           1 :                     break;
     515           2 :                 case QGRHanaDataTypes::Integer:
     516           2 :                     featWriter.SetFieldValueAsArray<int32_t, int32_t>(
     517             :                         fieldIndex, val);
     518           2 :                     break;
     519           2 :                 case QGRHanaDataTypes::BigInt:
     520           2 :                     featWriter.SetFieldValueAsArray<GIntBig, GIntBig>(
     521             :                         fieldIndex, val);
     522           2 :                     break;
     523           0 :                 case QGRHanaDataTypes::Float:
     524             :                 case QGRHanaDataTypes::Real:
     525           0 :                     featWriter.SetFieldValueAsArray<float, double>(fieldIndex,
     526             :                                                                    val);
     527           0 :                     break;
     528           2 :                 case QGRHanaDataTypes::Double:
     529           2 :                     featWriter.SetFieldValueAsArray<double, double>(fieldIndex,
     530             :                                                                     val);
     531           2 :                     break;
     532           3 :                 case QGRHanaDataTypes::Char:
     533             :                 case QGRHanaDataTypes::VarChar:
     534             :                 case QGRHanaDataTypes::LongVarChar:
     535             :                 case QGRHanaDataTypes::WChar:
     536             :                 case QGRHanaDataTypes::WVarChar:
     537             :                 case QGRHanaDataTypes::WLongVarChar:
     538           3 :                     featWriter.SetFieldValueAsStringArray(fieldIndex, val);
     539           3 :                     break;
     540             :             }
     541             : 
     542          11 :             continue;
     543             :         }
     544             : 
     545        2326 :         switch (clmDesc.type)
     546             :         {
     547           1 :             case QGRHanaDataTypes::Bit:
     548             :             case QGRHanaDataTypes::Boolean:
     549             :             {
     550           1 :                 odbc::Boolean val = resultSet_->getBoolean(paramIndex);
     551           1 :                 featWriter.SetFieldValue(fieldIndex, val);
     552             :             }
     553           1 :             break;
     554           0 :             case QGRHanaDataTypes::TinyInt:
     555             :             {
     556           0 :                 odbc::Byte val = resultSet_->getByte(paramIndex);
     557           0 :                 featWriter.SetFieldValue(fieldIndex, val);
     558             :             }
     559           0 :             break;
     560           1 :             case QGRHanaDataTypes::SmallInt:
     561             :             {
     562           1 :                 odbc::Short val = resultSet_->getShort(paramIndex);
     563           1 :                 featWriter.SetFieldValue(fieldIndex, val);
     564             :             }
     565           1 :             break;
     566           2 :             case QGRHanaDataTypes::Integer:
     567             :             {
     568           2 :                 odbc::Int val = resultSet_->getInt(paramIndex);
     569           2 :                 featWriter.SetFieldValue(fieldIndex, val);
     570             :             }
     571           2 :             break;
     572         588 :             case QGRHanaDataTypes::BigInt:
     573             :             {
     574         588 :                 odbc::Long val = resultSet_->getLong(paramIndex);
     575         588 :                 featWriter.SetFieldValue(fieldIndex, val);
     576             :             }
     577         588 :             break;
     578           1 :             case QGRHanaDataTypes::Real:
     579             :             case QGRHanaDataTypes::Float:
     580             :             {
     581           1 :                 odbc::Float val = resultSet_->getFloat(paramIndex);
     582           1 :                 featWriter.SetFieldValue(fieldIndex, val);
     583             :             }
     584           1 :             break;
     585         573 :             case QGRHanaDataTypes::Double:
     586             :             {
     587         573 :                 odbc::Double val = resultSet_->getDouble(paramIndex);
     588         573 :                 featWriter.SetFieldValue(fieldIndex, val);
     589             :             }
     590         573 :             break;
     591           1 :             case QGRHanaDataTypes::Decimal:
     592             :             case QGRHanaDataTypes::Numeric:
     593             :             {
     594           2 :                 odbc::Decimal val = resultSet_->getDecimal(paramIndex);
     595           1 :                 featWriter.SetFieldValue(fieldIndex, val);
     596             :             }
     597           1 :             break;
     598        1146 :             case QGRHanaDataTypes::Char:
     599             :             case QGRHanaDataTypes::VarChar:
     600             :             case QGRHanaDataTypes::LongVarChar:
     601             :             // Note: NVARCHAR data type is converted to UTF-8 on the HANA side
     602             :             // when using a connection setting CHAR_AS_UTF8=1.
     603             :             case QGRHanaDataTypes::WChar:
     604             :             case QGRHanaDataTypes::WVarChar:
     605             :             case QGRHanaDataTypes::WLongVarChar:
     606             :             {
     607        1146 :                 std::size_t len = resultSet_->getStringLength(paramIndex);
     608        1146 :                 if (len == odbc::ResultSet::NULL_DATA)
     609         570 :                     feature->SetFieldNull(fieldIndex);
     610         576 :                 else if (len == 0)
     611           0 :                     feature->SetField(fieldIndex, "");
     612         576 :                 else if (len != odbc::ResultSet::UNKNOWN_LENGTH)
     613             :                 {
     614         576 :                     EnsureBufferCapacity(len + 1);
     615         576 :                     resultSet_->getStringData(paramIndex, dataBuffer_.data(),
     616             :                                               len + 1);
     617         576 :                     featWriter.SetFieldValue(fieldIndex, dataBuffer_.data());
     618             :                 }
     619             :                 else
     620             :                 {
     621           0 :                     odbc::String data = resultSet_->getString(paramIndex);
     622           0 :                     featWriter.SetFieldValue(fieldIndex, data);
     623             :                 }
     624             :             }
     625        1146 :             break;
     626           7 :             case QGRHanaDataTypes::Binary:
     627             :             case QGRHanaDataTypes::VarBinary:
     628             :             case QGRHanaDataTypes::LongVarBinary:
     629             :             case QGRHanaDataTypes::RealVector:
     630             :             {
     631           7 :                 std::size_t len = resultSet_->getBinaryLength(paramIndex);
     632           7 :                 if (len == 0)
     633           0 :                     feature->SetField(fieldIndex, 0,
     634             :                                       static_cast<GByte *>(nullptr));
     635           7 :                 else if (len == odbc::ResultSet::NULL_DATA)
     636           0 :                     feature->SetFieldNull(fieldIndex);
     637           7 :                 else if (len != odbc::ResultSet::UNKNOWN_LENGTH)
     638             :                 {
     639           7 :                     EnsureBufferCapacity(len);
     640           7 :                     resultSet_->getBinaryData(paramIndex, dataBuffer_.data(),
     641             :                                               len);
     642           7 :                     featWriter.SetFieldValue(fieldIndex, dataBuffer_.data(),
     643             :                                              len);
     644             :                 }
     645             :                 else
     646             :                 {
     647           0 :                     odbc::Binary binData = resultSet_->getBinary(paramIndex);
     648           0 :                     featWriter.SetFieldValue(fieldIndex, binData);
     649             :                 }
     650             :             }
     651           7 :             break;
     652           1 :             case QGRHanaDataTypes::Date:
     653             :             case QGRHanaDataTypes::TypeDate:
     654             :             {
     655           1 :                 odbc::Date date = resultSet_->getDate(paramIndex);
     656           1 :                 featWriter.SetFieldValue(fieldIndex, date);
     657             :             }
     658           1 :             break;
     659           1 :             case QGRHanaDataTypes::Time:
     660             :             case QGRHanaDataTypes::TypeTime:
     661             :             {
     662           1 :                 odbc::Time time = resultSet_->getTime(paramIndex);
     663           1 :                 featWriter.SetFieldValue(fieldIndex, time);
     664             :             }
     665           1 :             break;
     666           4 :             case QGRHanaDataTypes::Timestamp:
     667             :             case QGRHanaDataTypes::TypeTimestamp:
     668             :             {
     669             :                 odbc::Timestamp timestamp =
     670           4 :                     resultSet_->getTimestamp(paramIndex);
     671           4 :                 featWriter.SetFieldValue(fieldIndex, timestamp);
     672             :             }
     673           4 :             break;
     674           0 :             default:
     675           0 :                 break;
     676             :         }
     677             :     }
     678             : 
     679         601 :     return feature.release();
     680             : }
     681             : 
     682             : /************************************************************************/
     683             : /*                      InitFeatureDefinition()                         */
     684             : /************************************************************************/
     685             : 
     686          55 : OGRErr OGRHanaLayer::InitFeatureDefinition(const CPLString &schemaName,
     687             :                                            const CPLString &tableName,
     688             :                                            const CPLString &query,
     689             :                                            const CPLString &featureDefName)
     690             : {
     691          55 :     attrColumns_.clear();
     692          55 :     geomColumns_.clear();
     693          55 :     fidFieldIndex_ = OGRNullFID;
     694          55 :     fidFieldName_.clear();
     695          55 :     featureDefn_ = new OGRFeatureDefn(featureDefName.c_str());
     696          55 :     featureDefn_->Reference();
     697             : 
     698         110 :     std::vector<ColumnDescription> columnDescriptions;
     699             :     OGRErr err =
     700          55 :         dataSource_->GetQueryColumns(schemaName, query, columnDescriptions);
     701          55 :     if (err != OGRERR_NONE)
     702           0 :         return err;
     703             : 
     704             :     std::vector<CPLString> primKeys =
     705          55 :         dataSource_->GetTablePrimaryKeys(schemaName, tableName);
     706             : 
     707          55 :     if (featureDefn_->GetGeomFieldCount() == 1)
     708          55 :         featureDefn_->DeleteGeomFieldDefn(0);
     709             : 
     710         254 :     for (const ColumnDescription &clmDesc : columnDescriptions)
     711             :     {
     712         199 :         if (clmDesc.isGeometry)
     713             :         {
     714          33 :             const GeometryColumnDescription &geometryColumnDesc =
     715             :                 clmDesc.geometryDescription;
     716             : 
     717             :             auto geomFieldDefn = std::make_unique<OGRGeomFieldDefn>(
     718          66 :                 geometryColumnDesc.name.c_str(), geometryColumnDesc.type);
     719          33 :             geomFieldDefn->SetNullable(geometryColumnDesc.isNullable);
     720             : 
     721          33 :             if (geometryColumnDesc.srid >= 0)
     722             :             {
     723             :                 OGRSpatialReference *srs =
     724          30 :                     dataSource_->GetSrsById(geometryColumnDesc.srid);
     725          30 :                 geomFieldDefn->SetSpatialRef(srs);
     726          30 :                 srs->Release();
     727             :             }
     728          33 :             geomColumns_.push_back(geometryColumnDesc);
     729          33 :             featureDefn_->AddGeomFieldDefn(std::move(geomFieldDefn));
     730          33 :             continue;
     731             :         }
     732             : 
     733             :         AttributeColumnDescription attributeColumnDesc =
     734         332 :             clmDesc.attributeDescription;
     735         332 :         auto field = CreateFieldDefn(attributeColumnDesc);
     736             : 
     737         276 :         if ((field->GetType() == OFTInteger ||
     738         332 :              field->GetType() == OFTInteger64) &&
     739          80 :             (fidFieldIndex_ == OGRNullFID && primKeys.size() > 0))
     740             :         {
     741         746 :             for (const CPLString &key : primKeys)
     742             :             {
     743         743 :                 if (key.compare(attributeColumnDesc.name) == 0)
     744             :                 {
     745          47 :                     fidFieldIndex_ = static_cast<int>(attrColumns_.size());
     746          47 :                     fidFieldName_ = field->GetNameRef();
     747          47 :                     attributeColumnDesc.isFeatureID = true;
     748          47 :                     break;
     749             :                 }
     750             :             }
     751             :         }
     752             : 
     753         166 :         if (!attributeColumnDesc.isFeatureID)
     754         119 :             featureDefn_->AddFieldDefn(field.get());
     755         166 :         attrColumns_.push_back(attributeColumnDesc);
     756             :     }
     757             : 
     758          55 :     return OGRERR_NONE;
     759             : }
     760             : 
     761             : /************************************************************************/
     762             : /*                         ReadGeometryExtent()                         */
     763             : /************************************************************************/
     764             : 
     765          14 : void OGRHanaLayer::ReadGeometryExtent(int geomField, OGREnvelope *extent,
     766             :                                       int force)
     767             : {
     768          14 :     EnsureInitialized();
     769             : 
     770          14 :     OGRGeomFieldDefn *geomFieldDef = featureDefn_->GetGeomFieldDefn(geomField);
     771          14 :     const char *clmName = geomFieldDef->GetNameRef();
     772          28 :     odbc::PreparedStatementRef stmt;
     773          14 :     if (!force && IsTableLayer())
     774             :     {
     775           2 :         auto names = dataSource_->FindSchemaAndTableNames(rawQuery_.c_str());
     776           4 :         stmt = dataSource_->PrepareStatement(
     777             :             "SELECT MIN_X, MIN_Y, MAX_X, MAX_Y FROM "
     778             :             "SYS.M_ST_GEOMETRY_COLUMNS WHERE SCHEMA_NAME=? AND TABLE_NAME=? "
     779           2 :             "AND COLUMN_NAME=?");
     780           4 :         stmt->setString(1, names.first == ""
     781           4 :                                ? odbc::String(dataSource_->schemaName_)
     782             :                                : odbc::String(names.first));
     783           2 :         stmt->setString(2, odbc::String(names.second));
     784           2 :         stmt->setString(3, odbc::String(clmName));
     785             :     }
     786             :     else
     787             :     {
     788          12 :         int srid = GetGeometryColumnSrid(geomField);
     789          12 :         if (dataSource_->IsSrsRoundEarth(srid))
     790             :         {
     791           4 :             CPLString quotedClmName = QuotedIdentifier(clmName);
     792             :             bool hasSrsPlanarEquivalent =
     793           2 :                 dataSource_->HasSrsPlanarEquivalent(srid);
     794             :             CPLString geomColumn =
     795           2 :                 !hasSrsPlanarEquivalent
     796             :                     ? quotedClmName
     797           4 :                     : CPLString().Printf("%s.ST_SRID(%d)",
     798             :                                          quotedClmName.c_str(),
     799           6 :                                          ToPlanarSRID(srid));
     800           2 :             CPLString columns = CPLString().Printf(
     801             :                 "MIN(%s.ST_XMin()), MIN(%s.ST_YMin()), MAX(%s.ST_XMax()), "
     802             :                 "MAX(%s.ST_YMax())",
     803             :                 geomColumn.c_str(), geomColumn.c_str(), geomColumn.c_str(),
     804           2 :                 geomColumn.c_str());
     805           4 :             stmt = dataSource_->PrepareStatement(
     806           6 :                 BuildQuery(rawQuery_.c_str(), columns.c_str()));
     807             :         }
     808             :         else
     809             :         {
     810             :             CPLString columns =
     811          10 :                 CPLString().Printf("ST_EnvelopeAggr(%s) AS ext",
     812          30 :                                    QuotedIdentifier(clmName).c_str());
     813          10 :             CPLString subQuery = BuildQuery(rawQuery_.c_str(), columns);
     814          30 :             stmt = dataSource_->PrepareStatement(CPLString().Printf(
     815             :                 "SELECT "
     816             :                 "ext.ST_XMin(),ext.ST_YMin(),ext.ST_XMax(),ext.ST_YMax() "
     817             :                 "FROM (%s)",
     818          20 :                 subQuery.c_str()));
     819             :         }
     820             :     }
     821             : 
     822          14 :     bool set = false;
     823          14 :     extent->MinX = 0.0;
     824          14 :     extent->MaxX = 0.0;
     825          14 :     extent->MinY = 0.0;
     826          14 :     extent->MaxY = 0.0;
     827             : 
     828          28 :     odbc::ResultSetRef rsExtent = stmt->executeQuery();
     829          14 :     if (rsExtent->next())
     830             :     {
     831          14 :         odbc::Double val = rsExtent->getDouble(1);
     832          14 :         if (!val.isNull())
     833             :         {
     834          12 :             extent->MinX = *val;
     835          12 :             extent->MinY = *rsExtent->getDouble(2);
     836          12 :             extent->MaxX = *rsExtent->getDouble(3);
     837          12 :             extent->MaxY = *rsExtent->getDouble(4);
     838          12 :             set = true;
     839             :         }
     840             :     }
     841             : 
     842          14 :     rsExtent->close();
     843          14 :     if (!set && !force)
     844           1 :         ReadGeometryExtent(geomField, extent, true);
     845          14 : }
     846             : 
     847          16 : bool OGRHanaLayer::IsFastExtentAvailable()
     848             : {
     849          16 :     if (geomColumns_.empty())
     850           0 :         return false;
     851             : 
     852          16 :     switch (dataSource_->GetHanaVersion().major())
     853             :     {
     854           0 :         case 2:
     855           0 :             return dataSource_->GetHanaVersion() >= HanaVersion(2, 0, 80);
     856          16 :         case 4:
     857          16 :             return dataSource_->GetHanaCloudVersion() >=
     858          32 :                    HanaVersion(2024, 2, 0);
     859             :     }
     860             : 
     861           0 :     return false;
     862             : }
     863             : 
     864             : /************************************************************************/
     865             : /*                          ResetReading()                              */
     866             : /************************************************************************/
     867             : 
     868         307 : void OGRHanaLayer::ResetReading()
     869             : {
     870         307 :     nextFeatureId_ = 0;
     871         307 :     resultSet_.reset();
     872         307 : }
     873             : 
     874             : /************************************************************************/
     875             : /*                            GetExtent()                               */
     876             : /************************************************************************/
     877             : 
     878          17 : OGRErr OGRHanaLayer::GetExtent(int iGeomField, OGREnvelope *extent, int force)
     879             : {
     880          30 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
     881          13 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
     882             :     {
     883           4 :         extent->MinX = 0.0;
     884           4 :         extent->MaxX = 0.0;
     885           4 :         extent->MinY = 0.0;
     886           4 :         extent->MaxY = 0.0;
     887             : 
     888           4 :         if (iGeomField != 0)
     889             :         {
     890           4 :             CPLError(CE_Failure, CPLE_AppDefined,
     891             :                      "Invalid geometry field index : %d", iGeomField);
     892             :         }
     893           4 :         return OGRERR_FAILURE;
     894             :     }
     895             : 
     896             :     try
     897             :     {
     898          13 :         if (!force)
     899           2 :             force = !IsFastExtentAvailable();
     900          13 :         ReadGeometryExtent(iGeomField, extent, force);
     901             : 
     902          13 :         return OGRERR_NONE;
     903             :     }
     904           0 :     catch (const std::exception &ex)
     905             :     {
     906             :         CPLString clmName =
     907           0 :             (iGeomField < static_cast<int>(geomColumns_.size()))
     908           0 :                 ? geomColumns_[static_cast<std::size_t>(geomColumns_.size())]
     909           0 :                       .name
     910           0 :                 : "unknown column";
     911           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     912             :                  "Unable to query extent of '%s' using fast method: %s",
     913           0 :                  clmName.c_str(), ex.what());
     914             :     }
     915             : 
     916           0 :     if (iGeomField == 0)
     917           0 :         return OGRLayer::GetExtent(extent, force);
     918             :     else
     919           0 :         return OGRLayer::GetExtent(iGeomField, extent, force);
     920             : }
     921             : 
     922             : /************************************************************************/
     923             : /*                            GetFeatureCount()                         */
     924             : /************************************************************************/
     925             : 
     926          55 : GIntBig OGRHanaLayer::GetFeatureCount(CPL_UNUSED int force)
     927             : {
     928          55 :     EnsureInitialized();
     929             : 
     930          55 :     GIntBig ret = 0;
     931          55 :     CPLString sql = CPLString().Printf("SELECT COUNT(*) FROM (%s) AS tmp",
     932         110 :                                        GetQueryStatement().c_str());
     933         110 :     odbc::StatementRef stmt = dataSource_->CreateStatement();
     934          55 :     odbc::ResultSetRef rs = stmt->executeQuery(sql.c_str());
     935          55 :     if (rs->next())
     936          55 :         ret = *rs->getLong(1);
     937          55 :     rs->close();
     938         110 :     return ret;
     939             : }
     940             : 
     941             : /************************************************************************/
     942             : /*                           GetLayerDefn()                             */
     943             : /************************************************************************/
     944             : 
     945         464 : OGRFeatureDefn *OGRHanaLayer::GetLayerDefn()
     946             : {
     947         464 :     EnsureInitialized();
     948         464 :     return featureDefn_;
     949             : }
     950             : 
     951             : /************************************************************************/
     952             : /*                               GetName()                              */
     953             : /************************************************************************/
     954             : 
     955           2 : const char *OGRHanaLayer::GetName()
     956             : {
     957           2 :     return GetDescription();
     958             : }
     959             : 
     960             : /************************************************************************/
     961             : /*                         GetNextFeature()                             */
     962             : /************************************************************************/
     963             : 
     964         655 : OGRFeature *OGRHanaLayer::GetNextFeature()
     965             : {
     966         655 :     EnsureInitialized();
     967             : 
     968             :     while (true)
     969             :     {
     970         655 :         OGRFeature *feature = GetNextFeatureInternal();
     971         655 :         if (feature == nullptr)
     972          54 :             return nullptr;
     973             : 
     974        1289 :         if ((m_poFilterGeom == nullptr ||
     975        1202 :              FilterGeometry(feature->GetGeometryRef())) &&
     976         601 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(feature)))
     977         601 :             return feature;
     978             : 
     979           0 :         delete feature;
     980           0 :     }
     981             : }
     982             : 
     983             : /************************************************************************/
     984             : /*                            GetFIDColumn()                            */
     985             : /************************************************************************/
     986             : 
     987          96 : const char *OGRHanaLayer::GetFIDColumn()
     988             : {
     989          96 :     EnsureInitialized();
     990          96 :     return fidFieldName_.c_str();
     991             : }
     992             : 
     993             : /************************************************************************/
     994             : /*                         SetAttributeFilter()                         */
     995             : /************************************************************************/
     996             : 
     997         119 : OGRErr OGRHanaLayer::SetAttributeFilter(const char *pszQuery)
     998             : {
     999         119 :     CPLFree(m_pszAttrQueryString);
    1000         119 :     m_pszAttrQueryString = pszQuery ? CPLStrdup(pszQuery) : nullptr;
    1001             : 
    1002         119 :     if (pszQuery == nullptr || strlen(pszQuery) == 0)
    1003          87 :         attrFilter_ = "";
    1004             :     else
    1005          32 :         attrFilter_.assign(pszQuery, strlen(pszQuery));
    1006             : 
    1007         119 :     ClearQueryStatement();
    1008         119 :     BuildWhereClause();
    1009         119 :     ResetReading();
    1010             : 
    1011         119 :     return OGRERR_NONE;
    1012             : }
    1013             : 
    1014             : /************************************************************************/
    1015             : /*                          SetSpatialFilter()                          */
    1016             : /************************************************************************/
    1017             : 
    1018          96 : void OGRHanaLayer::SetSpatialFilter(int iGeomField, OGRGeometry *poGeom)
    1019             : {
    1020          96 :     m_iGeomFieldFilter = 0;
    1021             : 
    1022          96 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
    1023             :     {
    1024           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    1025             :                  "Invalid geometry field index : %d", iGeomField);
    1026           4 :         return;
    1027             :     }
    1028          92 :     m_iGeomFieldFilter = iGeomField;
    1029             : 
    1030          92 :     if (!InstallFilter(poGeom))
    1031          72 :         return;
    1032             : 
    1033          20 :     ClearQueryStatement();
    1034          20 :     BuildWhereClause();
    1035          20 :     ResetReading();
    1036             : }
    1037             : 
    1038             : }  // namespace OGRHANA

Generated by: LCOV version 1.14