LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/arrow_common - ograrrowlayer.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2740 3119 87.8 %
Date: 2025-07-09 17:50:03 Functions: 128 135 94.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Arrow generic code
       4             :  * Purpose:  Arrow generic code
       5             :  * Author:   Even Rouault, <even.rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Planet Labs
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #ifndef OGARROWLAYER_HPP_INCLUDED
      14             : #define OGARROWLAYER_HPP_INCLUDED
      15             : 
      16             : #include "ogr_arrow.h"
      17             : 
      18             : #include "cpl_float.h"
      19             : #include "cpl_json.h"
      20             : #include "cpl_time.h"
      21             : #include "ogrlayerarrow.h"
      22             : #include "ogr_p.h"
      23             : #include "ogr_swq.h"
      24             : #include "ogr_wkb.h"
      25             : 
      26             : #include <algorithm>
      27             : #include <cassert>
      28             : #include <cinttypes>
      29             : #include <limits>
      30             : #include <string_view>
      31             : 
      32             : #define SWQ_ISNOTNULL (-SWQ_ISNULL)
      33             : 
      34             : #if defined(__clang__)
      35             : #pragma clang diagnostic push
      36             : #pragma clang diagnostic ignored "-Wweak-vtables"
      37             : #endif
      38             : 
      39             : /************************************************************************/
      40             : /*                         OGRArrowLayer()                              */
      41             : /************************************************************************/
      42             : 
      43        1681 : inline OGRArrowLayer::OGRArrowLayer(OGRArrowDataset *poDS,
      44        1681 :                                     const char *pszLayerName)
      45        1681 :     : m_poArrowDS(poDS), m_poMemoryPool(poDS->GetMemoryPool())
      46             : {
      47        1681 :     m_poFeatureDefn = new OGRFeatureDefn(pszLayerName);
      48        1681 :     m_poFeatureDefn->SetGeomType(wkbNone);
      49        1681 :     m_poFeatureDefn->Reference();
      50        1681 :     SetDescription(pszLayerName);
      51        1681 : }
      52             : 
      53             : /************************************************************************/
      54             : /*                        ~OGRFeatherLayer()                            */
      55             : /************************************************************************/
      56             : 
      57        1681 : inline OGRArrowLayer::~OGRArrowLayer()
      58             : {
      59        1681 :     if (m_sCachedSchema.release)
      60          59 :         m_sCachedSchema.release(&m_sCachedSchema);
      61             : 
      62        1681 :     CPLDebug("ARROW", "Memory pool: bytes_allocated = %" PRId64,
      63        1681 :              m_poMemoryPool->bytes_allocated());
      64        1681 :     CPLDebug("ARROW", "Memory pool: max_memory = %" PRId64,
      65        1681 :              m_poMemoryPool->max_memory());
      66        1681 :     m_poFeatureDefn->Release();
      67        1681 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                         LoadGDALSchema()                             */
      71             : /************************************************************************/
      72             : 
      73             : inline std::map<std::string, std::unique_ptr<OGRFieldDefn>>
      74        1681 : OGRArrowLayer::LoadGDALSchema(const arrow::KeyValueMetadata *kv_metadata)
      75             : {
      76             :     std::map<std::string, std::unique_ptr<OGRFieldDefn>>
      77        1681 :         oMapFieldNameToGDALSchemaFieldDefn;
      78        1718 :     if (kv_metadata && kv_metadata->Contains("gdal:schema") &&
      79          37 :         CPLTestBool(CPLGetConfigOption(
      80        1718 :             ("OGR_" + GetDriverUCName() + "_READ_GDAL_SCHEMA").c_str(), "YES")))
      81             :     {
      82          74 :         auto gdalSchema = kv_metadata->Get("gdal:schema");
      83          37 :         if (gdalSchema.ok())
      84             :         {
      85          37 :             CPLDebug(GetDriverUCName().c_str(), "gdal:schema = %s",
      86             :                      gdalSchema->c_str());
      87          74 :             CPLJSONDocument oDoc;
      88          37 :             if (oDoc.LoadMemory(*gdalSchema))
      89             :             {
      90          74 :                 auto oRoot = oDoc.GetRoot();
      91             : 
      92          37 :                 m_osFIDColumn = oRoot.GetString("fid");
      93             : 
      94         111 :                 auto oColumns = oRoot.GetObj("columns");
      95          37 :                 if (oColumns.IsValid())
      96             :                 {
      97         414 :                     for (const auto &oColumn : oColumns.GetChildren())
      98             :                     {
      99         754 :                         const auto osName = oColumn.GetName();
     100        1131 :                         const auto osType = oColumn.GetString("type");
     101        1131 :                         const auto osSubType = oColumn.GetString("subtype");
     102             :                         auto poFieldDefn = std::make_unique<OGRFieldDefn>(
     103         754 :                             osName.c_str(), OFTString);
     104        2267 :                         for (int iType = 0;
     105        2267 :                              iType <= static_cast<int>(OFTMaxType); iType++)
     106             :                         {
     107        2267 :                             if (EQUAL(osType.c_str(),
     108             :                                       OGRFieldDefn::GetFieldTypeName(
     109             :                                           static_cast<OGRFieldType>(iType))))
     110             :                             {
     111         377 :                                 poFieldDefn->SetType(
     112             :                                     static_cast<OGRFieldType>(iType));
     113         377 :                                 break;
     114             :                             }
     115             :                         }
     116         377 :                         if (!osSubType.empty())
     117             :                         {
     118         500 :                             for (int iSubType = 0;
     119         500 :                                  iSubType <= static_cast<int>(OFSTMaxSubType);
     120             :                                  iSubType++)
     121             :                             {
     122         500 :                                 if (EQUAL(osSubType.c_str(),
     123             :                                           OGRFieldDefn::GetFieldSubTypeName(
     124             :                                               static_cast<OGRFieldSubType>(
     125             :                                                   iSubType))))
     126             :                                 {
     127         112 :                                     poFieldDefn->SetSubType(
     128             :                                         static_cast<OGRFieldSubType>(iSubType));
     129         112 :                                     break;
     130             :                                 }
     131             :                             }
     132             :                         }
     133         377 :                         poFieldDefn->SetWidth(oColumn.GetInteger("width"));
     134         377 :                         poFieldDefn->SetPrecision(
     135             :                             oColumn.GetInteger("precision"));
     136             : 
     137             :                         const auto osAlternativeName =
     138        1131 :                             oColumn.GetString("alternative_name");
     139         377 :                         if (!osAlternativeName.empty())
     140           2 :                             poFieldDefn->SetAlternativeName(
     141             :                                 osAlternativeName.c_str());
     142             : 
     143        1131 :                         const auto osComment = oColumn.GetString("comment");
     144         377 :                         if (!osComment.empty())
     145           3 :                             poFieldDefn->SetComment(osComment);
     146             : 
     147         377 :                         oMapFieldNameToGDALSchemaFieldDefn[osName] =
     148         754 :                             std::move(poFieldDefn);
     149             :                     }
     150             :                 }
     151             :             }
     152             :         }
     153             :     }
     154        1681 :     return oMapFieldNameToGDALSchemaFieldDefn;
     155             : }
     156             : 
     157             : /************************************************************************/
     158             : /*                        LoadGDALMetadata()                            */
     159             : /************************************************************************/
     160             : 
     161             : inline void
     162        1136 : OGRArrowLayer::LoadGDALMetadata(const arrow::KeyValueMetadata *kv_metadata)
     163             : {
     164        1136 :     if (kv_metadata && kv_metadata->Contains("gdal:metadata"))
     165             :     {
     166          14 :         auto gdalMetadata = kv_metadata->Get("gdal:metadata");
     167           7 :         if (gdalMetadata.ok())
     168             :         {
     169          14 :             CPLJSONDocument oDoc;
     170           7 :             if (oDoc.LoadMemory(*gdalMetadata))
     171             :             {
     172          14 :                 auto oRoot = oDoc.GetRoot();
     173          16 :                 for (const auto &oDomain : oRoot.GetChildren())
     174             :                 {
     175          10 :                     if (STARTS_WITH(oDomain.GetName().c_str(), "json:") &&
     176           1 :                         oDomain.GetType() == CPLJSONObject::Type::Object)
     177             :                     {
     178           1 :                         char **papszMD = nullptr;
     179           1 :                         papszMD = CSLAddString(
     180             :                             papszMD,
     181           2 :                             oDomain.Format(CPLJSONObject::PrettyFormat::Plain)
     182             :                                 .c_str());
     183           1 :                         SetMetadata(papszMD, oDomain.GetName().c_str());
     184           1 :                         CSLDestroy(papszMD);
     185             :                     }
     186           9 :                     else if (STARTS_WITH(oDomain.GetName().c_str(), "xml:") &&
     187           1 :                              oDomain.GetType() == CPLJSONObject::Type::String)
     188             :                     {
     189           1 :                         char **papszMD = nullptr;
     190             :                         papszMD =
     191           1 :                             CSLAddString(papszMD, oDomain.ToString().c_str());
     192           1 :                         SetMetadata(papszMD, oDomain.GetName().c_str());
     193           1 :                         CSLDestroy(papszMD);
     194             :                     }
     195             :                     else
     196             :                     {
     197          14 :                         for (const auto &oItem : oDomain.GetChildren())
     198             :                         {
     199           7 :                             if (oItem.GetType() == CPLJSONObject::Type::String)
     200             :                             {
     201          14 :                                 SetMetadataItem(oItem.GetName().c_str(),
     202          14 :                                                 oItem.ToString().c_str(),
     203          14 :                                                 oDomain.GetName().c_str());
     204             :                             }
     205             :                         }
     206             :                     }
     207             :                 }
     208             :             }
     209             :         }
     210             :     }
     211        1136 : }
     212             : 
     213             : /************************************************************************/
     214             : /*                        IsIntegerArrowType()                          */
     215             : /************************************************************************/
     216             : 
     217        8952 : inline bool OGRArrowLayer::IsIntegerArrowType(arrow::Type::type typeId)
     218             : {
     219        8551 :     return typeId == arrow::Type::INT8 || typeId == arrow::Type::UINT8 ||
     220        7749 :            typeId == arrow::Type::INT16 || typeId == arrow::Type::UINT16 ||
     221        6540 :            typeId == arrow::Type::INT32 || typeId == arrow::Type::UINT32 ||
     222       17503 :            typeId == arrow::Type::INT64 || typeId == arrow::Type::UINT64;
     223             : }
     224             : 
     225             : /************************************************************************/
     226             : /*                         IsHandledListOrMapType()                     */
     227             : /************************************************************************/
     228             : 
     229        8946 : inline bool OGRArrowLayer::IsHandledListOrMapType(
     230             :     const std::shared_ptr<arrow::DataType> &valueType)
     231             : {
     232        8946 :     const auto itemTypeId = valueType->id();
     233        8545 :     return itemTypeId == arrow::Type::BOOL || IsIntegerArrowType(itemTypeId) ||
     234        5291 :            itemTypeId == arrow::Type::HALF_FLOAT ||
     235        4890 :            itemTypeId == arrow::Type::FLOAT ||
     236        4489 :            itemTypeId == arrow::Type::DOUBLE ||
     237             : #if ARROW_VERSION_MAJOR >= 18
     238        4489 :            itemTypeId == arrow::Type::DECIMAL32 ||
     239        4489 :            itemTypeId == arrow::Type::DECIMAL64 ||
     240             : #endif
     241        3715 :            itemTypeId == arrow::Type::DECIMAL128 ||
     242        3691 :            itemTypeId == arrow::Type::DECIMAL256 ||
     243        1715 :            itemTypeId == arrow::Type::STRING ||
     244        1691 :            itemTypeId == arrow::Type::LARGE_STRING ||
     245             : #if ARROW_VERSION_MAJOR >= 15
     246        1687 :            itemTypeId == arrow::Type::STRING_VIEW ||
     247             : #endif
     248        1686 :            itemTypeId == arrow::Type::BINARY ||
     249             : #if ARROW_VERSION_MAJOR >= 15
     250        1686 :            itemTypeId == arrow::Type::BINARY_VIEW ||
     251             : #endif
     252        1262 :            itemTypeId == arrow::Type::STRUCT ||
     253          26 :            (itemTypeId == arrow::Type::MAP &&
     254          13 :             IsHandledMapType(
     255       27699 :                 std::static_pointer_cast<arrow::MapType>(valueType))) ||
     256          48 :            ((itemTypeId == arrow::Type::LIST ||
     257          24 :              itemTypeId == arrow::Type::LARGE_LIST ||
     258        2498 :              itemTypeId == arrow::Type::FIXED_SIZE_LIST) &&
     259        1249 :             IsHandledListType(
     260       19141 :                 std::static_pointer_cast<arrow::BaseListType>(valueType)));
     261             : }
     262             : 
     263             : /************************************************************************/
     264             : /*                         IsHandledListType()                          */
     265             : /************************************************************************/
     266             : 
     267        1717 : inline bool OGRArrowLayer::IsHandledListType(
     268             :     const std::shared_ptr<arrow::BaseListType> &listType)
     269             : {
     270        1717 :     return IsHandledListOrMapType(listType->value_type());
     271             : }
     272             : 
     273             : /************************************************************************/
     274             : /*                          IsHandledMapType()                          */
     275             : /************************************************************************/
     276             : 
     277             : inline bool
     278        7229 : OGRArrowLayer::IsHandledMapType(const std::shared_ptr<arrow::MapType> &mapType)
     279             : {
     280        7229 :     const auto typeId = mapType->key_type()->id();
     281             :     return (typeId == arrow::Type::STRING
     282             : #if ARROW_VERSION_MAJOR >= 15
     283           2 :             || typeId == arrow::Type::STRING_VIEW
     284             : #endif
     285       14458 :             ) &&
     286       14458 :            IsHandledListOrMapType(mapType->item_type());
     287             : }
     288             : 
     289             : /************************************************************************/
     290             : /*                        MapArrowTypeToOGR()                           */
     291             : /************************************************************************/
     292             : 
     293       35916 : inline bool OGRArrowLayer::MapArrowTypeToOGR(
     294             :     const std::shared_ptr<arrow::DataType> &typeIn,
     295             :     const std::shared_ptr<arrow::Field> &field, OGRFieldDefn &oField,
     296             :     OGRFieldType &eType, OGRFieldSubType &eSubType,
     297             :     const std::vector<int> &path,
     298             :     const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
     299             :         &oMapFieldNameToGDALSchemaFieldDefn)
     300             : {
     301       35916 :     bool bTypeOK = false;
     302             : 
     303       71832 :     std::string osExtensionName;
     304       35916 :     std::shared_ptr<arrow::DataType> type(typeIn);
     305       35916 :     if (type->id() == arrow::Type::EXTENSION)
     306             :     {
     307         167 :         auto extensionType = cpl::down_cast<arrow::ExtensionType *>(type.get());
     308         167 :         osExtensionName = extensionType->extension_name();
     309         167 :         type = extensionType->storage_type();
     310             :     }
     311       35754 :     else if (const auto &field_kv_metadata = field->metadata())
     312             :     {
     313          10 :         auto extension_name = field_kv_metadata->Get(ARROW_EXTENSION_NAME_KEY);
     314           5 :         if (extension_name.ok())
     315             :         {
     316           3 :             osExtensionName = *extension_name;
     317             :         }
     318             :     }
     319             : 
     320       36086 :     if (!osExtensionName.empty() &&
     321         170 :         osExtensionName != EXTENSION_NAME_ARROW_JSON)
     322             :     {
     323           6 :         CPLDebug(GetDriverUCName().c_str(),
     324             :                  "Dealing with field %s of extension type %s as %s",
     325           3 :                  field->name().c_str(), osExtensionName.c_str(),
     326           6 :                  type->ToString().c_str());
     327             :     }
     328             : 
     329       35916 :     switch (type->id())
     330             :     {
     331         382 :         case arrow::Type::NA:
     332         382 :             break;
     333             : 
     334         413 :         case arrow::Type::BOOL:
     335         413 :             bTypeOK = true;
     336         413 :             eType = OFTInteger;
     337         413 :             eSubType = OFSTBoolean;
     338         413 :             break;
     339        1194 :         case arrow::Type::UINT8:
     340             :         case arrow::Type::INT8:
     341             :         case arrow::Type::UINT16:
     342        1194 :             bTypeOK = true;
     343        1194 :             eType = OFTInteger;
     344        1194 :             break;
     345         406 :         case arrow::Type::INT16:
     346         406 :             bTypeOK = true;
     347         406 :             eType = OFTInteger;
     348         406 :             eSubType = OFSTInt16;
     349         406 :             break;
     350          24 :         case arrow::Type::UINT32:
     351          24 :             bTypeOK = true;
     352          24 :             eType = OFTInteger64;
     353          24 :             break;
     354        1398 :         case arrow::Type::INT32:
     355        1398 :             bTypeOK = true;
     356        1398 :             eType = OFTInteger;
     357        1398 :             break;
     358         398 :         case arrow::Type::UINT64:
     359         398 :             bTypeOK = true;
     360         398 :             eType = OFTReal;  // potential loss
     361         398 :             break;
     362        1700 :         case arrow::Type::INT64:
     363        1700 :             bTypeOK = true;
     364        1700 :             eType = OFTInteger64;
     365        1700 :             break;
     366         519 :         case arrow::Type::HALF_FLOAT:  // should use OFSTFloat16 if we had it
     367             :         case arrow::Type::FLOAT:
     368         519 :             bTypeOK = true;
     369         519 :             eType = OFTReal;
     370         519 :             eSubType = OFSTFloat32;
     371         519 :             break;
     372         880 :         case arrow::Type::DOUBLE:
     373         880 :             bTypeOK = true;
     374         880 :             eType = OFTReal;
     375         880 :             break;
     376        2489 :         case arrow::Type::STRING:
     377             :         case arrow::Type::LARGE_STRING:
     378             : #if ARROW_VERSION_MAJOR >= 15
     379             :         case arrow::Type::STRING_VIEW:
     380             : #endif
     381        2489 :             bTypeOK = true;
     382        2489 :             eType = OFTString;
     383        2489 :             if (osExtensionName == EXTENSION_NAME_ARROW_JSON)
     384         167 :                 eSubType = OFSTJSON;
     385        2489 :             break;
     386         830 :         case arrow::Type::BINARY:
     387             :         case arrow::Type::LARGE_BINARY:
     388             : #if ARROW_VERSION_MAJOR >= 15
     389             :         case arrow::Type::BINARY_VIEW:
     390             : #endif
     391         830 :             bTypeOK = true;
     392         830 :             eType = OFTBinary;
     393         830 :             break;
     394         406 :         case arrow::Type::FIXED_SIZE_BINARY:
     395         406 :             bTypeOK = true;
     396         406 :             eType = OFTBinary;
     397         406 :             oField.SetWidth(
     398         812 :                 std::static_pointer_cast<arrow::FixedSizeBinaryType>(type)
     399         406 :                     ->byte_width());
     400         406 :             break;
     401             : 
     402         857 :         case arrow::Type::DATE32:
     403             :         case arrow::Type::DATE64:
     404         857 :             bTypeOK = true;
     405         857 :             eType = OFTDate;
     406         857 :             break;
     407             : 
     408        2482 :         case arrow::Type::TIMESTAMP:
     409             :         {
     410        2482 :             bTypeOK = true;
     411             :             const auto timestampType =
     412        2482 :                 static_cast<arrow::TimestampType *>(type.get());
     413        2482 :             eType = OFTDateTime;
     414        2482 :             const auto &osTZ = timestampType->timezone();
     415        2482 :             int nTZFlag = OGRTimezoneToTZFlag(osTZ.c_str(), false);
     416        2482 :             if (nTZFlag == OGR_TZFLAG_UNKNOWN && !osTZ.empty())
     417             :             {
     418           0 :                 CPLDebug(GetDriverUCName().c_str(),
     419             :                          "Field %s has unrecognized timezone %s. "
     420             :                          "UTC datetime will be used instead.",
     421           0 :                          field->name().c_str(), osTZ.c_str());
     422           0 :                 nTZFlag = OGR_TZFLAG_UTC;
     423             :             }
     424        2482 :             oField.SetTZFlag(nTZFlag);
     425        2482 :             break;
     426             :         }
     427             : 
     428         812 :         case arrow::Type::TIME32:
     429         812 :             bTypeOK = true;
     430         812 :             eType = OFTTime;
     431         812 :             break;
     432             : 
     433         796 :         case arrow::Type::TIME64:
     434         796 :             bTypeOK = true;
     435         796 :             eType = OFTInteger64;  // our OFTTime doesn't have micro or
     436             :                                    // nanosecond accuracy
     437         796 :             break;
     438             : 
     439             : #if ARROW_VERSION_MAJOR >= 18
     440         822 :         case arrow::Type::DECIMAL32:
     441             :         case arrow::Type::DECIMAL64:
     442             : #endif
     443             :         case arrow::Type::DECIMAL128:
     444             :         case arrow::Type::DECIMAL256:
     445             :         {
     446         822 :             bTypeOK = true;
     447             :             const auto decimalType =
     448        1644 :                 std::static_pointer_cast<arrow::DecimalType>(type);
     449         822 :             eType = OFTReal;
     450         822 :             oField.SetWidth(decimalType->precision());
     451         822 :             oField.SetPrecision(decimalType->scale());
     452         822 :             break;
     453             :         }
     454             : 
     455       11892 :         case arrow::Type::LIST:
     456             :         case arrow::Type::FIXED_SIZE_LIST:
     457             :         {
     458       11892 :             bTypeOK = true;
     459       23784 :             auto listType = std::static_pointer_cast<arrow::BaseListType>(type);
     460       11892 :             switch (listType->value_type()->id())
     461             :             {
     462         812 :                 case arrow::Type::BOOL:
     463         812 :                     eType = OFTIntegerList;
     464         812 :                     eSubType = OFSTBoolean;
     465         812 :                     break;
     466        4060 :                 case arrow::Type::UINT8:
     467             :                 case arrow::Type::INT8:
     468             :                 case arrow::Type::UINT16:
     469             :                 case arrow::Type::INT16:
     470             :                 case arrow::Type::INT32:
     471        4060 :                     eType = OFTIntegerList;
     472        4060 :                     break;
     473          46 :                 case arrow::Type::UINT32:
     474          46 :                     eType = OFTInteger64List;
     475          46 :                     break;
     476         796 :                 case arrow::Type::UINT64:
     477         796 :                     eType = OFTRealList;  // potential loss
     478         796 :                     break;
     479        1985 :                 case arrow::Type::INT64:
     480        1985 :                     eType = OFTInteger64List;
     481        1985 :                     break;
     482         841 :                 case arrow::Type::HALF_FLOAT:  // should use OFSTFloat16 if we
     483             :                                                // had it
     484             :                 case arrow::Type::FLOAT:
     485         841 :                     eType = OFTRealList;
     486         841 :                     eSubType = OFSTFloat32;
     487         841 :                     break;
     488        1643 :                 case arrow::Type::DOUBLE:
     489             : #if ARROW_VERSION_MAJOR >= 18
     490             :                 case arrow::Type::DECIMAL32:
     491             :                 case arrow::Type::DECIMAL64:
     492             : #endif
     493             :                 case arrow::Type::DECIMAL128:
     494             :                 case arrow::Type::DECIMAL256:
     495        1643 :                     eType = OFTRealList;
     496        1643 :                     break;
     497        1241 :                 case arrow::Type::STRING:
     498             :                 case arrow::Type::LARGE_STRING:
     499             :                 case arrow::Type::BINARY:
     500             : #if ARROW_VERSION_MAJOR >= 15
     501             :                 case arrow::Type::STRING_VIEW:
     502             : #endif
     503        1241 :                     eType = OFTStringList;
     504        1241 :                     break;
     505         468 :                 default:
     506             :                 {
     507         468 :                     if (IsHandledListType(listType))
     508             :                     {
     509         468 :                         eType = OFTString;
     510         468 :                         eSubType = OFSTJSON;
     511             :                     }
     512             :                     else
     513             :                     {
     514           0 :                         bTypeOK = false;
     515           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     516             :                                  "Field %s of unhandled type %s ignored",
     517           0 :                                  field->name().c_str(),
     518           0 :                                  type->ToString().c_str());
     519             :                     }
     520         468 :                     break;
     521             :                 }
     522             :             }
     523       11892 :             break;
     524             :         }
     525             : 
     526        7216 :         case arrow::Type::MAP:
     527             :         {
     528        7216 :             bTypeOK = true;
     529       14432 :             auto mapType = std::static_pointer_cast<arrow::MapType>(type);
     530        7216 :             if (IsHandledMapType(mapType))
     531             :             {
     532        7216 :                 eType = OFTString;
     533        7216 :                 eSubType = OFSTJSON;
     534             :             }
     535             :             else
     536             :             {
     537           0 :                 bTypeOK = false;
     538           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     539             :                          "Field %s of unhandled type %s ignored",
     540           0 :                          field->name().c_str(), type->ToString().c_str());
     541             :             }
     542        7216 :             break;
     543             :         }
     544             : 
     545           0 :         case arrow::Type::STRUCT:
     546             :             // should be handled by specialized code
     547           0 :             CPLAssert(false);
     548             :             break;
     549             : 
     550             :             // unhandled types
     551             : 
     552           0 :         case arrow::Type::INTERVAL_MONTHS:
     553             :         case arrow::Type::INTERVAL_DAY_TIME:
     554             :         case arrow::Type::SPARSE_UNION:
     555             :         case arrow::Type::DENSE_UNION:
     556             :         case arrow::Type::DICTIONARY:
     557             :         case arrow::Type::EXTENSION:
     558             :         case arrow::Type::DURATION:
     559             :         case arrow::Type::LARGE_LIST:
     560             :         case arrow::Type::INTERVAL_MONTH_DAY_NANO:
     561             : #if ARROW_VERSION_MAJOR >= 12
     562             :         case arrow::Type::RUN_END_ENCODED:
     563             : #endif
     564             : #if ARROW_VERSION_MAJOR >= 15
     565             :         case arrow::Type::LIST_VIEW:
     566             :         case arrow::Type::LARGE_LIST_VIEW:
     567             : #endif
     568             :         case arrow::Type::MAX_ID:
     569             :         {
     570           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     571             :                      "Field %s of unhandled type %s ignored",
     572           0 :                      field->name().c_str(), type->ToString().c_str());
     573           0 :             break;
     574             :         }
     575             :     }
     576             : 
     577       35916 :     if (bTypeOK)
     578             :     {
     579             :         const auto oIter =
     580       35534 :             oMapFieldNameToGDALSchemaFieldDefn.find(field->name());
     581       35534 :         oField.SetType(eType);
     582       35534 :         if (oIter != oMapFieldNameToGDALSchemaFieldDefn.end())
     583             :         {
     584         377 :             const auto &poGDALFieldDefn = oIter->second;
     585         377 :             if (poGDALFieldDefn->GetType() == eType)
     586             :             {
     587         377 :                 if (eSubType == OFSTNone)
     588             :                 {
     589         265 :                     eSubType = poGDALFieldDefn->GetSubType();
     590             :                 }
     591         112 :                 else if (eSubType != poGDALFieldDefn->GetSubType())
     592             :                 {
     593           0 :                     CPLDebug(
     594           0 :                         GetDriverUCName().c_str(),
     595             :                         "Field subtype inferred from Parquet/Arrow schema is "
     596             :                         "%s, "
     597             :                         "whereas the one in gdal:schema is %s. "
     598             :                         "Using the former one.",
     599             :                         OGR_GetFieldSubTypeName(eSubType),
     600             :                         OGR_GetFieldSubTypeName(poGDALFieldDefn->GetSubType()));
     601             :                 }
     602             :             }
     603             :             else
     604             :             {
     605           0 :                 CPLDebug(GetDriverUCName().c_str(),
     606             :                          "Field type inferred from Parquet/Arrow schema is %s, "
     607             :                          "whereas the one in gdal:schema is %s. "
     608             :                          "Using the former one.",
     609             :                          OGR_GetFieldTypeName(eType),
     610             :                          OGR_GetFieldTypeName(poGDALFieldDefn->GetType()));
     611             :             }
     612         377 :             if (poGDALFieldDefn->GetWidth() > 0)
     613          36 :                 oField.SetWidth(poGDALFieldDefn->GetWidth());
     614         377 :             if (poGDALFieldDefn->GetPrecision() > 0)
     615          16 :                 oField.SetPrecision(poGDALFieldDefn->GetPrecision());
     616         377 :             if (poGDALFieldDefn->GetAlternativeNameRef()[0])
     617           2 :                 oField.SetAlternativeName(
     618             :                     poGDALFieldDefn->GetAlternativeNameRef());
     619         377 :             if (!poGDALFieldDefn->GetComment().empty())
     620           3 :                 oField.SetComment(poGDALFieldDefn->GetComment());
     621             :         }
     622       35534 :         oField.SetSubType(eSubType);
     623       35534 :         oField.SetNullable(field->nullable());
     624       35534 :         m_poFeatureDefn->AddFieldDefn(&oField);
     625       35534 :         m_anMapFieldIndexToArrowColumn.push_back(path);
     626             :     }
     627             : 
     628       71832 :     return bTypeOK;
     629             : }
     630             : 
     631             : /************************************************************************/
     632             : /*                         CreateFieldFromSchema()                      */
     633             : /************************************************************************/
     634             : 
     635       10289 : inline void OGRArrowLayer::CreateFieldFromSchema(
     636             :     const std::shared_ptr<arrow::Field> &field, const std::vector<int> &path,
     637             :     const std::map<std::string, std::unique_ptr<OGRFieldDefn>>
     638             :         &oMapFieldNameToGDALSchemaFieldDefn)
     639             : {
     640       20578 :     OGRFieldDefn oField(field->name().c_str(), OFTString);
     641       10289 :     OGRFieldType eType = OFTString;
     642       10289 :     OGRFieldSubType eSubType = OFSTNone;
     643       10289 :     bool bTypeOK = true;
     644             : 
     645       20578 :     auto type = field->type();
     646       10289 :     if (type->id() == arrow::Type::DICTIONARY && path.size() == 1)
     647             :     {
     648             :         const auto dictionaryType =
     649         218 :             std::static_pointer_cast<arrow::DictionaryType>(field->type());
     650         218 :         auto indexType = dictionaryType->index_type();
     651         218 :         if (dictionaryType->value_type()->id() == arrow::Type::STRING &&
     652         109 :             IsIntegerArrowType(indexType->id()))
     653             :         {
     654         218 :             std::string osDomainName(field->name() + "Domain");
     655         109 :             m_poArrowDS->RegisterDomainName(osDomainName,
     656         109 :                                             m_poFeatureDefn->GetFieldCount());
     657         109 :             oField.SetDomainName(osDomainName);
     658         109 :             type = std::move(indexType);
     659             :         }
     660             :         else
     661             :         {
     662           0 :             bTypeOK = false;
     663             :         }
     664             :     }
     665             : 
     666       10289 :     if (type->id() == arrow::Type::STRUCT)
     667             :     {
     668         420 :         const auto subfields = field->Flatten();
     669         420 :         auto newpath = path;
     670         210 :         newpath.push_back(0);
     671         941 :         for (int j = 0; j < static_cast<int>(subfields.size()); j++)
     672             :         {
     673         731 :             const auto &subfield = subfields[j];
     674         731 :             newpath.back() = j;
     675         731 :             CreateFieldFromSchema(subfield, newpath,
     676             :                                   oMapFieldNameToGDALSchemaFieldDefn);
     677             :         }
     678             :     }
     679       10079 :     else if (bTypeOK)
     680             :     {
     681       10079 :         MapArrowTypeToOGR(type, field, oField, eType, eSubType, path,
     682             :                           oMapFieldNameToGDALSchemaFieldDefn);
     683             :     }
     684       10289 : }
     685             : 
     686             : /************************************************************************/
     687             : /*                       BuildDomainFromBatch()                         */
     688             : /************************************************************************/
     689             : 
     690          35 : inline std::unique_ptr<OGRFieldDomain> OGRArrowLayer::BuildDomainFromBatch(
     691             :     const std::string &osDomainName,
     692             :     const std::shared_ptr<arrow::RecordBatch> &poBatch, int iCol) const
     693             : {
     694          70 :     const auto array = poBatch->column(iCol);
     695          70 :     auto castArray = std::static_pointer_cast<arrow::DictionaryArray>(array);
     696          70 :     auto dict = castArray->dictionary();
     697          35 :     CPLAssert(dict->type_id() == arrow::Type::STRING);
     698          35 :     OGRFieldType eType = OFTInteger;
     699          35 :     const auto indexTypeId = castArray->dict_type()->index_type()->id();
     700          35 :     if (indexTypeId == arrow::Type::UINT32 ||
     701          35 :         indexTypeId == arrow::Type::UINT64 || indexTypeId == arrow::Type::INT64)
     702           0 :         eType = OFTInteger64;
     703          70 :     auto values = std::static_pointer_cast<arrow::StringArray>(dict);
     704          70 :     std::vector<OGRCodedValue> asValues;
     705          35 :     if (values->length() > INT_MAX)
     706             :     {
     707           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     708             :                  "BuildDomainFromBatch(): too many values");
     709           0 :         return nullptr;
     710             :     }
     711             :     try
     712             :     {
     713          35 :         asValues.reserve(static_cast<size_t>(values->length()));
     714             :     }
     715           0 :     catch (const std::bad_alloc &)
     716             :     {
     717           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     718             :                  "BuildDomainFromBatch(): out of memory");
     719           0 :         return nullptr;
     720             :     }
     721         140 :     for (int i = 0; i < static_cast<int>(values->length()); ++i)
     722             :     {
     723         105 :         if (!values->IsNull(i))
     724             :         {
     725             :             OGRCodedValue val;
     726         105 :             val.pszCode = CPLStrdup(CPLSPrintf("%d", i));
     727         105 :             val.pszValue = CPLStrdup(values->GetString(i).c_str());
     728         105 :             asValues.emplace_back(val);
     729             :         }
     730             :     }
     731          70 :     return std::make_unique<OGRCodedFieldDomain>(
     732         105 :         osDomainName, std::string(), eType, OFSTNone, std::move(asValues));
     733             : }
     734             : 
     735             : /************************************************************************/
     736             : /*                         GetStorageArray()                            */
     737             : /************************************************************************/
     738             : 
     739      186677 : static const arrow::Array *GetStorageArray(const arrow::Array *array)
     740             : {
     741      186677 :     if (array->type_id() == arrow::Type::EXTENSION)
     742             :     {
     743             :         auto extensionArray =
     744        1896 :             cpl::down_cast<const arrow::ExtensionArray *>(array);
     745        1896 :         array = extensionArray->storage().get();
     746             :     }
     747      186677 :     return array;
     748             : }
     749             : 
     750             : /************************************************************************/
     751             : /*               ComputeGeometryColumnTypeProcessBatch()                */
     752             : /************************************************************************/
     753             : 
     754         535 : inline OGRwkbGeometryType OGRArrowLayer::ComputeGeometryColumnTypeProcessBatch(
     755             :     const std::shared_ptr<arrow::RecordBatch> &poBatch, int iGeomCol,
     756             :     int iBatchCol, OGRwkbGeometryType eGeomType) const
     757             : {
     758        1070 :     const auto array = poBatch->column(iBatchCol);
     759         535 :     const auto storageArray = GetStorageArray(array.get());
     760             :     const auto castBinaryArray =
     761         535 :         (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB)
     762         535 :             ? dynamic_cast<const arrow::BinaryArray *>(storageArray)
     763         535 :             : nullptr;
     764             :     const auto castLargeBinaryArray =
     765         535 :         (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB)
     766         535 :             ? dynamic_cast<const arrow::LargeBinaryArray *>(storageArray)
     767         535 :             : nullptr;
     768             :     const auto castStringArray =
     769         535 :         (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT)
     770         535 :             ? dynamic_cast<const arrow::StringArray *>(storageArray)
     771         535 :             : nullptr;
     772             :     const auto castLargeStringArray =
     773         535 :         (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT)
     774         535 :             ? dynamic_cast<const arrow::LargeStringArray *>(storageArray)
     775         535 :             : nullptr;
     776        3208 :     for (int64_t i = 0; i < poBatch->num_rows(); i++)
     777             :     {
     778        2675 :         if (!storageArray->IsNull(i))
     779             :         {
     780        2152 :             OGRwkbGeometryType eThisGeomType = wkbNone;
     781        2152 :             if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB &&
     782             :                 castBinaryArray)
     783             :             {
     784        2040 :                 arrow::BinaryArray::offset_type out_length = 0;
     785        2040 :                 const uint8_t *data = castBinaryArray->GetValue(i, &out_length);
     786        2040 :                 if (out_length >= 5)
     787             :                 {
     788        2040 :                     OGRReadWKBGeometryType(data, wkbVariantIso, &eThisGeomType);
     789             :                 }
     790             :             }
     791         112 :             else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKB &&
     792             :                      castLargeBinaryArray)
     793             :             {
     794           0 :                 arrow::LargeBinaryArray::offset_type out_length = 0;
     795             :                 const uint8_t *data =
     796           0 :                     castLargeBinaryArray->GetValue(i, &out_length);
     797           0 :                 if (out_length >= 5)
     798             :                 {
     799           0 :                     OGRReadWKBGeometryType(data, wkbVariantIso, &eThisGeomType);
     800             :                 }
     801             :             }
     802         112 :             else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT &&
     803             :                      castStringArray)
     804             :             {
     805         224 :                 const auto osWKT = castStringArray->GetString(i);
     806         112 :                 if (!osWKT.empty())
     807             :                 {
     808         112 :                     OGRReadWKTGeometryType(osWKT.c_str(), &eThisGeomType);
     809             :                 }
     810             :             }
     811           0 :             else if (m_aeGeomEncoding[iGeomCol] == OGRArrowGeomEncoding::WKT &&
     812             :                      castLargeStringArray)
     813             :             {
     814           0 :                 const auto osWKT = castLargeStringArray->GetString(i);
     815           0 :                 if (!osWKT.empty())
     816             :                 {
     817           0 :                     OGRReadWKTGeometryType(osWKT.c_str(), &eThisGeomType);
     818             :                 }
     819             :             }
     820             : 
     821        2152 :             if (eThisGeomType != wkbNone)
     822             :             {
     823        2152 :                 if (eGeomType == wkbNone)
     824         531 :                     eGeomType = eThisGeomType;
     825        1621 :                 else if (wkbFlatten(eThisGeomType) == wkbFlatten(eGeomType))
     826             :                     ;
     827           6 :                 else if (wkbFlatten(eThisGeomType) == wkbMultiLineString &&
     828           0 :                          wkbFlatten(eGeomType) == wkbLineString)
     829             :                 {
     830           0 :                     eGeomType = OGR_GT_SetModifier(
     831             :                         wkbMultiLineString,
     832           0 :                         OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
     833           0 :                         OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
     834             :                 }
     835           8 :                 else if (wkbFlatten(eThisGeomType) == wkbLineString &&
     836           2 :                          wkbFlatten(eGeomType) == wkbMultiLineString)
     837             :                     ;
     838           6 :                 else if (wkbFlatten(eThisGeomType) == wkbMultiPolygon &&
     839           0 :                          wkbFlatten(eGeomType) == wkbPolygon)
     840             :                 {
     841           0 :                     eGeomType = OGR_GT_SetModifier(
     842             :                         wkbMultiPolygon,
     843           0 :                         OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
     844           0 :                         OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
     845             :                 }
     846          10 :                 else if (wkbFlatten(eThisGeomType) == wkbPolygon &&
     847           4 :                          wkbFlatten(eGeomType) == wkbMultiPolygon)
     848             :                     ;
     849             :                 else
     850           2 :                     return wkbUnknown;
     851             : 
     852        2150 :                 eGeomType = OGR_GT_SetModifier(
     853             :                     eGeomType,
     854        2150 :                     OGR_GT_HasZ(eThisGeomType) || OGR_GT_HasZ(eGeomType),
     855        2150 :                     OGR_GT_HasM(eThisGeomType) || OGR_GT_HasM(eGeomType));
     856             :             }
     857             :         }
     858             :     }
     859         533 :     return eGeomType;
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                           IsPointType()                              */
     864             : /************************************************************************/
     865             : 
     866         828 : static bool IsPointType(const std::shared_ptr<arrow::DataType> &type,
     867             :                         bool &bHasZOut, bool &bHasMOut)
     868             : {
     869         828 :     if (type->id() != arrow::Type::FIXED_SIZE_LIST)
     870         498 :         return false;
     871         660 :     auto poListType = std::static_pointer_cast<arrow::FixedSizeListType>(type);
     872         330 :     const int nOutDimensionality = poListType->list_size();
     873         660 :     const std::string osValueFieldName(poListType->value_field()->name());
     874         330 :     if (nOutDimensionality == 2)
     875             :     {
     876         114 :         bHasZOut = false;
     877         114 :         bHasMOut = false;
     878             :     }
     879         216 :     else if (nOutDimensionality == 3)
     880             :     {
     881         156 :         if (osValueFieldName == "xym")
     882             :         {
     883          60 :             bHasZOut = false;
     884          60 :             bHasMOut = true;
     885             :         }
     886             :         else /* if (osValueFieldName == "xyz" || osValueFieldName == "element") */
     887             :         {
     888          96 :             bHasMOut = false;
     889          96 :             bHasZOut = true;
     890             :         }
     891             :     }
     892          60 :     else if (nOutDimensionality == 4)
     893             :     {
     894          60 :         bHasMOut = true;
     895          60 :         bHasZOut = true;
     896             :     }
     897             :     else
     898             :     {
     899           0 :         return false;
     900             :     }
     901         330 :     return poListType->value_type()->id() == arrow::Type::DOUBLE;
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                         IsListOfPointType()                          */
     906             : /************************************************************************/
     907             : 
     908        1298 : static bool IsListOfPointType(const std::shared_ptr<arrow::DataType> &type,
     909             :                               int nDepth, bool &bHasZOut, bool &bHasMOut)
     910             : {
     911        1298 :     if (type->id() != arrow::Type::LIST)
     912           0 :         return false;
     913        1298 :     auto poListType = std::static_pointer_cast<arrow::ListType>(type);
     914             :     return nDepth == 1
     915        1298 :                ? IsPointType(poListType->value_type(), bHasZOut, bHasMOut)
     916         607 :                : IsListOfPointType(poListType->value_type(), nDepth - 1,
     917        1298 :                                    bHasZOut, bHasMOut);
     918             : }
     919             : 
     920             : /************************************************************************/
     921             : /*                         IsPointStructType()                          */
     922             : /************************************************************************/
     923             : 
     924         498 : static bool IsPointStructType(const std::shared_ptr<arrow::DataType> &type,
     925             :                               bool &bHasZOut, bool &bHasMOut)
     926             : {
     927         498 :     if (type->id() != arrow::Type::STRUCT)
     928           0 :         return false;
     929         996 :     auto poStructType = std::static_pointer_cast<arrow::StructType>(type);
     930         498 :     const int nNumFields = poStructType->num_fields();
     931         498 :     if (nNumFields < 2 || nNumFields > 4)
     932           0 :         return false;
     933         498 :     bHasZOut = false;
     934         498 :     bHasMOut = false;
     935         996 :     const auto poFieldX = poStructType->field(0);
     936         996 :     if (poFieldX->name() != "x" ||
     937         498 :         poFieldX->type()->id() != arrow::Type::DOUBLE)
     938           0 :         return false;
     939         996 :     const auto poFieldY = poStructType->field(1);
     940         996 :     if (poFieldY->name() != "y" ||
     941         498 :         poFieldY->type()->id() != arrow::Type::DOUBLE)
     942           0 :         return false;
     943         498 :     if (nNumFields == 2)
     944         288 :         return true;
     945         420 :     const auto poField2 = poStructType->field(2);
     946         210 :     if (poField2->type()->id() != arrow::Type::DOUBLE)
     947           0 :         return false;
     948         210 :     if (poField2->name() == "z")
     949             :     {
     950         204 :         bHasZOut = true;
     951         204 :         if (nNumFields == 4)
     952             :         {
     953           6 :             const auto poField3 = poStructType->field(3);
     954          12 :             if (poField3->name() != "m" ||
     955           6 :                 poField3->type()->id() != arrow::Type::DOUBLE)
     956           0 :                 return false;
     957           6 :             bHasMOut = true;
     958             :         }
     959             :     }
     960           6 :     else if (poField2->name() == "m")
     961             :     {
     962           6 :         bHasMOut = true;
     963             :     }
     964             :     else
     965             :     {
     966           0 :         return false;
     967             :     }
     968         210 :     return true;
     969             : }
     970             : 
     971             : /************************************************************************/
     972             : /*                    IsListOfPointStructType()                         */
     973             : /************************************************************************/
     974             : 
     975             : static bool
     976         790 : IsListOfPointStructType(const std::shared_ptr<arrow::DataType> &type,
     977             :                         int nDepth, bool &bHasZOut, bool &bHasMOut)
     978             : {
     979         790 :     if (type->id() != arrow::Type::LIST)
     980           0 :         return false;
     981         790 :     auto poListType = std::static_pointer_cast<arrow::ListType>(type);
     982             :     return nDepth == 1
     983         790 :                ? IsPointStructType(poListType->value_type(), bHasZOut, bHasMOut)
     984         376 :                : IsListOfPointStructType(poListType->value_type(), nDepth - 1,
     985         790 :                                          bHasZOut, bHasMOut);
     986             : }
     987             : 
     988             : /************************************************************************/
     989             : /*                        IsValidGeometryEncoding()                     */
     990             : /************************************************************************/
     991             : 
     992        1755 : inline bool OGRArrowLayer::IsValidGeometryEncoding(
     993             :     const std::shared_ptr<arrow::Field> &field, const std::string &osEncoding,
     994             :     bool bWarnIfUnknownEncoding, OGRwkbGeometryType &eGeomTypeOut,
     995             :     OGRArrowGeomEncoding &eOGRArrowGeomEncodingOut)
     996             : {
     997        1755 :     const auto &fieldName = field->name();
     998        3510 :     std::shared_ptr<arrow::DataType> fieldType = field->type();
     999        1755 :     auto fieldTypeId = fieldType->id();
    1000             : 
    1001        1755 :     if (fieldTypeId == arrow::Type::EXTENSION)
    1002             :     {
    1003             :         auto extensionType =
    1004         127 :             cpl::down_cast<arrow::ExtensionType *>(fieldType.get());
    1005         127 :         fieldType = extensionType->storage_type();
    1006         127 :         fieldTypeId = fieldType->id();
    1007             :     }
    1008             : 
    1009        1755 :     eGeomTypeOut = wkbUnknown;
    1010             : 
    1011        3455 :     if (osEncoding == "WKT" ||  // As used in Parquet geo metadata
    1012        1700 :         osEncoding ==
    1013        3455 :             "ogc.wkt" ||  // As used in ARROW:extension:name field metadata
    1014        1700 :         osEncoding ==
    1015             :             "geoarrow.wkt"  // As used in ARROW:extension:name field metadata
    1016             :     )
    1017             :     {
    1018          55 :         if (fieldTypeId != arrow::Type::LARGE_STRING &&
    1019             :             fieldTypeId != arrow::Type::STRING)
    1020             :         {
    1021           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1022             :                      "Geometry column %s has a non String type: %s. "
    1023             :                      "Handling it as a regular field",
    1024           0 :                      fieldName.c_str(), fieldType->ToString().c_str());
    1025           0 :             return false;
    1026             :         }
    1027          55 :         eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKT;
    1028          55 :         return true;
    1029             :     }
    1030             : 
    1031        2658 :     if (osEncoding == "WKB" ||  // As used in Parquet geo metadata
    1032         958 :         osEncoding ==
    1033        2658 :             "ogc.wkb" ||  // As used in ARROW:extension:name field metadata
    1034         958 :         osEncoding ==
    1035             :             "geoarrow.wkb"  // As used in ARROW:extension:name field metadata
    1036             :     )
    1037             :     {
    1038         743 :         if (fieldTypeId != arrow::Type::LARGE_BINARY &&
    1039             :             fieldTypeId != arrow::Type::BINARY)
    1040             :         {
    1041           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1042             :                      "Geometry column %s has a non Binary type: %s. "
    1043             :                      "Handling it as a regular field",
    1044           0 :                      fieldName.c_str(), fieldType->ToString().c_str());
    1045           0 :             return false;
    1046             :         }
    1047         743 :         eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::WKB;
    1048         743 :         return true;
    1049             :     }
    1050             : 
    1051         957 :     bool bHasZ = false;
    1052         957 :     bool bHasM = false;
    1053         957 :     if (osEncoding == "geoarrow.point" || osEncoding == "point")
    1054             :     {
    1055         137 :         if (IsPointType(fieldType, bHasZ, bHasM))
    1056             :         {
    1057          53 :             eOGRArrowGeomEncodingOut = OGRArrowGeomEncoding::GEOARROW_FSL_POINT;
    1058             :         }
    1059          84 :         else if (IsPointStructType(fieldType, bHasZ, bHasM))
    1060             :         {
    1061          84 :             eOGRArrowGeomEncodingOut =
    1062             :                 OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT;
    1063             :         }
    1064             :         else
    1065             :         {
    1066           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1067             :                      "Geometry column %s has a type != fixed_size_list<xy: "
    1068             :                      "double>[2]> and != struct<x: double, y: double>: %s. "
    1069             :                      "Handling it as a regular field",
    1070           0 :                      fieldName.c_str(), fieldType->name().c_str());
    1071           0 :             return false;
    1072             :         }
    1073         137 :         eGeomTypeOut = OGR_GT_SetModifier(wkbPoint, static_cast<int>(bHasZ),
    1074             :                                           static_cast<int>(bHasM));
    1075         137 :         return true;
    1076             :     }
    1077             : 
    1078         820 :     else if (osEncoding == "geoarrow.linestring" || osEncoding == "linestring")
    1079             :     {
    1080         122 :         if (IsListOfPointType(fieldType, 1, bHasZ, bHasM))
    1081             :         {
    1082          52 :             eOGRArrowGeomEncodingOut =
    1083             :                 OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING;
    1084             :         }
    1085          70 :         else if (IsListOfPointStructType(fieldType, 1, bHasZ, bHasM))
    1086             :         {
    1087          70 :             eOGRArrowGeomEncodingOut =
    1088             :                 OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING;
    1089             :         }
    1090             :         else
    1091             :         {
    1092           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1093             :                      "Geometry column %s has a type != fixed_size_list<xy: "
    1094             :                      "double>[2]> and != list<element: struct<x: double, y: "
    1095             :                      "double>>: %s. "
    1096             :                      "Handling it as a regular field",
    1097           0 :                      fieldName.c_str(), fieldType->ToString().c_str());
    1098           0 :             return false;
    1099             :         }
    1100         122 :         eGeomTypeOut = OGR_GT_SetModifier(
    1101             :             wkbLineString, static_cast<int>(bHasZ), static_cast<int>(bHasM));
    1102         122 :         return true;
    1103             :     }
    1104             : 
    1105         698 :     else if (osEncoding == "geoarrow.polygon" || osEncoding == "polygon")
    1106             :     {
    1107         165 :         if (IsListOfPointType(fieldType, 2, bHasZ, bHasM))
    1108             :         {
    1109          63 :             eOGRArrowGeomEncodingOut =
    1110             :                 OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON;
    1111             :         }
    1112         102 :         else if (IsListOfPointStructType(fieldType, 2, bHasZ, bHasM))
    1113             :         {
    1114         102 :             eOGRArrowGeomEncodingOut =
    1115             :                 OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON;
    1116             :         }
    1117             :         else
    1118             :         {
    1119           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1120             :                      "Geometry column %s has a type != list<vertices: "
    1121             :                      "fixed_size_list<xy: double>[2]>> and != list<element: "
    1122             :                      "list<element: struct<x: double, y: double>>>: %s. "
    1123             :                      "Handling it as a regular field",
    1124           0 :                      fieldName.c_str(), fieldType->ToString().c_str());
    1125           0 :             return false;
    1126             :         }
    1127         165 :         eGeomTypeOut = OGR_GT_SetModifier(wkbPolygon, static_cast<int>(bHasZ),
    1128             :                                           static_cast<int>(bHasM));
    1129         165 :         return true;
    1130             :     }
    1131             : 
    1132         533 :     else if (osEncoding == "geoarrow.multipoint" || osEncoding == "multipoint")
    1133             :     {
    1134         122 :         if (IsListOfPointType(fieldType, 1, bHasZ, bHasM))
    1135             :         {
    1136          52 :             eOGRArrowGeomEncodingOut =
    1137             :                 OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT;
    1138             :         }
    1139          70 :         else if (IsListOfPointStructType(fieldType, 1, bHasZ, bHasM))
    1140             :         {
    1141          70 :             eOGRArrowGeomEncodingOut =
    1142             :                 OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT;
    1143             :         }
    1144             :         else
    1145             :         {
    1146           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1147             :                      "Geometry column %s has a type != fixed_size_list<xy: "
    1148             :                      "double>[2]> and != list<element: struct<x: double, y: "
    1149             :                      "double>>: %s. "
    1150             :                      "Handling it as a regular field",
    1151           0 :                      fieldName.c_str(), fieldType->ToString().c_str());
    1152           0 :             return false;
    1153             :         }
    1154         122 :         eGeomTypeOut = OGR_GT_SetModifier(
    1155             :             wkbMultiPoint, static_cast<int>(bHasZ), static_cast<int>(bHasM));
    1156         122 :         return true;
    1157             :     }
    1158             : 
    1159         770 :     else if (osEncoding == "geoarrow.multilinestring" ||
    1160         359 :              osEncoding == "multilinestring")
    1161             :     {
    1162         122 :         if (IsListOfPointType(fieldType, 2, bHasZ, bHasM))
    1163             :         {
    1164          52 :             eOGRArrowGeomEncodingOut =
    1165             :                 OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING;
    1166             :         }
    1167          70 :         else if (IsListOfPointStructType(fieldType, 2, bHasZ, bHasM))
    1168             :         {
    1169          70 :             eOGRArrowGeomEncodingOut =
    1170             :                 OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING;
    1171             :         }
    1172             :         else
    1173             :         {
    1174           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1175             :                      "Geometry column %s has a type != list<vertices: "
    1176             :                      "fixed_size_list<xy: double>[2]>> and != list<element: "
    1177             :                      "list<element: struct<x: double, y: double>>>: %s. "
    1178             :                      "Handling it as a regular field",
    1179           0 :                      fieldName.c_str(), fieldType->ToString().c_str());
    1180           0 :             return false;
    1181             :         }
    1182         122 :         eGeomTypeOut =
    1183         122 :             OGR_GT_SetModifier(wkbMultiLineString, static_cast<int>(bHasZ),
    1184             :                                static_cast<int>(bHasM));
    1185         122 :         return true;
    1186             :     }
    1187             : 
    1188         520 :     else if (osEncoding == "geoarrow.multipolygon" ||
    1189         231 :              osEncoding == "multipolygon")
    1190             :     {
    1191         160 :         if (IsListOfPointType(fieldType, 3, bHasZ, bHasM))
    1192             :         {
    1193          58 :             eOGRArrowGeomEncodingOut =
    1194             :                 OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON;
    1195             :         }
    1196         102 :         else if (IsListOfPointStructType(fieldType, 3, bHasZ, bHasM))
    1197             :         {
    1198         102 :             eOGRArrowGeomEncodingOut =
    1199             :                 OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON;
    1200             :         }
    1201             :         else
    1202             :         {
    1203           0 :             CPLError(
    1204             :                 CE_Warning, CPLE_AppDefined,
    1205             :                 "Geometry column %s has a type != list<polygons: list<rings: "
    1206             :                 "list<vertices: fixed_size_list<xy: double>[2]>>> and != "
    1207             :                 "list<element: list<element: list<element: struct<x: double, "
    1208             :                 "y: double>>>>: %s. "
    1209             :                 "Handling it as a regular field",
    1210           0 :                 fieldName.c_str(), fieldType->ToString().c_str());
    1211           0 :             return false;
    1212             :         }
    1213         160 :         eGeomTypeOut = OGR_GT_SetModifier(
    1214             :             wkbMultiPolygon, static_cast<int>(bHasZ), static_cast<int>(bHasM));
    1215         160 :         return true;
    1216             :     }
    1217             : 
    1218         129 :     if (bWarnIfUnknownEncoding)
    1219             :     {
    1220           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1221             :                  "Geometry column %s uses a unhandled encoding: %s. "
    1222             :                  "Handling it as a regular field",
    1223             :                  fieldName.c_str(), osEncoding.c_str());
    1224             :     }
    1225         129 :     return false;
    1226             : }
    1227             : 
    1228             : /************************************************************************/
    1229             : /*                    GetGeometryTypeFromString()                       */
    1230             : /************************************************************************/
    1231             : 
    1232             : inline OGRwkbGeometryType
    1233         263 : OGRArrowLayer::GetGeometryTypeFromString(const std::string &osType)
    1234             : {
    1235         263 :     OGRwkbGeometryType eGeomType = wkbUnknown;
    1236         263 :     OGRReadWKTGeometryType(osType.c_str(), &eGeomType);
    1237         263 :     if (eGeomType == wkbUnknown && !osType.empty())
    1238             :     {
    1239           0 :         CPLDebug("ARROW", "Unknown geometry type: %s", osType.c_str());
    1240             :     }
    1241         263 :     return eGeomType;
    1242             : }
    1243             : 
    1244             : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
    1245             :                                      const size_t nIdx);
    1246             : 
    1247             : /************************************************************************/
    1248             : /*                               AddToArray()                           */
    1249             : /************************************************************************/
    1250             : 
    1251             : template <class Container>
    1252       26735 : void AddToContainer(Container &oContainer, const arrow::Array *array,
    1253             :                     const size_t nIdx)
    1254             : {
    1255       26735 :     switch (array->type()->id())
    1256             :     {
    1257         727 :         case arrow::Type::BOOL:
    1258             :         {
    1259         727 :             oContainer.Add(
    1260         727 :                 static_cast<const arrow::BooleanArray *>(array)->Value(nIdx));
    1261         727 :             break;
    1262             :         }
    1263         734 :         case arrow::Type::UINT8:
    1264             :         {
    1265         734 :             oContainer.Add(
    1266         734 :                 static_cast<const arrow::UInt8Array *>(array)->Value(nIdx));
    1267         734 :             break;
    1268             :         }
    1269         734 :         case arrow::Type::INT8:
    1270             :         {
    1271         734 :             oContainer.Add(
    1272         734 :                 static_cast<const arrow::Int8Array *>(array)->Value(nIdx));
    1273         734 :             break;
    1274             :         }
    1275         734 :         case arrow::Type::UINT16:
    1276             :         {
    1277         734 :             oContainer.Add(
    1278         734 :                 static_cast<const arrow::UInt16Array *>(array)->Value(nIdx));
    1279         734 :             break;
    1280             :         }
    1281         734 :         case arrow::Type::INT16:
    1282             :         {
    1283         734 :             oContainer.Add(
    1284         734 :                 static_cast<const arrow::Int16Array *>(array)->Value(nIdx));
    1285         734 :             break;
    1286             :         }
    1287         734 :         case arrow::Type::INT32:
    1288             :         {
    1289         734 :             oContainer.Add(
    1290         725 :                 static_cast<const arrow::Int32Array *>(array)->Value(nIdx));
    1291         734 :             break;
    1292             :         }
    1293         117 :         case arrow::Type::UINT32:
    1294             :         {
    1295         117 :             oContainer.Add(static_cast<GInt64>(
    1296         117 :                 static_cast<const arrow::UInt32Array *>(array)->Value(nIdx)));
    1297         117 :             break;
    1298             :         }
    1299        4873 :         case arrow::Type::INT64:
    1300             :         {
    1301        4873 :             oContainer.Add(static_cast<GInt64>(
    1302        4873 :                 static_cast<const arrow::Int64Array *>(array)->Value(nIdx)));
    1303        4873 :             break;
    1304             :         }
    1305         734 :         case arrow::Type::UINT64:
    1306             :         {
    1307         734 :             oContainer.Add(static_cast<uint64_t>(
    1308         734 :                 static_cast<const arrow::UInt64Array *>(array)->Value(nIdx)));
    1309         734 :             break;
    1310             :         }
    1311         330 :         case arrow::Type::HALF_FLOAT:
    1312             :         {
    1313             :             const uint16_t nFloat16 =
    1314         330 :                 static_cast<const arrow::HalfFloatArray *>(array)->Value(nIdx);
    1315         330 :             uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
    1316             :             float f;
    1317         330 :             memcpy(&f, &nFloat32, sizeof(nFloat32));
    1318         330 :             oContainer.Add(f);
    1319         330 :             break;
    1320             :         }
    1321         734 :         case arrow::Type::FLOAT:
    1322             :         {
    1323         734 :             oContainer.Add(
    1324         734 :                 static_cast<const arrow::FloatArray *>(array)->Value(nIdx));
    1325         734 :             break;
    1326             :         }
    1327        3948 :         case arrow::Type::DOUBLE:
    1328             :         {
    1329        3948 :             oContainer.Add(
    1330        3939 :                 static_cast<const arrow::DoubleArray *>(array)->Value(nIdx));
    1331        3948 :             break;
    1332             :         }
    1333             : 
    1334             : #if ARROW_VERSION_MAJOR >= 18
    1335           0 :         case arrow::Type::DECIMAL32:
    1336             :         {
    1337           0 :             oContainer.Add(
    1338           0 :                 CPLAtof(static_cast<const arrow::Decimal32Array *>(array)
    1339             :                             ->FormatValue(nIdx)
    1340             :                             .c_str()));
    1341           0 :             break;
    1342             :         }
    1343           0 :         case arrow::Type::DECIMAL64:
    1344             :         {
    1345           0 :             oContainer.Add(
    1346           0 :                 CPLAtof(static_cast<const arrow::Decimal64Array *>(array)
    1347             :                             ->FormatValue(nIdx)
    1348             :                             .c_str()));
    1349           0 :             break;
    1350             :         }
    1351             : #endif
    1352        1343 :         case arrow::Type::DECIMAL128:
    1353             :         {
    1354        1343 :             oContainer.Add(
    1355        1339 :                 CPLAtof(static_cast<const arrow::Decimal128Array *>(array)
    1356             :                             ->FormatValue(nIdx)
    1357             :                             .c_str()));
    1358        1343 :             break;
    1359             :         }
    1360         109 :         case arrow::Type::DECIMAL256:
    1361             :         {
    1362         109 :             oContainer.Add(
    1363         105 :                 CPLAtof(static_cast<const arrow::Decimal256Array *>(array)
    1364             :                             ->FormatValue(nIdx)
    1365             :                             .c_str()));
    1366         109 :             break;
    1367             :         }
    1368        3579 :         case arrow::Type::STRING:
    1369             :         {
    1370        3579 :             oContainer.Add(
    1371             :                 static_cast<const arrow::StringArray *>(array)->GetString(
    1372             :                     nIdx));
    1373        3579 :             break;
    1374             :         }
    1375         109 :         case arrow::Type::LARGE_STRING:
    1376             :         {
    1377         109 :             oContainer.Add(
    1378             :                 static_cast<const arrow::LargeStringArray *>(array)->GetString(
    1379             :                     nIdx));
    1380         109 :             break;
    1381             :         }
    1382             : #if ARROW_VERSION_MAJOR >= 15
    1383           4 :         case arrow::Type::STRING_VIEW:
    1384             :         {
    1385           4 :             oContainer.Add(
    1386             :                 static_cast<const arrow::StringViewArray *>(array)->GetString(
    1387             :                     nIdx));
    1388           4 :             break;
    1389             :         }
    1390             : #endif
    1391        6454 :         case arrow::Type::LIST:
    1392             :         case arrow::Type::LARGE_LIST:
    1393             :         case arrow::Type::FIXED_SIZE_LIST:
    1394             :         case arrow::Type::MAP:
    1395             :         case arrow::Type::STRUCT:
    1396             :         {
    1397        6454 :             oContainer.Add(GetObjectAsJSON(array, nIdx));
    1398        6454 :             break;
    1399             :         }
    1400             : 
    1401           4 :         case arrow::Type::BINARY:
    1402             :         {
    1403           4 :             const auto castArray =
    1404             :                 static_cast<const arrow::BinaryArray *>(array);
    1405           4 :             int length = 0;
    1406           4 :             const uint8_t *data = castArray->GetValue(nIdx, &length);
    1407           4 :             bool bIsASCII = true;
    1408           9 :             for (int i = 0; i < length && bIsASCII; ++i)
    1409           5 :                 bIsASCII = data[i] >= 32 && data[i] <= 127;
    1410           4 :             if (bIsASCII)
    1411             :             {
    1412           3 :                 oContainer.Add(
    1413             :                     std::string(reinterpret_cast<const char *>(data), length));
    1414             :             }
    1415             :             else
    1416             :             {
    1417           1 :                 char *pszBase64 = CPLBase64Encode(length, data);
    1418           1 :                 oContainer.Add(std::string("base64:").append(pszBase64));
    1419           1 :                 CPLFree(pszBase64);
    1420             :             }
    1421           4 :             break;
    1422             :         }
    1423             : 
    1424             : #if ARROW_VERSION_MAJOR >= 15
    1425           0 :         case arrow::Type::BINARY_VIEW:
    1426             :         {
    1427           0 :             const auto castArray =
    1428             :                 static_cast<const arrow::BinaryViewArray *>(array);
    1429           0 :             const auto view = castArray->GetView(nIdx);
    1430           0 :             if (view.size() <= INT_MAX)
    1431             :             {
    1432           0 :                 const int length = static_cast<int>(view.size());
    1433           0 :                 const char *data = view.data();
    1434           0 :                 bool bIsASCII = true;
    1435           0 :                 for (int i = 0; i < length && bIsASCII; ++i)
    1436           0 :                     bIsASCII =
    1437           0 :                         data[i] >= 32 && static_cast<unsigned>(data[i]) <= 127;
    1438           0 :                 if (bIsASCII)
    1439             :                 {
    1440           0 :                     oContainer.Add(std::string(view));
    1441             :                 }
    1442             :                 else
    1443             :                 {
    1444           0 :                     char *pszBase64 = CPLBase64Encode(
    1445             :                         length, reinterpret_cast<const GByte *>(data));
    1446           0 :                     oContainer.Add(std::string("base64:").append(pszBase64));
    1447           0 :                     CPLFree(pszBase64);
    1448             :                 }
    1449             :             }
    1450             :             else
    1451             :             {
    1452           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large binary view");
    1453             :             }
    1454           0 :             break;
    1455             :         }
    1456             : #endif
    1457             : 
    1458           0 :         default:
    1459             :         {
    1460           0 :             CPLDebug("ARROW", "AddToContainer(): unexpected data type %s",
    1461           0 :                      array->type()->ToString().c_str());
    1462           0 :             break;
    1463             :         }
    1464             :     }
    1465       26735 : }
    1466             : 
    1467             : /************************************************************************/
    1468             : /*                               AddToArray()                           */
    1469             : /************************************************************************/
    1470             : 
    1471        6349 : static void AddToArray(CPLJSONArray &oArray, const arrow::Array *array,
    1472             :                        const size_t nIdx)
    1473             : {
    1474        6349 :     AddToContainer(oArray, array, nIdx);
    1475        6349 : }
    1476             : 
    1477             : /************************************************************************/
    1478             : /*                         GetListAsJSON()                              */
    1479             : /************************************************************************/
    1480             : 
    1481             : template <class ArrowType>
    1482        4929 : static CPLJSONArray GetListAsJSON(const ArrowType *array,
    1483             :                                   const size_t nIdxInArray)
    1484             : {
    1485        9858 :     const auto values = array->values();
    1486        4929 :     const auto nIdxStart = array->value_offset(nIdxInArray);
    1487        4929 :     const auto nCount = array->value_length(nIdxInArray);
    1488        4929 :     CPLJSONArray oArray;
    1489       13608 :     for (auto k = decltype(nCount){0}; k < nCount; k++)
    1490             :     {
    1491        8679 :         if (values->IsNull(nIdxStart + k))
    1492        2330 :             oArray.AddNull();
    1493             :         else
    1494        6349 :             AddToArray(oArray, values.get(),
    1495        6349 :                        static_cast<size_t>(nIdxStart + k));
    1496             :     }
    1497        9858 :     return oArray;
    1498             : }
    1499             : 
    1500             : /************************************************************************/
    1501             : /*                              AddToDict()                             */
    1502             : /************************************************************************/
    1503             : 
    1504             : namespace
    1505             : {
    1506             : struct ContainerAdapter
    1507             : {
    1508             :     CPLJSONObject &m_oDict;
    1509             :     const std::string m_osKey;
    1510             : 
    1511       20386 :     ContainerAdapter(CPLJSONObject &oDictIn, const std::string &osKeyIn)
    1512       20386 :         : m_oDict(oDictIn), m_osKey(osKeyIn)
    1513             :     {
    1514       20386 :     }
    1515             : 
    1516       20386 :     template <class T> void Add(const T &v)
    1517             :     {
    1518       20386 :         m_oDict.Add(m_osKey, v);
    1519       20386 :     }
    1520             : };
    1521             : }  // namespace
    1522             : 
    1523       20386 : static void AddToDict(CPLJSONObject &oDict, const std::string &osKey,
    1524             :                       const arrow::Array *array, const size_t nIdx)
    1525             : {
    1526       40772 :     ContainerAdapter dictAdapter(oDict, osKey);
    1527       20386 :     AddToContainer(dictAdapter, array, nIdx);
    1528       20386 : }
    1529             : 
    1530             : /************************************************************************/
    1531             : /*                         GetMapAsJSON()                               */
    1532             : /************************************************************************/
    1533             : 
    1534             : template <class KeyArrayType>
    1535       24926 : static CPLJSONObject GetMapAsJSON(const arrow::Array *array,
    1536             :                                   const size_t nIdxInArray)
    1537             : {
    1538       24926 :     const auto mapArray = static_cast<const arrow::MapArray *>(array);
    1539       49852 :     const auto keys = std::static_pointer_cast<KeyArrayType>(mapArray->keys());
    1540       49852 :     const auto values = mapArray->items();
    1541       24926 :     const auto nIdxStart = mapArray->value_offset(nIdxInArray);
    1542       24926 :     const int nCount = mapArray->value_length(nIdxInArray);
    1543       24926 :     CPLJSONObject oRoot;
    1544       45342 :     for (int k = 0; k < nCount; k++)
    1545             :     {
    1546       20416 :         if (!keys->IsNull(nIdxStart + k))
    1547             :         {
    1548       40832 :             const auto osKey = keys->GetString(nIdxStart + k);
    1549       20416 :             if (!values->IsNull(nIdxStart + k))
    1550       13597 :                 AddToDict(oRoot, osKey, values.get(), nIdxStart + k);
    1551             :             else
    1552        6819 :                 oRoot.AddNull(osKey);
    1553             :         }
    1554             :     }
    1555       49852 :     return oRoot;
    1556             : }
    1557             : 
    1558       24926 : static CPLJSONObject GetMapAsJSON(const arrow::Array *array,
    1559             :                                   const size_t nIdxInArray)
    1560             : {
    1561       24926 :     const auto mapArray = static_cast<const arrow::MapArray *>(array);
    1562       24926 :     const auto eKeyType = mapArray->keys()->type()->id();
    1563       24926 :     if (eKeyType == arrow::Type::STRING)
    1564       24924 :         return GetMapAsJSON<arrow::StringArray>(array, nIdxInArray);
    1565             : #if ARROW_VERSION_MAJOR >= 15
    1566           2 :     else if (eKeyType == arrow::Type::STRING_VIEW)
    1567           2 :         return GetMapAsJSON<arrow::StringViewArray>(array, nIdxInArray);
    1568             : #endif
    1569             :     else
    1570             :     {
    1571           0 :         CPLAssert(false);
    1572             :         return CPLJSONObject();
    1573             :     }
    1574             : }
    1575             : 
    1576             : /************************************************************************/
    1577             : /*                        GetStructureAsJSON()                          */
    1578             : /************************************************************************/
    1579             : 
    1580        3541 : static CPLJSONObject GetStructureAsJSON(const arrow::Array *array,
    1581             :                                         const size_t nIdxInArray)
    1582             : {
    1583        3541 :     CPLJSONObject oRoot;
    1584        3541 :     const auto structArray = static_cast<const arrow::StructArray *>(array);
    1585        7082 :     const auto structArrayType = structArray->type();
    1586       13559 :     for (int i = 0; i < structArrayType->num_fields(); ++i)
    1587             :     {
    1588       20036 :         const auto field = structArray->field(i);
    1589       10018 :         if (!field->IsNull(nIdxInArray))
    1590             :         {
    1591        6789 :             AddToDict(oRoot, structArrayType->field(i)->name(), field.get(),
    1592             :                       nIdxInArray);
    1593             :         }
    1594             :         else
    1595        3229 :             oRoot.AddNull(structArrayType->field(i)->name());
    1596             :     }
    1597             : 
    1598        7082 :     return oRoot;
    1599             : }
    1600             : 
    1601             : /************************************************************************/
    1602             : /*                        GetObjectAsJSON()                             */
    1603             : /************************************************************************/
    1604             : 
    1605        6454 : static CPLJSONObject GetObjectAsJSON(const arrow::Array *array,
    1606             :                                      const size_t nIdxInArray)
    1607             : {
    1608        6454 :     switch (array->type()->id())
    1609             :     {
    1610          26 :         case arrow::Type::MAP:
    1611          26 :             return GetMapAsJSON(array, nIdxInArray);
    1612        2604 :         case arrow::Type::LIST:
    1613        5208 :             return GetListAsJSON(static_cast<const arrow::ListArray *>(array),
    1614        2604 :                                  nIdxInArray);
    1615         108 :         case arrow::Type::LARGE_LIST:
    1616         216 :             return GetListAsJSON(
    1617         108 :                 static_cast<const arrow::LargeListArray *>(array), nIdxInArray);
    1618         175 :         case arrow::Type::FIXED_SIZE_LIST:
    1619         350 :             return GetListAsJSON(
    1620             :                 static_cast<const arrow::FixedSizeListArray *>(array),
    1621         175 :                 nIdxInArray);
    1622        3541 :         case arrow::Type::STRUCT:
    1623        3541 :             return GetStructureAsJSON(array, nIdxInArray);
    1624           0 :         default:
    1625             :         {
    1626           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1627             :                      "GetObjectAsJSON(): unhandled value format: %s",
    1628           0 :                      array->type()->ToString().c_str());
    1629           0 :             return CPLJSONObject();
    1630             :         }
    1631             :     }
    1632             : }
    1633             : 
    1634             : template <class OGRType, class ArrowType, class ArrayType>
    1635       32323 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
    1636             :                      const ArrayType *array)
    1637             : {
    1638       64646 :     const auto values = std::static_pointer_cast<ArrowType>(array->values());
    1639       32323 :     const auto nIdxStart = array->value_offset(nIdxInArray);
    1640       32323 :     const int nCount = array->value_length(nIdxInArray);
    1641       64646 :     std::vector<OGRType> aValues;
    1642       32323 :     aValues.reserve(nCount);
    1643       96781 :     for (int k = 0; k < nCount; k++)
    1644             :     {
    1645       64458 :         aValues.push_back(static_cast<OGRType>(values->Value(nIdxStart + k)));
    1646             :     }
    1647       32323 :     poFeature->SetField(i, nCount, aValues.data());
    1648       32323 : }
    1649             : 
    1650             : template <class ArrowType, class ArrayType>
    1651        7203 : static void ReadListDouble(OGRFeature *poFeature, int i, int64_t nIdxInArray,
    1652             :                            const ArrayType *array)
    1653             : {
    1654       14406 :     const auto values = std::static_pointer_cast<ArrowType>(array->values());
    1655        7203 :     const auto rawValues = values->raw_values();
    1656        7203 :     const auto nIdxStart = array->value_offset(nIdxInArray);
    1657        7203 :     const int nCount = array->value_length(nIdxInArray);
    1658       14406 :     std::vector<double> aValues;
    1659        7203 :     aValues.reserve(nCount);
    1660       21099 :     for (int k = 0; k < nCount; k++)
    1661             :     {
    1662       13896 :         if (values->IsNull(nIdxStart + k))
    1663        2948 :             aValues.push_back(std::numeric_limits<double>::quiet_NaN());
    1664             :         else
    1665       10948 :             aValues.push_back(rawValues[nIdxStart + k]);
    1666             :     }
    1667        7203 :     poFeature->SetField(i, nCount, aValues.data());
    1668        7203 : }
    1669             : 
    1670             : template <class ArrayType>
    1671       49681 : static void ReadList(OGRFeature *poFeature, int i, int64_t nIdxInArray,
    1672             :                      const ArrayType *array, arrow::Type::type valueTypeId)
    1673             : {
    1674       49681 :     switch (valueTypeId)
    1675             :     {
    1676        3405 :         case arrow::Type::BOOL:
    1677             :         {
    1678        3405 :             ReadList<int, arrow::BooleanArray>(poFeature, i, nIdxInArray,
    1679             :                                                array);
    1680        3405 :             break;
    1681             :         }
    1682        3235 :         case arrow::Type::UINT8:
    1683             :         {
    1684        3235 :             ReadList<int, arrow::UInt8Array>(poFeature, i, nIdxInArray, array);
    1685        3235 :             break;
    1686             :         }
    1687        3235 :         case arrow::Type::INT8:
    1688             :         {
    1689        3235 :             ReadList<int, arrow::Int8Array>(poFeature, i, nIdxInArray, array);
    1690        3235 :             break;
    1691             :         }
    1692        3235 :         case arrow::Type::UINT16:
    1693             :         {
    1694        3235 :             ReadList<int, arrow::UInt16Array>(poFeature, i, nIdxInArray, array);
    1695        3235 :             break;
    1696             :         }
    1697        3235 :         case arrow::Type::INT16:
    1698             :         {
    1699        3235 :             ReadList<int, arrow::Int16Array>(poFeature, i, nIdxInArray, array);
    1700        3235 :             break;
    1701             :         }
    1702        4085 :         case arrow::Type::INT32:
    1703             :         {
    1704        4085 :             ReadList<int, arrow::Int32Array>(poFeature, i, nIdxInArray, array);
    1705        4085 :             break;
    1706             :         }
    1707         429 :         case arrow::Type::UINT32:
    1708             :         {
    1709         429 :             ReadList<GIntBig, arrow::UInt32Array>(poFeature, i, nIdxInArray,
    1710             :                                                   array);
    1711         429 :             break;
    1712             :         }
    1713        8229 :         case arrow::Type::INT64:
    1714             :         {
    1715        8229 :             ReadList<GIntBig, arrow::Int64Array>(poFeature, i, nIdxInArray,
    1716             :                                                  array);
    1717        8229 :             break;
    1718             :         }
    1719        3235 :         case arrow::Type::UINT64:
    1720             :         {
    1721        3235 :             ReadList<double, arrow::UInt64Array>(poFeature, i, nIdxInArray,
    1722             :                                                  array);
    1723        3235 :             break;
    1724             :         }
    1725         194 :         case arrow::Type::HALF_FLOAT:
    1726             :         {
    1727         388 :             const auto values = std::static_pointer_cast<arrow::HalfFloatArray>(
    1728             :                 array->values());
    1729         194 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1730         194 :             const int nCount = array->value_length(nIdxInArray);
    1731         388 :             std::vector<double> aValues;
    1732         194 :             aValues.reserve(nCount);
    1733         547 :             for (int k = 0; k < nCount; k++)
    1734             :             {
    1735         353 :                 if (values->IsNull(nIdxStart + k))
    1736         128 :                     aValues.push_back(std::numeric_limits<double>::quiet_NaN());
    1737             :                 else
    1738             :                 {
    1739         225 :                     const uint16_t nFloat16 = values->Value(nIdxStart + k);
    1740         225 :                     uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
    1741             :                     float f;
    1742         225 :                     memcpy(&f, &nFloat32, sizeof(nFloat32));
    1743         225 :                     aValues.push_back(f);
    1744             :                 }
    1745             :             }
    1746         194 :             poFeature->SetField(i, nCount, aValues.data());
    1747         194 :             break;
    1748             :         }
    1749        3466 :         case arrow::Type::FLOAT:
    1750             :         {
    1751        3466 :             ReadListDouble<arrow::FloatArray>(poFeature, i, nIdxInArray, array);
    1752        3466 :             break;
    1753             :         }
    1754        3737 :         case arrow::Type::DOUBLE:
    1755             :         {
    1756        3737 :             ReadListDouble<arrow::DoubleArray>(poFeature, i, nIdxInArray,
    1757             :                                                array);
    1758        3737 :             break;
    1759             :         }
    1760             : 
    1761             : #if ARROW_VERSION_MAJOR >= 18
    1762           0 :         case arrow::Type::DECIMAL32:
    1763             :         {
    1764           0 :             const auto values = std::static_pointer_cast<arrow::Decimal32Array>(
    1765             :                 array->values());
    1766           0 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1767           0 :             const int nCount = array->value_length(nIdxInArray);
    1768           0 :             std::vector<double> aValues;
    1769           0 :             aValues.reserve(nCount);
    1770           0 :             for (int k = 0; k < nCount; k++)
    1771             :             {
    1772           0 :                 if (values->IsNull(nIdxStart + k))
    1773           0 :                     aValues.push_back(std::numeric_limits<double>::quiet_NaN());
    1774             :                 else
    1775           0 :                     aValues.push_back(
    1776           0 :                         CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
    1777             :             }
    1778           0 :             poFeature->SetField(i, nCount, aValues.data());
    1779           0 :             break;
    1780             :         }
    1781             : 
    1782           0 :         case arrow::Type::DECIMAL64:
    1783             :         {
    1784           0 :             const auto values = std::static_pointer_cast<arrow::Decimal64Array>(
    1785             :                 array->values());
    1786           0 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1787           0 :             const int nCount = array->value_length(nIdxInArray);
    1788           0 :             std::vector<double> aValues;
    1789           0 :             aValues.reserve(nCount);
    1790           0 :             for (int k = 0; k < nCount; k++)
    1791             :             {
    1792           0 :                 if (values->IsNull(nIdxStart + k))
    1793           0 :                     aValues.push_back(std::numeric_limits<double>::quiet_NaN());
    1794             :                 else
    1795           0 :                     aValues.push_back(
    1796           0 :                         CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
    1797             :             }
    1798           0 :             poFeature->SetField(i, nCount, aValues.data());
    1799           0 :             break;
    1800             :         }
    1801             : #endif
    1802             : 
    1803        1474 :         case arrow::Type::DECIMAL128:
    1804             :         {
    1805        2948 :             const auto values =
    1806             :                 std::static_pointer_cast<arrow::Decimal128Array>(
    1807             :                     array->values());
    1808        1474 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1809        1474 :             const int nCount = array->value_length(nIdxInArray);
    1810        2948 :             std::vector<double> aValues;
    1811        1474 :             aValues.reserve(nCount);
    1812        2948 :             for (int k = 0; k < nCount; k++)
    1813             :             {
    1814        1474 :                 if (values->IsNull(nIdxStart + k))
    1815         344 :                     aValues.push_back(std::numeric_limits<double>::quiet_NaN());
    1816             :                 else
    1817        1130 :                     aValues.push_back(
    1818        1130 :                         CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
    1819             :             }
    1820        1474 :             poFeature->SetField(i, nCount, aValues.data());
    1821        1474 :             break;
    1822             :         }
    1823             : 
    1824        1474 :         case arrow::Type::DECIMAL256:
    1825             :         {
    1826        2948 :             const auto values =
    1827             :                 std::static_pointer_cast<arrow::Decimal256Array>(
    1828             :                     array->values());
    1829        1474 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1830        1474 :             const int nCount = array->value_length(nIdxInArray);
    1831        2948 :             std::vector<double> aValues;
    1832        1474 :             aValues.reserve(nCount);
    1833        2948 :             for (int k = 0; k < nCount; k++)
    1834             :             {
    1835        1474 :                 if (values->IsNull(nIdxStart + k))
    1836         344 :                     aValues.push_back(std::numeric_limits<double>::quiet_NaN());
    1837             :                 else
    1838        1130 :                     aValues.push_back(
    1839        1130 :                         CPLAtof(values->FormatValue(nIdxStart + k).c_str()));
    1840             :             }
    1841        1474 :             poFeature->SetField(i, nCount, aValues.data());
    1842        1474 :             break;
    1843             :         }
    1844             : 
    1845        3493 :         case arrow::Type::STRING:
    1846             :         {
    1847        6986 :             const auto values =
    1848             :                 std::static_pointer_cast<arrow::StringArray>(array->values());
    1849        3493 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1850        3493 :             const int nCount = array->value_length(nIdxInArray);
    1851        6986 :             CPLStringList aosList;
    1852        9104 :             for (int k = 0; k < nCount; k++)
    1853             :             {
    1854        5611 :                 if (values->IsNull(nIdxStart + k))
    1855         352 :                     aosList.AddString(
    1856             :                         "");  // we cannot have null strings in a list
    1857             :                 else
    1858        5259 :                     aosList.AddString(values->GetString(nIdxStart + k).c_str());
    1859             :             }
    1860        3493 :             poFeature->SetField(i, aosList.List());
    1861        3493 :             break;
    1862             :         }
    1863             : 
    1864        1474 :         case arrow::Type::LARGE_STRING:
    1865             :         {
    1866        2948 :             const auto values =
    1867             :                 std::static_pointer_cast<arrow::LargeStringArray>(
    1868             :                     array->values());
    1869        1474 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1870        1474 :             const auto nCount = array->value_length(nIdxInArray);
    1871        2948 :             CPLStringList aosList;
    1872        3215 :             for (auto k = decltype(nCount){0}; k < nCount; k++)
    1873             :             {
    1874        1741 :                 if (values->IsNull(nIdxStart + k))
    1875         352 :                     aosList.AddString(
    1876             :                         "");  // we cannot have null strings in a list
    1877             :                 else
    1878        1389 :                     aosList.AddString(values->GetString(nIdxStart + k).c_str());
    1879             :             }
    1880        1474 :             poFeature->SetField(i, aosList.List());
    1881        1474 :             break;
    1882             :         }
    1883             : 
    1884             : #if ARROW_VERSION_MAJOR >= 15
    1885           2 :         case arrow::Type::STRING_VIEW:
    1886             :         {
    1887           4 :             const auto values =
    1888             :                 std::static_pointer_cast<arrow::StringViewArray>(
    1889             :                     array->values());
    1890           2 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1891           2 :             const int nCount = array->value_length(nIdxInArray);
    1892           4 :             CPLStringList aosList;
    1893           6 :             for (int k = 0; k < nCount; k++)
    1894             :             {
    1895           4 :                 if (values->IsNull(nIdxStart + k))
    1896           1 :                     aosList.AddString(
    1897             :                         "");  // we cannot have null strings in a list
    1898             :                 else
    1899           3 :                     aosList.AddString(values->GetString(nIdxStart + k).c_str());
    1900             :             }
    1901           2 :             poFeature->SetField(i, aosList.List());
    1902           2 :             break;
    1903             :         }
    1904             : #endif
    1905             : 
    1906           2 :         case arrow::Type::BINARY:
    1907             :         {
    1908           4 :             const auto values =
    1909             :                 std::static_pointer_cast<arrow::BinaryArray>(array->values());
    1910           2 :             const auto nIdxStart = array->value_offset(nIdxInArray);
    1911           2 :             const auto nCount = array->value_length(nIdxInArray);
    1912           4 :             CPLStringList aosList;
    1913           6 :             for (auto k = decltype(nCount){0}; k < nCount; k++)
    1914             :             {
    1915           4 :                 if (values->IsNull(nIdxStart + k))
    1916             :                 {
    1917           1 :                     aosList.AddString(
    1918             :                         "");  // we cannot have null strings in a list
    1919             :                 }
    1920             :                 else
    1921             :                 {
    1922           3 :                     int length = 0;
    1923           3 :                     const uint8_t *data =
    1924           3 :                         values->GetValue(nIdxStart + k, &length);
    1925           3 :                     bool bIsASCII = true;
    1926          10 :                     for (int j = 0; j < length && bIsASCII; ++j)
    1927           7 :                         bIsASCII = data[j] >= 32 && data[j] <= 127;
    1928           3 :                     if (bIsASCII)
    1929             :                     {
    1930           2 :                         aosList.AddString(
    1931             :                             std::string(reinterpret_cast<const char *>(data),
    1932             :                                         length)
    1933             :                                 .c_str());
    1934             :                     }
    1935             :                     else
    1936             :                     {
    1937           1 :                         char *pszBase64 = CPLBase64Encode(length, data);
    1938           1 :                         aosList.AddString(
    1939           2 :                             std::string("base64:").append(pszBase64).c_str());
    1940           1 :                         CPLFree(pszBase64);
    1941             :                     }
    1942             :                 }
    1943             :             }
    1944           2 :             poFeature->SetField(i, aosList.List());
    1945           2 :             break;
    1946             :         }
    1947             : 
    1948        2042 :         case arrow::Type::LIST:
    1949             :         case arrow::Type::LARGE_LIST:
    1950             :         case arrow::Type::FIXED_SIZE_LIST:
    1951             :         case arrow::Type::MAP:
    1952             :         case arrow::Type::STRUCT:
    1953             :         {
    1954        2042 :             poFeature->SetField(
    1955             :                 i, GetListAsJSON(array, static_cast<size_t>(nIdxInArray))
    1956             :                        .Format(CPLJSONObject::PrettyFormat::Plain)
    1957             :                        .c_str());
    1958        2042 :             break;
    1959             :         }
    1960             : 
    1961           0 :         default:
    1962             :         {
    1963           0 :             CPLDebug("ARROW", "ReadList(): unexpected data type %s",
    1964           0 :                      array->values()->type()->ToString().c_str());
    1965           0 :             break;
    1966             :         }
    1967             :     }
    1968       49681 : }
    1969             : 
    1970             : /************************************************************************/
    1971             : /*                         SetPointsOfLine()                            */
    1972             : /************************************************************************/
    1973             : 
    1974             : template <bool bHasZ, bool bHasM, int nDim>
    1975         948 : void SetPointsOfLine(OGRLineString *poLS, const arrow::DoubleArray *pointValues,
    1976             :                      size_t pointOffset, int numPoints)
    1977             : {
    1978             :     if (!bHasZ && !bHasM)
    1979             :     {
    1980             :         static_assert(sizeof(OGRRawPoint) == 2 * sizeof(double),
    1981             :                       "sizeof(OGRRawPoint) == 2 * sizeof(double)");
    1982        1216 :         poLS->setPoints(numPoints,
    1983             :                         reinterpret_cast<const OGRRawPoint *>(
    1984         608 :                             pointValues->raw_values() + pointOffset));
    1985         608 :         return;
    1986             :     }
    1987             : 
    1988         340 :     poLS->setNumPoints(numPoints, FALSE);
    1989        1634 :     for (int k = 0; k < numPoints; k++)
    1990             :     {
    1991             :         if constexpr (bHasZ)
    1992             :         {
    1993             :             if constexpr (bHasM)
    1994             :             {
    1995         330 :                 poLS->setPoint(k, pointValues->Value(pointOffset + nDim * k),
    1996         330 :                                pointValues->Value(pointOffset + nDim * k + 1),
    1997         330 :                                pointValues->Value(pointOffset + nDim * k + 2),
    1998         330 :                                pointValues->Value(pointOffset + nDim * k + 3));
    1999             :             }
    2000             :             else
    2001             :             {
    2002         634 :                 poLS->setPoint(k, pointValues->Value(pointOffset + nDim * k),
    2003         634 :                                pointValues->Value(pointOffset + nDim * k + 1),
    2004         634 :                                pointValues->Value(pointOffset + nDim * k + 2));
    2005             :             }
    2006             :         }
    2007             :         else /* if( bHasM ) */
    2008             :         {
    2009         330 :             poLS->setPointM(k, pointValues->Value(pointOffset + nDim * k),
    2010         330 :                             pointValues->Value(pointOffset + nDim * k + 1),
    2011         330 :                             pointValues->Value(pointOffset + nDim * k + 2));
    2012             :         }
    2013             :     }
    2014             : }
    2015             : 
    2016             : typedef void (*SetPointsOfLineType)(OGRLineString *, const arrow::DoubleArray *,
    2017             :                                     size_t, int);
    2018             : 
    2019         824 : static SetPointsOfLineType GetSetPointsOfLine(bool bHasZ, bool bHasM)
    2020             : {
    2021         824 :     if (bHasZ && bHasM)
    2022          66 :         return SetPointsOfLine<true, true, 4>;
    2023         758 :     if (bHasZ)
    2024         168 :         return SetPointsOfLine<true, false, 3>;
    2025         590 :     if (bHasM)
    2026          66 :         return SetPointsOfLine<false, true, 3>;
    2027         524 :     return SetPointsOfLine<false, false, 2>;
    2028             : }
    2029             : 
    2030             : /************************************************************************/
    2031             : /*                        SetPointsOfLineStruct()                       */
    2032             : /************************************************************************/
    2033             : 
    2034             : template <bool bHasZ, bool bHasM, int nDim>
    2035        1428 : void SetPointsOfLineStruct(OGRLineString *poLS,
    2036             :                            const arrow::StructArray *structArray,
    2037             :                            size_t pointOffset, int numPoints)
    2038             : {
    2039        1428 :     CPLAssert(structArray->num_fields() == nDim);
    2040        1428 :     const auto &fields = structArray->fields();
    2041        1428 :     const auto &fieldX = fields[0];
    2042        1428 :     CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    2043        1428 :     const auto fieldXDouble = static_cast<arrow::DoubleArray *>(fieldX.get());
    2044        1428 :     const auto &fieldY = fields[1];
    2045        1428 :     CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    2046        1428 :     const auto fieldYDouble = static_cast<arrow::DoubleArray *>(fieldY.get());
    2047        1428 :     const arrow::DoubleArray *fieldZDouble = nullptr;
    2048        1428 :     const arrow::DoubleArray *fieldMDouble = nullptr;
    2049        1428 :     int iField = 2;
    2050             :     if constexpr (bHasZ)
    2051             :     {
    2052         466 :         const auto &field = fields[iField];
    2053         466 :         ++iField;
    2054         466 :         CPLAssert(field->type_id() == arrow::Type::DOUBLE);
    2055         466 :         fieldZDouble = static_cast<arrow::DoubleArray *>(field.get());
    2056             :     }
    2057             :     if constexpr (bHasM)
    2058             :     {
    2059          26 :         const auto &field = fields[iField];
    2060          26 :         CPLAssert(field->type_id() == arrow::Type::DOUBLE);
    2061          26 :         fieldMDouble = static_cast<arrow::DoubleArray *>(field.get());
    2062             :     }
    2063             : 
    2064        1428 :     poLS->setNumPoints(numPoints, FALSE);
    2065        6544 :     for (int k = 0; k < numPoints; k++)
    2066             :     {
    2067             :         if constexpr (bHasZ)
    2068             :         {
    2069             :             if constexpr (bHasM)
    2070             :             {
    2071          55 :                 poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
    2072          55 :                                fieldYDouble->Value(pointOffset + k),
    2073          55 :                                fieldZDouble->Value(pointOffset + k),
    2074          55 :                                fieldMDouble->Value(pointOffset + k));
    2075             :             }
    2076             :             else
    2077             :             {
    2078        1335 :                 poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
    2079        1335 :                                fieldYDouble->Value(pointOffset + k),
    2080        1335 :                                fieldZDouble->Value(pointOffset + k));
    2081             :             }
    2082             :         }
    2083             :         else if constexpr (bHasM)
    2084             :         {
    2085          55 :             poLS->setPointM(k, fieldXDouble->Value(pointOffset + k),
    2086          55 :                             fieldYDouble->Value(pointOffset + k),
    2087          55 :                             fieldMDouble->Value(pointOffset + k));
    2088             :         }
    2089             :         else
    2090             :         {
    2091        3671 :             poLS->setPoint(k, fieldXDouble->Value(pointOffset + k),
    2092        3671 :                            fieldYDouble->Value(pointOffset + k));
    2093             :         }
    2094             :     }
    2095        1428 : }
    2096             : 
    2097             : typedef void (*SetPointsOfLineStructType)(OGRLineString *,
    2098             :                                           const arrow::StructArray *, size_t,
    2099             :                                           int);
    2100             : 
    2101        1100 : static SetPointsOfLineStructType GetSetPointsOfLineStruct(bool bHasZ,
    2102             :                                                           bool bHasM)
    2103             : {
    2104        1100 :     if (bHasZ && bHasM)
    2105          11 :         return SetPointsOfLineStruct<true, true, 4>;
    2106        1089 :     if (bHasZ)
    2107         419 :         return SetPointsOfLineStruct<true, false, 3>;
    2108         670 :     if (bHasM)
    2109          11 :         return SetPointsOfLineStruct<false, true, 3>;
    2110         659 :     return SetPointsOfLineStruct<false, false, 2>;
    2111             : }
    2112             : 
    2113             : /************************************************************************/
    2114             : /*                            TimestampToOGR()                          */
    2115             : /************************************************************************/
    2116             : 
    2117             : inline void
    2118       11122 : OGRArrowLayer::TimestampToOGR(int64_t timestamp,
    2119             :                               const arrow::TimestampType *timestampType,
    2120             :                               int nTZFlag, OGRField *psField)
    2121             : {
    2122       11122 :     const auto unit = timestampType->unit();
    2123       11122 :     double floatingPart = 0;
    2124       11122 :     if (unit == arrow::TimeUnit::MILLI)
    2125             :     {
    2126        7363 :         floatingPart = (timestamp % 1000) / 1e3;
    2127        7363 :         timestamp /= 1000;
    2128             :     }
    2129        3759 :     else if (unit == arrow::TimeUnit::MICRO)
    2130             :     {
    2131        3289 :         floatingPart = (timestamp % (1000 * 1000)) / 1e6;
    2132        3289 :         timestamp /= 1000 * 1000;
    2133             :     }
    2134         470 :     else if (unit == arrow::TimeUnit::NANO)
    2135             :     {
    2136         235 :         floatingPart = (timestamp % (1000 * 1000 * 1000)) / 1e9;
    2137         235 :         timestamp /= 1000 * 1000 * 1000;
    2138             :     }
    2139       11122 :     if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
    2140             :     {
    2141        5563 :         const int TZOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
    2142        5563 :         timestamp += TZOffset * 60;
    2143             :     }
    2144             :     struct tm dt;
    2145       11122 :     CPLUnixTimeToYMDHMS(timestamp, &dt);
    2146       11122 :     psField->Date.Year = static_cast<GInt16>(dt.tm_year + 1900);
    2147       11122 :     psField->Date.Month = static_cast<GByte>(dt.tm_mon + 1);
    2148       11122 :     psField->Date.Day = static_cast<GByte>(dt.tm_mday);
    2149       11122 :     psField->Date.Hour = static_cast<GByte>(dt.tm_hour);
    2150       11122 :     psField->Date.Minute = static_cast<GByte>(dt.tm_min);
    2151       11122 :     psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
    2152       11122 :     psField->Date.Second = static_cast<float>(dt.tm_sec + floatingPart);
    2153       11122 : }
    2154             : 
    2155             : /************************************************************************/
    2156             : /*                            ReadFeature()                             */
    2157             : /************************************************************************/
    2158             : 
    2159        9846 : inline OGRFeature *OGRArrowLayer::ReadFeature(
    2160             :     int64_t nIdxInBatch,
    2161             :     const std::vector<std::shared_ptr<arrow::Array>> &poColumnArrays) const
    2162             : {
    2163        9846 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
    2164             : 
    2165        9846 :     if (m_iFIDArrowColumn >= 0)
    2166             :     {
    2167        3396 :         const int iCol =
    2168        3396 :             m_bIgnoredFields ? m_nRequestedFIDColumn : m_iFIDArrowColumn;
    2169        3396 :         const arrow::Array *array = poColumnArrays[iCol].get();
    2170        3396 :         if (!array->IsNull(nIdxInBatch))
    2171             :         {
    2172        3396 :             if (array->type_id() == arrow::Type::INT64)
    2173             :             {
    2174        3396 :                 const auto castArray =
    2175             :                     static_cast<const arrow::Int64Array *>(array);
    2176        3396 :                 poFeature->SetFID(
    2177        3396 :                     static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
    2178             :             }
    2179           0 :             else if (array->type_id() == arrow::Type::INT32)
    2180             :             {
    2181           0 :                 const auto castArray =
    2182             :                     static_cast<const arrow::Int32Array *>(array);
    2183           0 :                 poFeature->SetFID(castArray->Value(nIdxInBatch));
    2184             :             }
    2185             :         }
    2186             :     }
    2187             : 
    2188        9846 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    2189      175244 :     for (int i = 0; i < nFieldCount; ++i)
    2190             :     {
    2191             :         int iCol;
    2192      165398 :         if (m_bIgnoredFields)
    2193             :         {
    2194       16634 :             iCol = m_anMapFieldIndexToArrayIndex[i];
    2195       16634 :             if (iCol < 0)
    2196        5040 :                 continue;
    2197             :         }
    2198             :         else
    2199             :         {
    2200      148764 :             iCol = m_anMapFieldIndexToArrowColumn[i][0];
    2201             :         }
    2202             : 
    2203      160358 :         const arrow::Array *array = GetStorageArray(poColumnArrays[iCol].get());
    2204      160358 :         if (array->IsNull(nIdxInBatch))
    2205             :         {
    2206       16680 :             poFeature->SetFieldNull(i);
    2207       16680 :             continue;
    2208             :         }
    2209             : 
    2210      143678 :         int j = 1;
    2211      143678 :         bool bSkipToNextField = false;
    2212      157798 :         while (array->type_id() == arrow::Type::STRUCT)
    2213             :         {
    2214       14120 :             const auto castArray =
    2215             :                 static_cast<const arrow::StructArray *>(array);
    2216       14120 :             const auto &subArrays = castArray->fields();
    2217       14120 :             CPLAssert(
    2218             :                 j < static_cast<int>(m_anMapFieldIndexToArrowColumn[i].size()));
    2219       14120 :             const int iArrowSubcol = m_anMapFieldIndexToArrowColumn[i][j];
    2220       14120 :             j++;
    2221       14120 :             CPLAssert(iArrowSubcol < static_cast<int>(subArrays.size()));
    2222       14120 :             array = GetStorageArray(subArrays[iArrowSubcol].get());
    2223       14120 :             if (array->IsNull(nIdxInBatch))
    2224             :             {
    2225           0 :                 poFeature->SetFieldNull(i);
    2226           0 :                 bSkipToNextField = true;
    2227           0 :                 break;
    2228             :             }
    2229             :         }
    2230      143678 :         if (bSkipToNextField)
    2231           0 :             continue;
    2232             : 
    2233      143678 :         if (array->type_id() == arrow::Type::DICTIONARY)
    2234             :         {
    2235        1495 :             const auto castArray =
    2236             :                 static_cast<const arrow::DictionaryArray *>(array);
    2237             :             m_poReadFeatureTmpArray =
    2238        1495 :                 castArray->indices();  // does not return a const reference
    2239        1495 :             array = GetStorageArray(m_poReadFeatureTmpArray.get());
    2240        1495 :             if (array->IsNull(nIdxInBatch))
    2241             :             {
    2242           0 :                 poFeature->SetFieldNull(i);
    2243           0 :                 continue;
    2244             :             }
    2245             :         }
    2246             : 
    2247      143678 :         switch (array->type_id())
    2248             :         {
    2249           0 :             case arrow::Type::NA:
    2250           0 :                 break;
    2251             : 
    2252        1543 :             case arrow::Type::BOOL:
    2253             :             {
    2254        1543 :                 const auto castArray =
    2255             :                     static_cast<const arrow::BooleanArray *>(array);
    2256        1543 :                 poFeature->SetFieldSameTypeUnsafe(
    2257        1543 :                     i, castArray->Value(nIdxInBatch));
    2258        1543 :                 break;
    2259             :             }
    2260        1478 :             case arrow::Type::UINT8:
    2261             :             {
    2262        1478 :                 const auto castArray =
    2263             :                     static_cast<const arrow::UInt8Array *>(array);
    2264        1478 :                 poFeature->SetFieldSameTypeUnsafe(
    2265        1478 :                     i, castArray->Value(nIdxInBatch));
    2266        1478 :                 break;
    2267             :             }
    2268        1474 :             case arrow::Type::INT8:
    2269             :             {
    2270        1474 :                 const auto castArray =
    2271             :                     static_cast<const arrow::Int8Array *>(array);
    2272        1474 :                 poFeature->SetFieldSameTypeUnsafe(
    2273        1474 :                     i, castArray->Value(nIdxInBatch));
    2274        1474 :                 break;
    2275             :             }
    2276        1474 :             case arrow::Type::UINT16:
    2277             :             {
    2278        1474 :                 const auto castArray =
    2279             :                     static_cast<const arrow::UInt16Array *>(array);
    2280        1474 :                 poFeature->SetFieldSameTypeUnsafe(
    2281        1474 :                     i, castArray->Value(nIdxInBatch));
    2282        1474 :                 break;
    2283             :             }
    2284        1552 :             case arrow::Type::INT16:
    2285             :             {
    2286        1552 :                 const auto castArray =
    2287             :                     static_cast<const arrow::Int16Array *>(array);
    2288        1552 :                 poFeature->SetFieldSameTypeUnsafe(
    2289        1552 :                     i, castArray->Value(nIdxInBatch));
    2290        1552 :                 break;
    2291             :             }
    2292         194 :             case arrow::Type::UINT32:
    2293             :             {
    2294         194 :                 const auto castArray =
    2295             :                     static_cast<const arrow::UInt32Array *>(array);
    2296         194 :                 poFeature->SetFieldSameTypeUnsafe(
    2297         194 :                     i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
    2298         194 :                 break;
    2299             :             }
    2300        8405 :             case arrow::Type::INT32:
    2301             :             {
    2302        8405 :                 const auto castArray =
    2303             :                     static_cast<const arrow::Int32Array *>(array);
    2304        8405 :                 poFeature->SetFieldSameTypeUnsafe(
    2305             :                     i, castArray->Value(nIdxInBatch));
    2306        8405 :                 break;
    2307             :             }
    2308        1474 :             case arrow::Type::UINT64:
    2309             :             {
    2310        1474 :                 const auto castArray =
    2311             :                     static_cast<const arrow::UInt64Array *>(array);
    2312        1474 :                 poFeature->SetFieldSameTypeUnsafe(
    2313        1474 :                     i, static_cast<double>(castArray->Value(nIdxInBatch)));
    2314        1474 :                 break;
    2315             :             }
    2316        7485 :             case arrow::Type::INT64:
    2317             :             {
    2318        7485 :                 const auto castArray =
    2319             :                     static_cast<const arrow::Int64Array *>(array);
    2320        7485 :                 poFeature->SetFieldSameTypeUnsafe(
    2321        7485 :                     i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
    2322        7485 :                 break;
    2323             :             }
    2324         194 :             case arrow::Type::HALF_FLOAT:
    2325             :             {
    2326         194 :                 const auto castArray =
    2327             :                     static_cast<const arrow::HalfFloatArray *>(array);
    2328         194 :                 const uint16_t nFloat16 = castArray->Value(nIdxInBatch);
    2329         194 :                 uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
    2330             :                 float f;
    2331         194 :                 memcpy(&f, &nFloat32, sizeof(nFloat32));
    2332         194 :                 poFeature->SetFieldSameTypeUnsafe(i, f);
    2333         194 :                 break;
    2334             :             }
    2335        1633 :             case arrow::Type::FLOAT:
    2336             :             {
    2337        1633 :                 const auto castArray =
    2338             :                     static_cast<const arrow::FloatArray *>(array);
    2339        1633 :                 poFeature->SetFieldSameTypeUnsafe(
    2340        1633 :                     i, castArray->Value(nIdxInBatch));
    2341        1633 :                 break;
    2342             :             }
    2343        3593 :             case arrow::Type::DOUBLE:
    2344             :             {
    2345        3593 :                 const auto castArray =
    2346             :                     static_cast<const arrow::DoubleArray *>(array);
    2347        3593 :                 poFeature->SetFieldSameTypeUnsafe(
    2348             :                     i, castArray->Value(nIdxInBatch));
    2349        3593 :                 break;
    2350             :             }
    2351        7436 :             case arrow::Type::STRING:
    2352             :             {
    2353        7436 :                 const auto castArray =
    2354             :                     static_cast<const arrow::StringArray *>(array);
    2355        7436 :                 int out_length = 0;
    2356             :                 const uint8_t *data =
    2357        7436 :                     castArray->GetValue(nIdxInBatch, &out_length);
    2358             :                 char *pszString =
    2359        7436 :                     static_cast<char *>(CPLMalloc(out_length + 1));
    2360        7436 :                 memcpy(pszString, data, out_length);
    2361        7436 :                 pszString[out_length] = 0;
    2362        7436 :                 poFeature->SetFieldSameTypeUnsafe(i, pszString);
    2363        7436 :                 break;
    2364             :             }
    2365        1946 :             case arrow::Type::BINARY:
    2366             :             {
    2367        1946 :                 const auto castArray =
    2368             :                     static_cast<const arrow::BinaryArray *>(array);
    2369        1946 :                 int out_length = 0;
    2370             :                 const uint8_t *data =
    2371        1946 :                     castArray->GetValue(nIdxInBatch, &out_length);
    2372        1946 :                 poFeature->SetField(i, out_length, data);
    2373        1946 :                 break;
    2374             :             }
    2375             : #if ARROW_VERSION_MAJOR >= 15
    2376           3 :             case arrow::Type::BINARY_VIEW:
    2377             :             {
    2378           3 :                 const auto castArray =
    2379             :                     static_cast<const arrow::BinaryViewArray *>(array);
    2380           3 :                 const auto view = castArray->GetView(nIdxInBatch);
    2381           3 :                 poFeature->SetField(i, static_cast<int>(view.size()),
    2382           3 :                                     view.data());
    2383           3 :                 break;
    2384             :             }
    2385             : #endif
    2386             : #if ARROW_VERSION_MAJOR >= 15
    2387           3 :             case arrow::Type::STRING_VIEW:
    2388             :             {
    2389           3 :                 const auto castArray =
    2390             :                     static_cast<const arrow::StringViewArray *>(array);
    2391           3 :                 const auto strView = castArray->GetView(nIdxInBatch);
    2392             :                 char *pszString =
    2393           3 :                     static_cast<char *>(CPLMalloc(strView.length() + 1));
    2394           3 :                 memcpy(pszString, strView.data(), strView.length());
    2395           3 :                 pszString[strView.length()] = 0;
    2396           3 :                 poFeature->SetFieldSameTypeUnsafe(i, pszString);
    2397           3 :                 break;
    2398             :             }
    2399             : #endif
    2400        1853 :             case arrow::Type::FIXED_SIZE_BINARY:
    2401             :             {
    2402        1853 :                 const auto castArray =
    2403             :                     static_cast<const arrow::FixedSizeBinaryArray *>(array);
    2404        1853 :                 const uint8_t *data = castArray->GetValue(nIdxInBatch);
    2405        1853 :                 poFeature->SetField(i, castArray->byte_width(), data);
    2406        1853 :                 break;
    2407             :             }
    2408        3471 :             case arrow::Type::DATE32:
    2409             :             {
    2410             :                 // number of days since Epoch
    2411        3471 :                 const auto castArray =
    2412             :                     static_cast<const arrow::Date32Array *>(array);
    2413             :                 int64_t timestamp =
    2414        3471 :                     static_cast<int64_t>(castArray->Value(nIdxInBatch)) * 3600 *
    2415        3471 :                     24;
    2416             :                 struct tm dt;
    2417        3471 :                 CPLUnixTimeToYMDHMS(timestamp, &dt);
    2418        3471 :                 poFeature->SetField(i, dt.tm_year + 1900, dt.tm_mon + 1,
    2419             :                                     dt.tm_mday, 0, 0, 0);
    2420        3471 :                 break;
    2421             :             }
    2422         235 :             case arrow::Type::DATE64:
    2423             :             {
    2424             :                 // number of milliseconds since Epoch
    2425         235 :                 const auto castArray =
    2426             :                     static_cast<const arrow::Date64Array *>(array);
    2427             :                 int64_t timestamp =
    2428         235 :                     static_cast<int64_t>(castArray->Value(nIdxInBatch)) / 1000;
    2429             :                 struct tm dt;
    2430         235 :                 CPLUnixTimeToYMDHMS(timestamp, &dt);
    2431         235 :                 poFeature->SetField(i, dt.tm_year + 1900, dt.tm_mon + 1,
    2432             :                                     dt.tm_mday, 0, 0, 0);
    2433         235 :                 break;
    2434             :             }
    2435       11120 :             case arrow::Type::TIMESTAMP:
    2436             :             {
    2437             :                 const auto timestampType = static_cast<arrow::TimestampType *>(
    2438       11120 :                     array->data()->type.get());
    2439       11120 :                 const auto castArray =
    2440             :                     static_cast<const arrow::TimestampArray *>(array);
    2441       11120 :                 const int64_t timestamp = castArray->Value(nIdxInBatch);
    2442             :                 OGRField sField;
    2443       11120 :                 sField.Set.nMarker1 = OGRUnsetMarker;
    2444       11120 :                 sField.Set.nMarker2 = OGRUnsetMarker;
    2445       11120 :                 sField.Set.nMarker3 = OGRUnsetMarker;
    2446       11120 :                 TimestampToOGR(timestamp, timestampType,
    2447       11120 :                                m_poFeatureDefn->GetFieldDefn(i)->GetTZFlag(),
    2448             :                                &sField);
    2449       11120 :                 poFeature->SetField(i, &sField);
    2450       11120 :                 break;
    2451             :             }
    2452        3335 :             case arrow::Type::TIME32:
    2453             :             {
    2454             :                 const auto timestampType =
    2455        3335 :                     static_cast<arrow::Time32Type *>(array->data()->type.get());
    2456        3335 :                 const auto castArray =
    2457             :                     static_cast<const arrow::Time32Array *>(array);
    2458        3335 :                 const auto unit = timestampType->unit();
    2459        3335 :                 int value = castArray->Value(nIdxInBatch);
    2460        3335 :                 double floatingPart = 0;
    2461        3335 :                 if (unit == arrow::TimeUnit::MILLI)
    2462             :                 {
    2463        3139 :                     floatingPart = (value % 1000) / 1e3;
    2464        3139 :                     value /= 1000;
    2465             :                 }
    2466        3335 :                 const int nHour = value / 3600;
    2467        3335 :                 const int nMinute = (value / 60) % 60;
    2468        3335 :                 const int nSecond = value % 60;
    2469        3335 :                 poFeature->SetField(i, 0, 0, 0, nHour, nMinute,
    2470        3335 :                                     static_cast<float>(nSecond + floatingPart));
    2471        3335 :                 break;
    2472             :             }
    2473        3039 :             case arrow::Type::TIME64:
    2474             :             {
    2475        3039 :                 const auto castArray =
    2476             :                     static_cast<const arrow::Time64Array *>(array);
    2477        3039 :                 poFeature->SetField(
    2478        3039 :                     i, static_cast<GIntBig>(castArray->Value(nIdxInBatch)));
    2479        3039 :                 break;
    2480             :             }
    2481             : 
    2482             : #if ARROW_VERSION_MAJOR >= 18
    2483           0 :             case arrow::Type::DECIMAL32:
    2484             :             {
    2485           0 :                 const auto castArray =
    2486             :                     static_cast<const arrow::Decimal32Array *>(array);
    2487           0 :                 poFeature->SetField(
    2488           0 :                     i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
    2489           0 :                 break;
    2490             :             }
    2491             : 
    2492           0 :             case arrow::Type::DECIMAL64:
    2493             :             {
    2494           0 :                 const auto castArray =
    2495             :                     static_cast<const arrow::Decimal64Array *>(array);
    2496           0 :                 poFeature->SetField(
    2497           0 :                     i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
    2498           0 :                 break;
    2499             :             }
    2500             : #endif
    2501             : 
    2502        1553 :             case arrow::Type::DECIMAL128:
    2503             :             {
    2504        1553 :                 const auto castArray =
    2505             :                     static_cast<const arrow::Decimal128Array *>(array);
    2506        1553 :                 poFeature->SetField(
    2507        3106 :                     i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
    2508        1553 :                 break;
    2509             :             }
    2510             : 
    2511        1369 :             case arrow::Type::DECIMAL256:
    2512             :             {
    2513        1369 :                 const auto castArray =
    2514             :                     static_cast<const arrow::Decimal256Array *>(array);
    2515        1369 :                 poFeature->SetField(
    2516        2738 :                     i, CPLAtof(castArray->FormatValue(nIdxInBatch).c_str()));
    2517        1369 :                 break;
    2518             :             }
    2519             : 
    2520       28549 :             case arrow::Type::LIST:
    2521             :             {
    2522       28549 :                 const auto castArray =
    2523             :                     static_cast<const arrow::ListArray *>(array);
    2524             :                 const auto listType = static_cast<const arrow::ListType *>(
    2525       28549 :                     array->data()->type.get());
    2526       28549 :                 ReadList(poFeature, i, nIdxInBatch, castArray,
    2527       28549 :                          listType->value_field()->type()->id());
    2528       28549 :                 break;
    2529             :             }
    2530             : 
    2531       21132 :             case arrow::Type::FIXED_SIZE_LIST:
    2532             :             {
    2533       21132 :                 const auto castArray =
    2534             :                     static_cast<const arrow::FixedSizeListArray *>(array);
    2535             :                 const auto listType =
    2536             :                     static_cast<const arrow::FixedSizeListType *>(
    2537       21132 :                         array->data()->type.get());
    2538       21132 :                 ReadList(poFeature, i, nIdxInBatch, castArray,
    2539       21132 :                          listType->value_field()->type()->id());
    2540       21132 :                 break;
    2541             :             }
    2542             : 
    2543        1474 :             case arrow::Type::LARGE_STRING:
    2544             :             {
    2545        1474 :                 const auto castArray =
    2546             :                     static_cast<const arrow::LargeStringArray *>(array);
    2547        1474 :                 poFeature->SetField(i,
    2548        2948 :                                     castArray->GetString(nIdxInBatch).c_str());
    2549        1474 :                 break;
    2550             :             }
    2551        1761 :             case arrow::Type::LARGE_BINARY:
    2552             :             {
    2553        1761 :                 const auto castArray =
    2554             :                     static_cast<const arrow::LargeBinaryArray *>(array);
    2555        1761 :                 arrow::LargeBinaryArray::offset_type out_length = 0;
    2556             :                 const uint8_t *data =
    2557        1761 :                     castArray->GetValue(nIdxInBatch, &out_length);
    2558        1761 :                 if (out_length >= 0 && out_length <= INT_MAX - 1)
    2559             :                 {
    2560             :                     // coverity[overflow_sink]
    2561        1761 :                     poFeature->SetField(i, static_cast<int>(out_length), data);
    2562             :                 }
    2563             :                 else
    2564             :                 {
    2565             :                     // this is probably the most likely code path if people use
    2566             :                     // LargeBinary...
    2567           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2568             :                              "Too large binary: " CPL_FRMT_GUIB " bytes",
    2569             :                              static_cast<GUIntBig>(out_length));
    2570             :                 }
    2571        1761 :                 break;
    2572             :             }
    2573             : 
    2574       24900 :             case arrow::Type::MAP:
    2575             :             {
    2576       24900 :                 const auto castArray =
    2577             :                     static_cast<const arrow::MapArray *>(array);
    2578       24900 :                 poFeature->SetField(
    2579       24900 :                     i, GetMapAsJSON(castArray, static_cast<size_t>(nIdxInBatch))
    2580       49800 :                            .Format(CPLJSONObject::PrettyFormat::Plain)
    2581             :                            .c_str());
    2582       24900 :                 break;
    2583             :             }
    2584             : 
    2585             :             // unhandled types
    2586           0 :             case arrow::Type::STRUCT:  // should not happen
    2587             :             case arrow::Type::INTERVAL_MONTHS:
    2588             :             case arrow::Type::INTERVAL_DAY_TIME:
    2589             :             case arrow::Type::SPARSE_UNION:
    2590             :             case arrow::Type::DENSE_UNION:
    2591             :             case arrow::Type::DICTIONARY:
    2592             :             case arrow::Type::EXTENSION:
    2593             :             case arrow::Type::DURATION:
    2594             :             case arrow::Type::LARGE_LIST:
    2595             :             case arrow::Type::INTERVAL_MONTH_DAY_NANO:
    2596             : #if ARROW_VERSION_MAJOR >= 12
    2597             :             case arrow::Type::RUN_END_ENCODED:
    2598             : #endif
    2599             : #if ARROW_VERSION_MAJOR >= 15
    2600             :             case arrow::Type::LIST_VIEW:
    2601             :             case arrow::Type::LARGE_LIST_VIEW:
    2602             : #endif
    2603             :             case arrow::Type::MAX_ID:
    2604             :             {
    2605             :                 // Shouldn't happen normally as we should have discarded those
    2606             :                 // fields when creating OGR field definitions
    2607           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2608             :                          "Cannot read content for field %s",
    2609           0 :                          m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    2610           0 :                 break;
    2611             :             }
    2612             :         }
    2613             :     }
    2614             : 
    2615        9846 :     const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
    2616       19367 :     for (int i = 0; i < nGeomFieldCount; ++i)
    2617             :     {
    2618             :         int iCol;
    2619        9521 :         if (m_bIgnoredFields)
    2620             :         {
    2621        1119 :             iCol = m_anMapGeomFieldIndexToArrayIndex[i];
    2622        1119 :             if (iCol < 0)
    2623          52 :                 continue;
    2624             :         }
    2625             :         else
    2626             :         {
    2627        8402 :             iCol = m_anMapGeomFieldIndexToArrowColumn[i];
    2628             :         }
    2629             : 
    2630        9469 :         const auto array = GetStorageArray(poColumnArrays[iCol].get());
    2631        9469 :         auto poGeometry = ReadGeometry(i, array, nIdxInBatch);
    2632        9469 :         if (poGeometry)
    2633             :         {
    2634        6917 :             const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
    2635        7317 :             if (wkbFlatten(poGeometry->getGeometryType()) == wkbLineString &&
    2636         400 :                 wkbFlatten(poGeomFieldDefn->GetType()) == wkbMultiLineString)
    2637             :             {
    2638             :                 poGeometry =
    2639           3 :                     OGRGeometryFactory::forceToMultiLineString(poGeometry);
    2640             :             }
    2641        7790 :             else if (wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon &&
    2642         876 :                      wkbFlatten(poGeomFieldDefn->GetType()) == wkbMultiPolygon)
    2643             :             {
    2644             :                 poGeometry =
    2645          33 :                     OGRGeometryFactory::forceToMultiPolygon(poGeometry);
    2646             :             }
    2647        6917 :             if (OGR_GT_HasZ(poGeomFieldDefn->GetType()) && !poGeometry->Is3D())
    2648             :             {
    2649          63 :                 poGeometry->set3D(true);
    2650             :             }
    2651        6917 :             poFeature->SetGeomFieldDirectly(i, poGeometry);
    2652             :         }
    2653             :     }
    2654             : 
    2655        9846 :     return poFeature;
    2656             : }
    2657             : 
    2658             : /************************************************************************/
    2659             : /*                           ReadGeometry()                             */
    2660             : /************************************************************************/
    2661             : 
    2662        9689 : inline OGRGeometry *OGRArrowLayer::ReadGeometry(int iGeomField,
    2663             :                                                 const arrow::Array *array,
    2664             :                                                 int64_t nIdxInBatch) const
    2665             : {
    2666        9689 :     if (array->IsNull(nIdxInBatch))
    2667             :     {
    2668        2601 :         return nullptr;
    2669             :     }
    2670        7088 :     OGRGeometry *poGeometry = nullptr;
    2671        7088 :     const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
    2672        7088 :     const auto eGeomType = poGeomFieldDefn->GetType();
    2673        7088 :     const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    2674        7088 :     const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    2675        7088 :     const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
    2676             : 
    2677             :     const auto CreatePoint =
    2678         798 :         [bHasZ, bHasM](const arrow::DoubleArray *pointValues, int pointOffset)
    2679             :     {
    2680         399 :         if (bHasZ)
    2681             :         {
    2682         199 :             if (bHasM)
    2683             :             {
    2684          66 :                 return new OGRPoint(pointValues->Value(pointOffset),
    2685          66 :                                     pointValues->Value(pointOffset + 1),
    2686          66 :                                     pointValues->Value(pointOffset + 2),
    2687          66 :                                     pointValues->Value(pointOffset + 3));
    2688             :             }
    2689             :             else
    2690             :             {
    2691         133 :                 return new OGRPoint(pointValues->Value(pointOffset),
    2692         133 :                                     pointValues->Value(pointOffset + 1),
    2693         133 :                                     pointValues->Value(pointOffset + 2));
    2694             :             }
    2695             :         }
    2696         200 :         else if (bHasM)
    2697             :         {
    2698          66 :             return OGRPoint::createXYM(pointValues->Value(pointOffset),
    2699          66 :                                        pointValues->Value(pointOffset + 1),
    2700         132 :                                        pointValues->Value(pointOffset + 2));
    2701             :         }
    2702             :         else
    2703             :         {
    2704         134 :             return new OGRPoint(pointValues->Value(pointOffset),
    2705         134 :                                 pointValues->Value(pointOffset + 1));
    2706             :         }
    2707        7088 :     };
    2708             : 
    2709             :     const auto CreateStructPoint =
    2710         667 :         [nDim, bHasZ, bHasM](const arrow::StructArray *structArray,
    2711        2001 :                              int64_t pointOffset)
    2712             :     {
    2713         667 :         CPL_IGNORE_RET_VAL(nDim);
    2714         667 :         CPLAssert(structArray->num_fields() == nDim);
    2715         667 :         const auto &fieldX = structArray->field(0);
    2716         667 :         CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    2717             :         const auto fieldXDouble =
    2718         667 :             static_cast<arrow::DoubleArray *>(fieldX.get());
    2719         667 :         const auto &fieldY = structArray->field(1);
    2720         667 :         CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    2721             :         const auto fieldYDouble =
    2722         667 :             static_cast<arrow::DoubleArray *>(fieldY.get());
    2723         667 :         if (bHasZ)
    2724             :         {
    2725         278 :             const auto &fieldZ = structArray->field(2);
    2726         278 :             CPLAssert(fieldZ->type_id() == arrow::Type::DOUBLE);
    2727             :             const auto fieldZDouble =
    2728         278 :                 static_cast<arrow::DoubleArray *>(fieldZ.get());
    2729         278 :             if (bHasM)
    2730             :             {
    2731          11 :                 const auto &fieldM = structArray->field(3);
    2732          11 :                 CPLAssert(fieldM->type_id() == arrow::Type::DOUBLE);
    2733             :                 const auto fieldMDouble =
    2734          11 :                     static_cast<arrow::DoubleArray *>(fieldM.get());
    2735          11 :                 return new OGRPoint(fieldXDouble->Value(pointOffset),
    2736          11 :                                     fieldYDouble->Value(pointOffset),
    2737          11 :                                     fieldZDouble->Value(pointOffset),
    2738          11 :                                     fieldMDouble->Value(pointOffset));
    2739             :             }
    2740             :             else
    2741             :             {
    2742         267 :                 return new OGRPoint(fieldXDouble->Value(pointOffset),
    2743         267 :                                     fieldYDouble->Value(pointOffset),
    2744         267 :                                     fieldZDouble->Value(pointOffset));
    2745             :             }
    2746             :         }
    2747         389 :         else if (bHasM)
    2748             :         {
    2749          11 :             const auto &fieldM = structArray->field(2);
    2750          11 :             CPLAssert(fieldM->type_id() == arrow::Type::DOUBLE);
    2751             :             const auto fieldMDouble =
    2752          11 :                 static_cast<arrow::DoubleArray *>(fieldM.get());
    2753          11 :             return OGRPoint::createXYM(fieldXDouble->Value(pointOffset),
    2754             :                                        fieldYDouble->Value(pointOffset),
    2755          11 :                                        fieldMDouble->Value(pointOffset));
    2756             :         }
    2757             :         else
    2758             :         {
    2759         378 :             return new OGRPoint(fieldXDouble->Value(pointOffset),
    2760         378 :                                 fieldYDouble->Value(pointOffset));
    2761             :         }
    2762        7088 :     };
    2763             : 
    2764             :     // Arrow 14 since https://github.com/apache/arrow/commit/95a8bfb319b2729c8f6daa069433caba3b4ddddd
    2765             :     // returns reference to shared pointers, so we can safely take the raw pointer
    2766             :     // and cast it.
    2767             :     // Earlier versions returned a non-reference shared pointer, so formally it
    2768             :     // is safer to use static_pointer_cast (although in practice given that
    2769             :     // "values" is a member variable), the Arrow >= 14 path might work...
    2770             : #if ARROW_VERSION_MAJOR >= 14
    2771             : #define GET_PTR_FROM_VALUES(var, type, values)                                 \
    2772             :     const auto var = static_cast<const type *>((values).get())
    2773             : #else
    2774             : #define GET_PTR_FROM_VALUES(var, type, values)                                 \
    2775             :     const auto var##tmp = std::static_pointer_cast<type>(values);              \
    2776             :     const auto var = var##tmp.get()
    2777             : #endif
    2778             : 
    2779        7088 :     switch (m_aeGeomEncoding[iGeomField])
    2780             :     {
    2781        4160 :         case OGRArrowGeomEncoding::WKB:
    2782             :         {
    2783        4160 :             int out_length = 0;
    2784             :             const uint8_t *data;
    2785        4160 :             if (array->type_id() == arrow::Type::BINARY)
    2786             :             {
    2787        4149 :                 const auto castArray =
    2788             :                     static_cast<const arrow::BinaryArray *>(array);
    2789        4149 :                 data = castArray->GetValue(nIdxInBatch, &out_length);
    2790             :             }
    2791             :             else
    2792             :             {
    2793          11 :                 CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
    2794          11 :                 const auto castArray =
    2795             :                     static_cast<const arrow::LargeBinaryArray *>(array);
    2796          11 :                 int64_t out_length64 = 0;
    2797          11 :                 data = castArray->GetValue(nIdxInBatch, &out_length64);
    2798          11 :                 if (out_length64 > INT_MAX)
    2799             :                 {
    2800           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
    2801           0 :                     return nullptr;
    2802             :                 }
    2803          11 :                 out_length = static_cast<int>(out_length64);
    2804             :             }
    2805        4160 :             if (OGRGeometryFactory::createFromWkb(
    2806        4160 :                     data, poGeomFieldDefn->GetSpatialRef(), &poGeometry,
    2807        4160 :                     out_length) == OGRERR_NONE)
    2808             :             {
    2809             : #ifdef DEBUG_ReadWKBBoundingBox
    2810             :                 OGREnvelope sEnvelopeFromWKB;
    2811             :                 bool bRet =
    2812             :                     OGRWKBGetBoundingBox(data, out_length, sEnvelopeFromWKB);
    2813             :                 CPLAssert(bRet);
    2814             :                 OGREnvelope sEnvelopeFromGeom;
    2815             :                 poGeometry->getEnvelope(&sEnvelopeFromGeom);
    2816             :                 CPLAssert(sEnvelopeFromWKB == sEnvelopeFromGeom);
    2817             : #endif
    2818             :             }
    2819        4160 :             break;
    2820             :         }
    2821             : 
    2822         120 :         case OGRArrowGeomEncoding::WKT:
    2823             :         {
    2824         120 :             if (array->type_id() == arrow::Type::STRING)
    2825             :             {
    2826         119 :                 const auto castArray =
    2827             :                     static_cast<const arrow::StringArray *>(array);
    2828         238 :                 const auto osWKT = castArray->GetString(nIdxInBatch);
    2829         119 :                 OGRGeometryFactory::createFromWkt(
    2830         119 :                     osWKT.c_str(), poGeomFieldDefn->GetSpatialRef(),
    2831             :                     &poGeometry);
    2832             :             }
    2833             :             else
    2834             :             {
    2835           1 :                 CPLAssert(array->type_id() == arrow::Type::LARGE_STRING);
    2836           1 :                 const auto castArray =
    2837             :                     static_cast<const arrow::LargeStringArray *>(array);
    2838           2 :                 const auto osWKT = castArray->GetString(nIdxInBatch);
    2839           1 :                 OGRGeometryFactory::createFromWkt(
    2840           1 :                     osWKT.c_str(), poGeomFieldDefn->GetSpatialRef(),
    2841             :                     &poGeometry);
    2842             :             }
    2843         120 :             break;
    2844             :         }
    2845             : 
    2846           0 :         case OGRArrowGeomEncoding::GEOARROW_FSL_GENERIC:
    2847             :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_GENERIC:
    2848             :         {
    2849           0 :             CPLAssert(false);
    2850             :             break;
    2851             :         }
    2852             : 
    2853         113 :         case OGRArrowGeomEncoding::GEOARROW_FSL_POINT:
    2854             :         {
    2855         113 :             CPLAssert(array->type_id() == arrow::Type::FIXED_SIZE_LIST);
    2856         113 :             const auto listArray =
    2857             :                 static_cast<const arrow::FixedSizeListArray *>(array);
    2858         113 :             CPLAssert(listArray->values()->type_id() == arrow::Type::DOUBLE);
    2859         113 :             GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
    2860             :                                 listArray->values());
    2861         113 :             if (!pointValues->IsNull(nDim * nIdxInBatch))
    2862             :             {
    2863         113 :                 poGeometry = CreatePoint(pointValues,
    2864             :                                          static_cast<int>(nDim * nIdxInBatch));
    2865         113 :                 poGeometry->assignSpatialReference(
    2866         113 :                     poGeomFieldDefn->GetSpatialRef());
    2867             :             }
    2868         113 :             break;
    2869             :         }
    2870             : 
    2871         100 :         case OGRArrowGeomEncoding::GEOARROW_FSL_LINESTRING:
    2872             :         {
    2873         100 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    2874         100 :             const auto listArray = static_cast<const arrow::ListArray *>(array);
    2875         100 :             CPLAssert(listArray->values()->type_id() ==
    2876             :                       arrow::Type::FIXED_SIZE_LIST);
    2877         100 :             GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
    2878             :                                 listArray->values());
    2879         100 :             CPLAssert(listOfPointsValues->values()->type_id() ==
    2880             :                       arrow::Type::DOUBLE);
    2881         100 :             GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
    2882             :                                 listOfPointsValues->values());
    2883         100 :             const auto nPoints = listArray->value_length(nIdxInBatch);
    2884             :             const auto nPointOffset =
    2885         100 :                 listArray->value_offset(nIdxInBatch) * nDim;
    2886         100 :             auto poLineString = new OGRLineString();
    2887         100 :             poGeometry = poLineString;
    2888         100 :             poGeometry->assignSpatialReference(
    2889         100 :                 poGeomFieldDefn->GetSpatialRef());
    2890         100 :             if (nPoints)
    2891             :             {
    2892          64 :                 GetSetPointsOfLine(bHasZ, bHasM)(poLineString, pointValues,
    2893             :                                                  nPointOffset, nPoints);
    2894             :             }
    2895             :             else
    2896             :             {
    2897          36 :                 poGeometry->set3D(bHasZ);
    2898          36 :                 poGeometry->setMeasured(bHasM);
    2899             :             }
    2900         100 :             break;
    2901             :         }
    2902             : 
    2903         456 :         case OGRArrowGeomEncoding::GEOARROW_FSL_POLYGON:
    2904             :         {
    2905         456 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    2906         456 :             const auto listOfRingsArray =
    2907             :                 static_cast<const arrow::ListArray *>(array);
    2908         456 :             CPLAssert(listOfRingsArray->values()->type_id() ==
    2909             :                       arrow::Type::LIST);
    2910         456 :             GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
    2911             :                                 listOfRingsArray->values());
    2912         456 :             CPLAssert(listOfRingsValues->values()->type_id() ==
    2913             :                       arrow::Type::FIXED_SIZE_LIST);
    2914         456 :             GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
    2915             :                                 listOfRingsValues->values());
    2916         456 :             CPLAssert(listOfPointsValues->values()->type_id() ==
    2917             :                       arrow::Type::DOUBLE);
    2918         456 :             GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
    2919             :                                 listOfPointsValues->values());
    2920         456 :             const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
    2921         456 :             const auto nRings = listOfRingsArray->value_length(nIdxInBatch);
    2922             :             const auto nRingOffset =
    2923         456 :                 listOfRingsArray->value_offset(nIdxInBatch);
    2924         456 :             auto poPoly = new OGRPolygon();
    2925         456 :             poGeometry = poPoly;
    2926         456 :             poGeometry->assignSpatialReference(
    2927         456 :                 poGeomFieldDefn->GetSpatialRef());
    2928         930 :             for (auto k = decltype(nRings){0}; k < nRings; k++)
    2929             :             {
    2930             :                 const auto nPoints =
    2931         474 :                     listOfRingsValues->value_length(nRingOffset + k);
    2932             :                 const auto nPointOffset =
    2933         474 :                     listOfRingsValues->value_offset(nRingOffset + k) * nDim;
    2934         474 :                 auto poRing = new OGRLinearRing();
    2935         474 :                 if (nPoints)
    2936             :                 {
    2937         474 :                     setPointsFun(poRing, pointValues, nPointOffset, nPoints);
    2938             :                 }
    2939         474 :                 poPoly->addRingDirectly(poRing);
    2940             :             }
    2941         456 :             if (poGeometry->IsEmpty())
    2942             :             {
    2943         119 :                 poGeometry->set3D(bHasZ);
    2944         119 :                 poGeometry->setMeasured(bHasM);
    2945             :             }
    2946         456 :             break;
    2947             :         }
    2948             : 
    2949         148 :         case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOINT:
    2950             :         {
    2951         148 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    2952         148 :             const auto listArray = static_cast<const arrow::ListArray *>(array);
    2953         148 :             CPLAssert(listArray->values()->type_id() ==
    2954             :                       arrow::Type::FIXED_SIZE_LIST);
    2955         148 :             GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
    2956             :                                 listArray->values());
    2957         148 :             CPLAssert(listOfPointsValues->values()->type_id() ==
    2958             :                       arrow::Type::DOUBLE);
    2959         148 :             GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
    2960             :                                 listOfPointsValues->values());
    2961         148 :             const auto nPoints = listArray->value_length(nIdxInBatch);
    2962             :             const auto nPointOffset =
    2963         148 :                 listArray->value_offset(nIdxInBatch) * nDim;
    2964         148 :             auto poMultiPoint = new OGRMultiPoint();
    2965         148 :             poGeometry = poMultiPoint;
    2966         148 :             poGeometry->assignSpatialReference(
    2967         148 :                 poGeomFieldDefn->GetSpatialRef());
    2968         434 :             for (auto k = decltype(nPoints){0}; k < nPoints; k++)
    2969             :             {
    2970         572 :                 poMultiPoint->addGeometryDirectly(
    2971         286 :                     CreatePoint(pointValues, nPointOffset + k * nDim));
    2972             :             }
    2973         148 :             if (poGeometry->IsEmpty())
    2974             :             {
    2975          34 :                 poGeometry->set3D(bHasZ);
    2976          34 :                 poGeometry->setMeasured(bHasM);
    2977             :             }
    2978         148 :             break;
    2979             :         }
    2980             : 
    2981         144 :         case OGRArrowGeomEncoding::GEOARROW_FSL_MULTILINESTRING:
    2982             :         {
    2983         144 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    2984         144 :             const auto listOfStringsArray =
    2985             :                 static_cast<const arrow::ListArray *>(array);
    2986         144 :             CPLAssert(listOfStringsArray->values()->type_id() ==
    2987             :                       arrow::Type::LIST);
    2988         144 :             GET_PTR_FROM_VALUES(listOfStringsValues, arrow::ListArray,
    2989             :                                 listOfStringsArray->values());
    2990         144 :             CPLAssert(listOfStringsValues->values()->type_id() ==
    2991             :                       arrow::Type::FIXED_SIZE_LIST);
    2992         144 :             GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
    2993             :                                 listOfStringsValues->values());
    2994         144 :             CPLAssert(listOfPointsValues->values()->type_id() ==
    2995             :                       arrow::Type::DOUBLE);
    2996         144 :             GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
    2997             :                                 listOfPointsValues->values());
    2998         144 :             const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
    2999         144 :             const auto nStrings = listOfStringsArray->value_length(nIdxInBatch);
    3000             :             const auto nRingOffset =
    3001         144 :                 listOfStringsArray->value_offset(nIdxInBatch);
    3002         144 :             auto poMLS = new OGRMultiLineString();
    3003         144 :             poGeometry = poMLS;
    3004         144 :             poGeometry->assignSpatialReference(
    3005         144 :                 poGeomFieldDefn->GetSpatialRef());
    3006         316 :             for (auto k = decltype(nStrings){0}; k < nStrings; k++)
    3007             :             {
    3008             :                 const auto nPoints =
    3009         172 :                     listOfStringsValues->value_length(nRingOffset + k);
    3010             :                 const auto nPointOffset =
    3011         172 :                     listOfStringsValues->value_offset(nRingOffset + k) * nDim;
    3012         172 :                 auto poLS = new OGRLineString();
    3013         172 :                 if (nPoints)
    3014             :                 {
    3015         172 :                     setPointsFun(poLS, pointValues, nPointOffset, nPoints);
    3016             :                 }
    3017         172 :                 poMLS->addGeometryDirectly(poLS);
    3018             :             }
    3019         144 :             if (poGeometry->IsEmpty())
    3020             :             {
    3021          36 :                 poGeometry->set3D(bHasZ);
    3022          36 :                 poGeometry->setMeasured(bHasM);
    3023             :             }
    3024         144 :             break;
    3025             :         }
    3026             : 
    3027         160 :         case OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON:
    3028             :         {
    3029         160 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    3030         160 :             const auto listOfPartsArray =
    3031             :                 static_cast<const arrow::ListArray *>(array);
    3032         160 :             CPLAssert(listOfPartsArray->values()->type_id() ==
    3033             :                       arrow::Type::LIST);
    3034         160 :             GET_PTR_FROM_VALUES(listOfPartsValues, arrow::ListArray,
    3035             :                                 listOfPartsArray->values());
    3036         160 :             CPLAssert(listOfPartsValues->values()->type_id() ==
    3037             :                       arrow::Type::LIST);
    3038         160 :             GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
    3039             :                                 listOfPartsValues->values());
    3040         160 :             CPLAssert(listOfRingsValues->values()->type_id() ==
    3041             :                       arrow::Type::FIXED_SIZE_LIST);
    3042         160 :             GET_PTR_FROM_VALUES(listOfPointsValues, arrow::FixedSizeListArray,
    3043             :                                 listOfRingsValues->values());
    3044         160 :             CPLAssert(listOfPointsValues->values()->type_id() ==
    3045             :                       arrow::Type::DOUBLE);
    3046         160 :             GET_PTR_FROM_VALUES(pointValues, arrow::DoubleArray,
    3047             :                                 listOfPointsValues->values());
    3048         160 :             auto poMP = new OGRMultiPolygon();
    3049         160 :             poGeometry = poMP;
    3050         160 :             poGeometry->assignSpatialReference(
    3051         160 :                 poGeomFieldDefn->GetSpatialRef());
    3052         160 :             const auto setPointsFun = GetSetPointsOfLine(bHasZ, bHasM);
    3053         160 :             const auto nParts = listOfPartsArray->value_length(nIdxInBatch);
    3054             :             const auto nPartOffset =
    3055         160 :                 listOfPartsArray->value_offset(nIdxInBatch);
    3056         356 :             for (auto j = decltype(nParts){0}; j < nParts; j++)
    3057             :             {
    3058             :                 const auto nRings =
    3059         196 :                     listOfPartsValues->value_length(nPartOffset + j);
    3060             :                 const auto nRingOffset =
    3061         196 :                     listOfPartsValues->value_offset(nPartOffset + j);
    3062         196 :                 auto poPoly = new OGRPolygon();
    3063         434 :                 for (auto k = decltype(nRings){0}; k < nRings; k++)
    3064             :                 {
    3065             :                     const auto nPoints =
    3066         238 :                         listOfRingsValues->value_length(nRingOffset + k);
    3067             :                     const auto nPointOffset =
    3068         238 :                         listOfRingsValues->value_offset(nRingOffset + k) * nDim;
    3069         238 :                     auto poRing = new OGRLinearRing();
    3070         238 :                     if (nPoints)
    3071             :                     {
    3072         238 :                         setPointsFun(poRing, pointValues, nPointOffset,
    3073             :                                      nPoints);
    3074             :                     }
    3075         238 :                     poPoly->addRingDirectly(poRing);
    3076             :                 }
    3077         196 :                 poMP->addGeometryDirectly(poPoly);
    3078             :             }
    3079         160 :             if (poGeometry->IsEmpty())
    3080             :             {
    3081          36 :                 poGeometry->set3D(bHasZ);
    3082          36 :                 poGeometry->setMeasured(bHasM);
    3083             :             }
    3084         160 :             break;
    3085             :         }
    3086             : 
    3087         311 :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT:
    3088             :         {
    3089         311 :             CPLAssert(array->type_id() == arrow::Type::STRUCT);
    3090         311 :             const auto structArray =
    3091             :                 static_cast<const arrow::StructArray *>(array);
    3092         311 :             if (!structArray->IsNull(nIdxInBatch))
    3093             :             {
    3094         311 :                 poGeometry = CreateStructPoint(structArray, nIdxInBatch);
    3095         311 :                 poGeometry->assignSpatialReference(
    3096         311 :                     poGeomFieldDefn->GetSpatialRef());
    3097             :             }
    3098         311 :             break;
    3099             :         }
    3100             : 
    3101         200 :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING:
    3102             :         {
    3103         200 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    3104         200 :             const auto listArray = static_cast<const arrow::ListArray *>(array);
    3105         200 :             CPLAssert(listArray->values()->type_id() == arrow::Type::STRUCT);
    3106         200 :             GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
    3107             :                                 listArray->values());
    3108         200 :             const auto nPoints = listArray->value_length(nIdxInBatch);
    3109         200 :             const auto nPointOffset = listArray->value_offset(nIdxInBatch);
    3110         200 :             auto poLineString = new OGRLineString();
    3111         200 :             poGeometry = poLineString;
    3112         200 :             poGeometry->assignSpatialReference(
    3113         200 :                 poGeomFieldDefn->GetSpatialRef());
    3114         200 :             if (nPoints)
    3115             :             {
    3116         164 :                 GetSetPointsOfLineStruct(bHasZ, bHasM)(
    3117             :                     poLineString, pointValues, nPointOffset, nPoints);
    3118             :             }
    3119             :             else
    3120             :             {
    3121          36 :                 poGeometry->set3D(bHasZ);
    3122          36 :                 poGeometry->setMeasured(bHasM);
    3123             :             }
    3124         200 :             break;
    3125             :         }
    3126             : 
    3127         316 :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON:
    3128             :         {
    3129         316 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    3130         316 :             const auto listOfRingsArray =
    3131             :                 static_cast<const arrow::ListArray *>(array);
    3132         316 :             CPLAssert(listOfRingsArray->values()->type_id() ==
    3133             :                       arrow::Type::LIST);
    3134         316 :             GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
    3135             :                                 listOfRingsArray->values());
    3136         316 :             CPLAssert(listOfRingsValues->values()->type_id() ==
    3137             :                       arrow::Type::STRUCT);
    3138         316 :             GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
    3139             :                                 listOfRingsValues->values());
    3140         316 :             const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
    3141         316 :             const auto nRings = listOfRingsArray->value_length(nIdxInBatch);
    3142             :             const auto nRingOffset =
    3143         316 :                 listOfRingsArray->value_offset(nIdxInBatch);
    3144         316 :             auto poPoly = new OGRPolygon();
    3145         316 :             poGeometry = poPoly;
    3146         316 :             poGeometry->assignSpatialReference(
    3147         316 :                 poGeomFieldDefn->GetSpatialRef());
    3148         648 :             for (auto k = decltype(nRings){0}; k < nRings; k++)
    3149             :             {
    3150             :                 const auto nPoints =
    3151         332 :                     listOfRingsValues->value_length(nRingOffset + k);
    3152             :                 const auto nPointOffset =
    3153         332 :                     listOfRingsValues->value_offset(nRingOffset + k);
    3154         332 :                 auto poRing = new OGRLinearRing();
    3155         332 :                 if (nPoints)
    3156             :                 {
    3157         332 :                     setPointsFun(poRing, pointValues, nPointOffset, nPoints);
    3158             :                 }
    3159         332 :                 poPoly->addRingDirectly(poRing);
    3160             :             }
    3161         316 :             if (poGeometry->IsEmpty())
    3162             :             {
    3163          52 :                 poGeometry->set3D(bHasZ);
    3164          52 :                 poGeometry->setMeasured(bHasM);
    3165             :             }
    3166         316 :             break;
    3167             :         }
    3168             : 
    3169         240 :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT:
    3170             :         {
    3171         240 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    3172         240 :             const auto listArray = static_cast<const arrow::ListArray *>(array);
    3173         240 :             CPLAssert(listArray->values()->type_id() == arrow::Type::STRUCT);
    3174         240 :             GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
    3175             :                                 listArray->values());
    3176         240 :             const auto nPoints = listArray->value_length(nIdxInBatch);
    3177         240 :             const auto nPointOffset = listArray->value_offset(nIdxInBatch);
    3178         240 :             auto poMultiPoint = new OGRMultiPoint();
    3179         240 :             poGeometry = poMultiPoint;
    3180         240 :             poGeometry->assignSpatialReference(
    3181         240 :                 poGeomFieldDefn->GetSpatialRef());
    3182         596 :             for (auto k = decltype(nPoints){0}; k < nPoints; k++)
    3183             :             {
    3184         712 :                 poMultiPoint->addGeometryDirectly(
    3185         356 :                     CreateStructPoint(pointValues, nPointOffset + k));
    3186             :             }
    3187         240 :             if (poGeometry->IsEmpty())
    3188             :             {
    3189          36 :                 poGeometry->set3D(bHasZ);
    3190          36 :                 poGeometry->setMeasured(bHasM);
    3191             :             }
    3192         240 :             break;
    3193             :         }
    3194             : 
    3195         284 :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING:
    3196             :         {
    3197         284 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    3198         284 :             const auto listOfStringsArray =
    3199             :                 static_cast<const arrow::ListArray *>(array);
    3200         284 :             CPLAssert(listOfStringsArray->values()->type_id() ==
    3201             :                       arrow::Type::LIST);
    3202         284 :             GET_PTR_FROM_VALUES(listOfStringsValues, arrow::ListArray,
    3203             :                                 listOfStringsArray->values());
    3204         284 :             CPLAssert(listOfStringsValues->values()->type_id() ==
    3205             :                       arrow::Type::STRUCT);
    3206         284 :             GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
    3207             :                                 listOfStringsValues->values());
    3208         284 :             const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
    3209         284 :             const auto nStrings = listOfStringsArray->value_length(nIdxInBatch);
    3210             :             const auto nRingOffset =
    3211         284 :                 listOfStringsArray->value_offset(nIdxInBatch);
    3212         284 :             auto poMLS = new OGRMultiLineString();
    3213         284 :             poGeometry = poMLS;
    3214         284 :             poGeometry->assignSpatialReference(
    3215         284 :                 poGeomFieldDefn->GetSpatialRef());
    3216         696 :             for (auto k = decltype(nStrings){0}; k < nStrings; k++)
    3217             :             {
    3218             :                 const auto nPoints =
    3219         412 :                     listOfStringsValues->value_length(nRingOffset + k);
    3220             :                 const auto nPointOffset =
    3221         412 :                     listOfStringsValues->value_offset(nRingOffset + k);
    3222         412 :                 auto poLS = new OGRLineString();
    3223         412 :                 if (nPoints)
    3224             :                 {
    3225         412 :                     setPointsFun(poLS, pointValues, nPointOffset, nPoints);
    3226             :                 }
    3227         412 :                 poMLS->addGeometryDirectly(poLS);
    3228             :             }
    3229         284 :             if (poGeometry->IsEmpty())
    3230             :             {
    3231          36 :                 poGeometry->set3D(bHasZ);
    3232          36 :                 poGeometry->setMeasured(bHasM);
    3233             :             }
    3234         284 :             break;
    3235             :         }
    3236             : 
    3237         336 :         case OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON:
    3238             :         {
    3239         336 :             CPLAssert(array->type_id() == arrow::Type::LIST);
    3240         336 :             const auto listOfPartsArray =
    3241             :                 static_cast<const arrow::ListArray *>(array);
    3242         336 :             CPLAssert(listOfPartsArray->values()->type_id() ==
    3243             :                       arrow::Type::LIST);
    3244         336 :             GET_PTR_FROM_VALUES(listOfPartsValues, arrow::ListArray,
    3245             :                                 listOfPartsArray->values());
    3246         336 :             CPLAssert(listOfPartsValues->values()->type_id() ==
    3247             :                       arrow::Type::LIST);
    3248         336 :             GET_PTR_FROM_VALUES(listOfRingsValues, arrow::ListArray,
    3249             :                                 listOfPartsValues->values());
    3250         336 :             CPLAssert(listOfRingsValues->values()->type_id() ==
    3251             :                       arrow::Type::STRUCT);
    3252         336 :             GET_PTR_FROM_VALUES(pointValues, arrow::StructArray,
    3253             :                                 listOfRingsValues->values());
    3254         336 :             auto poMP = new OGRMultiPolygon();
    3255         336 :             poGeometry = poMP;
    3256         336 :             poGeometry->assignSpatialReference(
    3257         336 :                 poGeomFieldDefn->GetSpatialRef());
    3258         336 :             const auto setPointsFun = GetSetPointsOfLineStruct(bHasZ, bHasM);
    3259         336 :             const auto nParts = listOfPartsArray->value_length(nIdxInBatch);
    3260             :             const auto nPartOffset =
    3261         336 :                 listOfPartsArray->value_offset(nIdxInBatch);
    3262         756 :             for (auto j = decltype(nParts){0}; j < nParts; j++)
    3263             :             {
    3264             :                 const auto nRings =
    3265         420 :                     listOfPartsValues->value_length(nPartOffset + j);
    3266             :                 const auto nRingOffset =
    3267         420 :                     listOfPartsValues->value_offset(nPartOffset + j);
    3268         420 :                 auto poPoly = new OGRPolygon();
    3269         940 :                 for (auto k = decltype(nRings){0}; k < nRings; k++)
    3270             :                 {
    3271             :                     const auto nPoints =
    3272         520 :                         listOfRingsValues->value_length(nRingOffset + k);
    3273             :                     const auto nPointOffset =
    3274         520 :                         listOfRingsValues->value_offset(nRingOffset + k);
    3275         520 :                     auto poRing = new OGRLinearRing();
    3276         520 :                     if (nPoints)
    3277             :                     {
    3278         520 :                         setPointsFun(poRing, pointValues, nPointOffset,
    3279             :                                      nPoints);
    3280             :                     }
    3281         520 :                     poPoly->addRingDirectly(poRing);
    3282             :                 }
    3283         420 :                 poMP->addGeometryDirectly(poPoly);
    3284             :             }
    3285         336 :             if (poGeometry->IsEmpty())
    3286             :             {
    3287          52 :                 poGeometry->set3D(bHasZ);
    3288          52 :                 poGeometry->setMeasured(bHasM);
    3289             :             }
    3290         336 :             break;
    3291             :         }
    3292             :     }
    3293        7088 :     return poGeometry;
    3294             : }
    3295             : 
    3296             : /************************************************************************/
    3297             : /*                           ResetReading()                             */
    3298             : /************************************************************************/
    3299             : 
    3300        7117 : inline void OGRArrowLayer::ResetReading()
    3301             : {
    3302        7117 :     m_bEOF = false;
    3303        7117 :     m_nFeatureIdx = 0;
    3304        7117 :     m_nIdxInBatch = 0;
    3305        7117 :     m_poReadFeatureTmpArray.reset();
    3306        7117 :     if (m_iRecordBatch != 0)
    3307             :     {
    3308        6303 :         m_iRecordBatch = -1;
    3309        6303 :         m_poBatch.reset();
    3310        6303 :         m_poBatchColumns.clear();
    3311             :     }
    3312        7117 : }
    3313             : 
    3314             : /***********************************************************************/
    3315             : /*                        GetColumnSubNode()                           */
    3316             : /***********************************************************************/
    3317             : 
    3318             : /* static*/
    3319             : inline const swq_expr_node *
    3320         284 : OGRArrowLayer::GetColumnSubNode(const swq_expr_node *poNode)
    3321             : {
    3322         284 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
    3323             :     {
    3324         284 :         if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
    3325         202 :             return poNode->papoSubExpr[0];
    3326          82 :         if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
    3327          12 :             return poNode->papoSubExpr[1];
    3328             :     }
    3329          70 :     return nullptr;
    3330             : }
    3331             : 
    3332             : /***********************************************************************/
    3333             : /*                        GetConstantSubNode()                         */
    3334             : /***********************************************************************/
    3335             : 
    3336             : /* static */
    3337             : inline const swq_expr_node *
    3338         284 : OGRArrowLayer::GetConstantSubNode(const swq_expr_node *poNode)
    3339             : {
    3340         284 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
    3341             :     {
    3342         284 :         if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
    3343         270 :             return poNode->papoSubExpr[1];
    3344          14 :         if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
    3345          12 :             return poNode->papoSubExpr[0];
    3346             :     }
    3347           2 :     return nullptr;
    3348             : }
    3349             : 
    3350             : /***********************************************************************/
    3351             : /*                           IsComparisonOp()                          */
    3352             : /***********************************************************************/
    3353             : 
    3354             : /* static*/
    3355         428 : inline bool OGRArrowLayer::IsComparisonOp(int op)
    3356             : {
    3357         166 :     return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
    3358         594 :             op == SWQ_GT || op == SWQ_GE);
    3359             : }
    3360             : 
    3361             : /***********************************************************************/
    3362             : /*                     FillTargetValueFromSrcExpr()                    */
    3363             : /***********************************************************************/
    3364             : 
    3365         212 : static bool FillTargetValueFromSrcExpr(const OGRFieldDefn *poFieldDefn,
    3366             :                                        OGRArrowLayer::Constraint *psConstraint,
    3367             :                                        const swq_expr_node *poSrcValue)
    3368             : {
    3369         212 :     switch (poFieldDefn->GetType())
    3370             :     {
    3371         111 :         case OFTInteger:
    3372         111 :             psConstraint->eType = OGRArrowLayer::Constraint::Type::Integer;
    3373         111 :             if (poSrcValue->field_type == SWQ_FLOAT)
    3374           2 :                 psConstraint->sValue.Integer =
    3375           2 :                     static_cast<int>(poSrcValue->float_value);
    3376             :             else
    3377         109 :                 psConstraint->sValue.Integer =
    3378         109 :                     static_cast<int>(poSrcValue->int_value);
    3379             :             psConstraint->osValue =
    3380         111 :                 std::to_string(psConstraint->sValue.Integer);
    3381         111 :             break;
    3382             : 
    3383          29 :         case OFTInteger64:
    3384          29 :             psConstraint->eType = OGRArrowLayer::Constraint::Type::Integer64;
    3385          29 :             if (poSrcValue->field_type == SWQ_FLOAT)
    3386           0 :                 psConstraint->sValue.Integer64 =
    3387           0 :                     static_cast<GIntBig>(poSrcValue->float_value);
    3388             :             else
    3389          29 :                 psConstraint->sValue.Integer64 = poSrcValue->int_value;
    3390             :             psConstraint->osValue =
    3391          29 :                 std::to_string(psConstraint->sValue.Integer64);
    3392          29 :             break;
    3393             : 
    3394          25 :         case OFTReal:
    3395          25 :             psConstraint->eType = OGRArrowLayer::Constraint::Type::Real;
    3396          25 :             psConstraint->sValue.Real = poSrcValue->float_value;
    3397          25 :             psConstraint->osValue = std::to_string(psConstraint->sValue.Real);
    3398          25 :             break;
    3399             : 
    3400          22 :         case OFTString:
    3401          22 :             psConstraint->eType = OGRArrowLayer::Constraint::Type::String;
    3402          22 :             psConstraint->sValue.String = poSrcValue->string_value;
    3403          22 :             psConstraint->osValue = psConstraint->sValue.String;
    3404          22 :             break;
    3405             : #ifdef not_yet_handled
    3406             :         case OFTDate:
    3407             :         case OFTTime:
    3408             :         case OFTDateTime:
    3409             :             if (poSrcValue->field_type == SWQ_TIMESTAMP ||
    3410             :                 poSrcValue->field_type == SWQ_DATE ||
    3411             :                 poSrcValue->field_type == SWQ_TIME)
    3412             :             {
    3413             :                 int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0,
    3414             :                     nSec = 0;
    3415             :                 if (sscanf(poSrcValue->string_value,
    3416             :                            "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
    3417             :                            &nDay, &nHour, &nMin, &nSec) == 6 ||
    3418             :                     sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear,
    3419             :                            &nMonth, &nDay) == 3 ||
    3420             :                     sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour,
    3421             :                            &nMin, &nSec) == 3)
    3422             :                 {
    3423             :                     psConstraint->eType =
    3424             :                         OGRArrowLayer::Constraint::Type::DateTime;
    3425             :                     psConstraint->sValue.Date.Year = (GInt16)nYear;
    3426             :                     psConstraint->sValue.Date.Month = (GByte)nMonth;
    3427             :                     psConstraint->sValue.Date.Day = (GByte)nDay;
    3428             :                     psConstraint->sValue.Date.Hour = (GByte)nHour;
    3429             :                     psConstraint->sValue.Date.Minute = (GByte)nMin;
    3430             :                     psConstraint->sValue.Date.Second = (GByte)nSec;
    3431             :                     psConstraint->sValue.Date.TZFlag = 0;
    3432             :                     psConstraint->sValue.Date.Reserved = 0;
    3433             :                 }
    3434             :                 else
    3435             :                     return false;
    3436             :             }
    3437             :             else
    3438             :                 return false;
    3439             :             break;
    3440             : #endif
    3441          25 :         default:
    3442          25 :             return false;
    3443             :     }
    3444         187 :     return true;
    3445             : }
    3446             : 
    3447             : /***********************************************************************/
    3448             : /*                  ComputeConstraintsArrayIdx()                       */
    3449             : /***********************************************************************/
    3450             : 
    3451         530 : inline void OGRArrowLayer::ComputeConstraintsArrayIdx()
    3452             : {
    3453         731 :     for (auto &constraint : m_asAttributeFilterConstraints)
    3454             :     {
    3455         202 :         if (m_bIgnoredFields)
    3456             :         {
    3457          25 :             if (constraint.iField == m_poFeatureDefn->GetFieldCount() + SPF_FID)
    3458             :             {
    3459           1 :                 constraint.iArrayIdx = m_nRequestedFIDColumn;
    3460           1 :                 if (constraint.iArrayIdx < 0 && m_osFIDColumn.empty())
    3461           1 :                     return;
    3462             :             }
    3463             :             else
    3464             :             {
    3465          24 :                 constraint.iArrayIdx =
    3466          24 :                     m_anMapFieldIndexToArrayIndex[constraint.iField];
    3467             :             }
    3468          24 :             if (constraint.iArrayIdx < 0)
    3469             :             {
    3470           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3471             :                          "Constraint on field %s cannot be applied due to "
    3472             :                          "it being ignored",
    3473           1 :                          constraint.iField ==
    3474           1 :                                  m_poFeatureDefn->GetFieldCount() + SPF_FID
    3475           0 :                              ? m_osFIDColumn.c_str()
    3476           1 :                              : m_poFeatureDefn->GetFieldDefn(constraint.iField)
    3477           1 :                                    ->GetNameRef());
    3478             :             }
    3479             :         }
    3480             :         else
    3481             :         {
    3482         177 :             if (constraint.iField == m_poFeatureDefn->GetFieldCount() + SPF_FID)
    3483             :             {
    3484           8 :                 constraint.iArrayIdx = m_iFIDArrowColumn;
    3485           8 :                 if (constraint.iArrayIdx < 0 && !m_osFIDColumn.empty())
    3486             :                 {
    3487           0 :                     CPLDebug(GetDriverUCName().c_str(),
    3488             :                              "Constraint on field %s cannot be applied",
    3489             :                              m_osFIDColumn.c_str());
    3490             :                 }
    3491             :             }
    3492             :             else
    3493             :             {
    3494         169 :                 constraint.iArrayIdx =
    3495         169 :                     m_anMapFieldIndexToArrowColumn[constraint.iField][0];
    3496             :             }
    3497             :         }
    3498             :     }
    3499             : }
    3500             : 
    3501             : /***********************************************************************/
    3502             : /*                     ExploreExprNode()                               */
    3503             : /***********************************************************************/
    3504             : 
    3505         347 : inline void OGRArrowLayer::ExploreExprNode(const swq_expr_node *poNode)
    3506             : {
    3507         199 :     const auto AddConstraint = [this](Constraint &constraint)
    3508         199 :     { m_asAttributeFilterConstraints.emplace_back(constraint); };
    3509             : 
    3510         347 :     if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
    3511          18 :         poNode->nSubExprCount == 2)
    3512             :     {
    3513          18 :         ExploreExprNode(poNode->papoSubExpr[0]);
    3514          18 :         ExploreExprNode(poNode->papoSubExpr[1]);
    3515             :     }
    3516             : 
    3517         329 :     else if (poNode->eNodeType == SNT_OPERATION &&
    3518         658 :              IsComparisonOp(poNode->nOperation) && poNode->nSubExprCount == 2)
    3519             :     {
    3520         284 :         const swq_expr_node *poColumn = GetColumnSubNode(poNode);
    3521         284 :         const swq_expr_node *poValue = GetConstantSubNode(poNode);
    3522         496 :         if (poColumn != nullptr && poValue != nullptr &&
    3523         212 :             (poColumn->field_index < m_poFeatureDefn->GetFieldCount() ||
    3524           8 :              poColumn->field_index ==
    3525           8 :                  m_poFeatureDefn->GetFieldCount() + SPF_FID))
    3526             :         {
    3527             :             const OGRFieldDefn oDummyFIDFieldDefn(m_osFIDColumn.c_str(),
    3528         424 :                                                   OFTInteger64);
    3529             :             const OGRFieldDefn *poFieldDefn =
    3530         212 :                 (poColumn->field_index ==
    3531         212 :                  m_poFeatureDefn->GetFieldCount() + SPF_FID)
    3532         212 :                     ? &oDummyFIDFieldDefn
    3533         204 :                     : m_poFeatureDefn->GetFieldDefn(poColumn->field_index);
    3534             : 
    3535         424 :             Constraint constraint;
    3536         212 :             constraint.iField = poColumn->field_index;
    3537         212 :             constraint.nOperation = poNode->nOperation;
    3538             : 
    3539         212 :             if (FillTargetValueFromSrcExpr(poFieldDefn, &constraint, poValue))
    3540             :             {
    3541         187 :                 if (poColumn == poNode->papoSubExpr[0])
    3542             :                 {
    3543             :                     // nothing to do
    3544             :                 }
    3545             :                 else
    3546             :                 {
    3547             :                     /* If "constant op column", then we must reverse */
    3548             :                     /* the operator for LE, LT, GE, GT */
    3549          12 :                     switch (poNode->nOperation)
    3550             :                     {
    3551           2 :                         case SWQ_LE:
    3552           2 :                             constraint.nOperation = SWQ_GE;
    3553           2 :                             break;
    3554           2 :                         case SWQ_LT:
    3555           2 :                             constraint.nOperation = SWQ_GT;
    3556           2 :                             break;
    3557           2 :                         case SWQ_NE: /* do nothing */;
    3558           2 :                             break;
    3559           2 :                         case SWQ_EQ: /* do nothing */;
    3560           2 :                             break;
    3561           2 :                         case SWQ_GE:
    3562           2 :                             constraint.nOperation = SWQ_LE;
    3563           2 :                             break;
    3564           2 :                         case SWQ_GT:
    3565           2 :                             constraint.nOperation = SWQ_LT;
    3566           2 :                             break;
    3567           0 :                         default:
    3568           0 :                             CPLAssert(false);
    3569             :                             break;
    3570             :                     }
    3571             :                 }
    3572             : 
    3573         187 :                 AddConstraint(constraint);
    3574             :             }
    3575             :         }
    3576             :     }
    3577             : 
    3578          45 :     else if (poNode->eNodeType == SNT_OPERATION &&
    3579          45 :              poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1)
    3580             :     {
    3581           4 :         const swq_expr_node *poColumn = poNode->papoSubExpr[0];
    3582           8 :         if (poColumn->eNodeType == SNT_COLUMN &&
    3583           4 :             poColumn->field_index < m_poFeatureDefn->GetFieldCount())
    3584             :         {
    3585           8 :             Constraint constraint;
    3586           4 :             constraint.iField = poColumn->field_index;
    3587           4 :             constraint.nOperation = poNode->nOperation;
    3588           4 :             AddConstraint(constraint);
    3589           4 :         }
    3590             :     }
    3591             : 
    3592          41 :     else if (poNode->eNodeType == SNT_OPERATION &&
    3593          41 :              poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
    3594          20 :              poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
    3595          20 :              poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
    3596          16 :              poNode->papoSubExpr[0]->nSubExprCount == 1)
    3597             :     {
    3598          16 :         const swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
    3599          32 :         if (poColumn->eNodeType == SNT_COLUMN &&
    3600          16 :             poColumn->field_index < m_poFeatureDefn->GetFieldCount())
    3601             :         {
    3602          16 :             Constraint constraint;
    3603           8 :             constraint.iField = poColumn->field_index;
    3604           8 :             constraint.nOperation = SWQ_ISNOTNULL;
    3605           8 :             AddConstraint(constraint);
    3606             :         }
    3607             :     }
    3608         347 : }
    3609             : 
    3610             : /***********************************************************************/
    3611             : /*                         SetAttributeFilter()                        */
    3612             : /***********************************************************************/
    3613             : 
    3614         748 : inline OGRErr OGRArrowLayer::SetAttributeFilter(const char *pszFilter)
    3615             : {
    3616         748 :     m_asAttributeFilterConstraints.clear();
    3617             : 
    3618             :     // When changing filters, we need to invalidate cached batches, as
    3619             :     // PostFilterArrowArray() has potentially modified array contents
    3620         748 :     if (m_poAttrQuery)
    3621         110 :         InvalidateCachedBatches();
    3622             : 
    3623         748 :     OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
    3624         748 :     if (eErr != OGRERR_NONE)
    3625           0 :         return eErr;
    3626             : 
    3627         748 :     if (m_poAttrQuery != nullptr)
    3628             :     {
    3629         461 :         if (m_nUseOptimizedAttributeFilter < 0)
    3630             :         {
    3631         364 :             m_nUseOptimizedAttributeFilter = CPLTestBool(CPLGetConfigOption(
    3632         728 :                 ("OGR_" + GetDriverUCName() + "_OPTIMIZED_ATTRIBUTE_FILTER")
    3633             :                     .c_str(),
    3634             :                 "YES"));
    3635             :         }
    3636         461 :         if (m_nUseOptimizedAttributeFilter)
    3637             :         {
    3638             :             swq_expr_node *poNode =
    3639         311 :                 static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
    3640         311 :             poNode->ReplaceBetweenByGEAndLERecurse();
    3641         311 :             ExploreExprNode(poNode);
    3642         311 :             ComputeConstraintsArrayIdx();
    3643             :         }
    3644             :     }
    3645             : 
    3646         748 :     return OGRERR_NONE;
    3647             : }
    3648             : 
    3649             : /************************************************************************/
    3650             : /*                        ConstraintEvaluator()                         */
    3651             : /************************************************************************/
    3652             : 
    3653             : namespace
    3654             : {
    3655             : template <class T, class U> struct CompareGeneric
    3656             : {
    3657         289 :     static inline bool get(int op, const T &val1, const U &val2)
    3658             :     {
    3659         289 :         switch (op)
    3660             :         {
    3661           8 :             case SWQ_LE:
    3662           8 :                 return val1 <= val2;
    3663           8 :             case SWQ_LT:
    3664           8 :                 return val1 < val2;
    3665          90 :             case SWQ_NE:
    3666          90 :                 return val1 != val2;
    3667         163 :             case SWQ_EQ:
    3668         163 :                 return val1 == val2;
    3669          12 :             case SWQ_GE:
    3670          12 :                 return val1 >= val2;
    3671           8 :             case SWQ_GT:
    3672           8 :                 return val1 > val2;
    3673             :                 break;
    3674           0 :             default:
    3675           0 :                 CPLAssert(false);
    3676             :         }
    3677             :         return true;
    3678             :     }
    3679             : };
    3680             : 
    3681             : template <class T, class U> struct Compare
    3682             : {
    3683             : };
    3684             : 
    3685             : template <class T> struct Compare<T, T> : public CompareGeneric<T, T>
    3686             : {
    3687             : };
    3688             : 
    3689             : template <> struct Compare<int, GIntBig> : public CompareGeneric<int, GIntBig>
    3690             : {
    3691             : };
    3692             : 
    3693             : template <> struct Compare<double, GIntBig>
    3694             : {
    3695           0 :     static inline bool get(int op, double val1, GIntBig val2)
    3696             :     {
    3697           0 :         return CompareGeneric<double, double>::get(op, val1,
    3698           0 :                                                    static_cast<double>(val2));
    3699             :     }
    3700             : };
    3701             : 
    3702             : template <> struct Compare<GIntBig, int> : public CompareGeneric<GIntBig, int>
    3703             : {
    3704             : };
    3705             : 
    3706             : template <> struct Compare<double, int> : public CompareGeneric<double, int>
    3707             : {
    3708             : };
    3709             : 
    3710             : template <class T>
    3711         289 : static bool ConstraintEvaluator(const OGRArrowLayer::Constraint &constraint,
    3712             :                                 const T value)
    3713             : {
    3714         289 :     bool b = false;
    3715         289 :     switch (constraint.eType)
    3716             :     {
    3717         204 :         case OGRArrowLayer::Constraint::Type::Integer:
    3718         408 :             b = Compare<T, int>::get(constraint.nOperation, value,
    3719         204 :                                      constraint.sValue.Integer);
    3720         204 :             break;
    3721          43 :         case OGRArrowLayer::Constraint::Type::Integer64:
    3722          86 :             b = Compare<T, GIntBig>::get(constraint.nOperation, value,
    3723          43 :                                          constraint.sValue.Integer64);
    3724          43 :             break;
    3725          42 :         case OGRArrowLayer::Constraint::Type::Real:
    3726          84 :             b = Compare<double, double>::get(constraint.nOperation,
    3727           0 :                                              static_cast<double>(value),
    3728          42 :                                              constraint.sValue.Real);
    3729          42 :             break;
    3730           0 :         case OGRArrowLayer::Constraint::Type::String:
    3731           0 :             b = Compare<std::string, std::string>::get(constraint.nOperation,
    3732             :                                                        std::to_string(value),
    3733           0 :                                                        constraint.osValue);
    3734           0 :             break;
    3735             :     }
    3736         289 :     return b;
    3737             : }
    3738             : 
    3739          45 : inline bool CompareStr(int op, const std::string_view &val1,
    3740             :                        const std::string &val2)
    3741             : {
    3742          45 :     if (op == SWQ_EQ)
    3743             :     {
    3744          14 :         return val1 == val2;
    3745             :     }
    3746          31 :     const int cmpRes = val2.compare(val1);
    3747          31 :     switch (op)
    3748             :     {
    3749           4 :         case SWQ_LE:
    3750           4 :             return cmpRes >= 0;
    3751           4 :         case SWQ_LT:
    3752           4 :             return cmpRes > 0;
    3753          23 :         case SWQ_NE:
    3754          23 :             return cmpRes != 0;
    3755             :         // case SWQ_EQ: return cmpRes == 0;
    3756           0 :         case SWQ_GE:
    3757           0 :             return cmpRes <= 0;
    3758           0 :         case SWQ_GT:
    3759           0 :             return cmpRes < 0;
    3760             :             break;
    3761           0 :         default:
    3762           0 :             CPLAssert(false);
    3763             :     }
    3764             :     return true;
    3765             : }
    3766             : 
    3767          45 : inline bool ConstraintEvaluator(const OGRArrowLayer::Constraint &constraint,
    3768             :                                 const std::string_view &value)
    3769             : {
    3770          45 :     return CompareStr(constraint.nOperation, value, constraint.osValue);
    3771             : }
    3772             : 
    3773             : }  // namespace
    3774             : 
    3775             : /************************************************************************/
    3776             : /*                 SkipToNextFeatureDueToAttributeFilter()              */
    3777             : /************************************************************************/
    3778             : 
    3779         441 : inline bool OGRArrowLayer::SkipToNextFeatureDueToAttributeFilter() const
    3780             : {
    3781         679 :     for (const auto &constraint : m_asAttributeFilterConstraints)
    3782             :     {
    3783         441 :         if (constraint.iArrayIdx < 0)
    3784             :         {
    3785          42 :             if (constraint.iField ==
    3786          34 :                     m_poFeatureDefn->GetFieldCount() + SPF_FID &&
    3787          16 :                 m_osFIDColumn.empty())
    3788             :             {
    3789          16 :                 if (!ConstraintEvaluator(constraint,
    3790          16 :                                          static_cast<GIntBig>(m_nFeatureIdx)))
    3791             :                 {
    3792         203 :                     return true;
    3793             :                 }
    3794           4 :                 continue;
    3795             :             }
    3796             :             else
    3797             :             {
    3798             :                 // can happen if ignoring a field that is needed by the
    3799             :                 // attribute filter. ComputeConstraintsArrayIdx() will have
    3800             :                 // warned about that
    3801           2 :                 continue;
    3802             :             }
    3803             :         }
    3804             : 
    3805             :         const arrow::Array *array =
    3806         423 :             m_poBatchColumns[constraint.iArrayIdx].get();
    3807             : 
    3808         423 :         const bool bIsNull = array->IsNull(m_nIdxInBatch);
    3809         423 :         if (constraint.nOperation == SWQ_ISNULL)
    3810             :         {
    3811           7 :             if (bIsNull)
    3812             :             {
    3813           3 :                 continue;
    3814             :             }
    3815           4 :             return true;
    3816             :         }
    3817         416 :         else if (constraint.nOperation == SWQ_ISNOTNULL)
    3818             :         {
    3819          22 :             if (!bIsNull)
    3820             :             {
    3821          18 :                 continue;
    3822             :             }
    3823           4 :             return true;
    3824             :         }
    3825         394 :         else if (bIsNull)
    3826             :         {
    3827          65 :             return true;
    3828             :         }
    3829             : 
    3830         329 :         switch (array->type_id())
    3831             :         {
    3832           0 :             case arrow::Type::NA:
    3833           0 :                 break;
    3834             : 
    3835          92 :             case arrow::Type::BOOL:
    3836             :             {
    3837          92 :                 const auto castArray =
    3838             :                     static_cast<const arrow::BooleanArray *>(array);
    3839          92 :                 if (!ConstraintEvaluator(
    3840             :                         constraint,
    3841          92 :                         static_cast<int>(castArray->Value(m_nIdxInBatch))))
    3842             :                 {
    3843          42 :                     return true;
    3844             :                 }
    3845          50 :                 break;
    3846             :             }
    3847           7 :             case arrow::Type::UINT8:
    3848             :             {
    3849           7 :                 const auto castArray =
    3850             :                     static_cast<const arrow::UInt8Array *>(array);
    3851           7 :                 if (!ConstraintEvaluator(
    3852             :                         constraint,
    3853           7 :                         static_cast<int>(castArray->Value(m_nIdxInBatch))))
    3854             :                 {
    3855           3 :                     return true;
    3856             :                 }
    3857           4 :                 break;
    3858             :             }
    3859          30 :             case arrow::Type::INT8:
    3860             :             {
    3861          30 :                 const auto castArray =
    3862             :                     static_cast<const arrow::Int8Array *>(array);
    3863          30 :                 if (!ConstraintEvaluator(
    3864             :                         constraint,
    3865          30 :                         static_cast<int>(castArray->Value(m_nIdxInBatch))))
    3866             :                 {
    3867           2 :                     return true;
    3868             :                 }
    3869          28 :                 break;
    3870             :             }
    3871           4 :             case arrow::Type::UINT16:
    3872             :             {
    3873           4 :                 const auto castArray =
    3874             :                     static_cast<const arrow::UInt16Array *>(array);
    3875           4 :                 if (!ConstraintEvaluator(
    3876             :                         constraint,
    3877           4 :                         static_cast<int>(castArray->Value(m_nIdxInBatch))))
    3878             :                 {
    3879           2 :                     return true;
    3880             :                 }
    3881           2 :                 break;
    3882             :             }
    3883           0 :             case arrow::Type::INT16:
    3884             :             {
    3885           0 :                 const auto castArray =
    3886             :                     static_cast<const arrow::Int16Array *>(array);
    3887           0 :                 if (!ConstraintEvaluator(
    3888             :                         constraint,
    3889           0 :                         static_cast<int>(castArray->Value(m_nIdxInBatch))))
    3890             :                 {
    3891           0 :                     return true;
    3892             :                 }
    3893           0 :                 break;
    3894             :             }
    3895           0 :             case arrow::Type::UINT32:
    3896             :             {
    3897           0 :                 const auto castArray =
    3898             :                     static_cast<const arrow::UInt32Array *>(array);
    3899           0 :                 if (!ConstraintEvaluator(
    3900             :                         constraint,
    3901           0 :                         static_cast<GIntBig>(castArray->Value(m_nIdxInBatch))))
    3902             :                 {
    3903           0 :                     return true;
    3904             :                 }
    3905           0 :                 break;
    3906             :             }
    3907          71 :             case arrow::Type::INT32:
    3908             :             {
    3909          71 :                 const auto castArray =
    3910             :                     static_cast<const arrow::Int32Array *>(array);
    3911          71 :                 if (!ConstraintEvaluator(constraint,
    3912          71 :                                          castArray->Value(m_nIdxInBatch)))
    3913             :                 {
    3914          28 :                     return true;
    3915             :                 }
    3916          43 :                 break;
    3917             :             }
    3918           8 :             case arrow::Type::UINT64:
    3919             :             {
    3920           8 :                 const auto castArray =
    3921             :                     static_cast<const arrow::UInt64Array *>(array);
    3922           8 :                 if (!ConstraintEvaluator(
    3923             :                         constraint,
    3924           8 :                         static_cast<double>(castArray->Value(m_nIdxInBatch))))
    3925             :                 {
    3926           6 :                     return true;
    3927             :                 }
    3928           2 :                 break;
    3929             :             }
    3930          27 :             case arrow::Type::INT64:
    3931             :             {
    3932          27 :                 const auto castArray =
    3933             :                     static_cast<const arrow::Int64Array *>(array);
    3934          27 :                 if (!ConstraintEvaluator(
    3935             :                         constraint,
    3936          27 :                         static_cast<GIntBig>(castArray->Value(m_nIdxInBatch))))
    3937             :                 {
    3938          10 :                     return true;
    3939             :                 }
    3940          17 :                 break;
    3941             :             }
    3942           0 :             case arrow::Type::HALF_FLOAT:
    3943             :             {
    3944           0 :                 const auto castArray =
    3945             :                     static_cast<const arrow::HalfFloatArray *>(array);
    3946           0 :                 const uint16_t nFloat16 = castArray->Value(m_nIdxInBatch);
    3947           0 :                 uint32_t nFloat32 = CPLHalfToFloat(nFloat16);
    3948             :                 float f;
    3949           0 :                 memcpy(&f, &nFloat32, sizeof(nFloat32));
    3950           0 :                 if (!ConstraintEvaluator(constraint, static_cast<double>(f)))
    3951             :                 {
    3952           0 :                     return true;
    3953             :                 }
    3954           0 :                 break;
    3955             :             }
    3956           4 :             case arrow::Type::FLOAT:
    3957             :             {
    3958           4 :                 const auto castArray =
    3959             :                     static_cast<const arrow::FloatArray *>(array);
    3960           4 :                 if (!ConstraintEvaluator(
    3961             :                         constraint,
    3962           4 :                         static_cast<double>(castArray->Value(m_nIdxInBatch))))
    3963             :                 {
    3964           2 :                     return true;
    3965             :                 }
    3966           2 :                 break;
    3967             :             }
    3968          22 :             case arrow::Type::DOUBLE:
    3969             :             {
    3970          22 :                 const auto castArray =
    3971             :                     static_cast<const arrow::DoubleArray *>(array);
    3972          22 :                 if (!ConstraintEvaluator(constraint,
    3973          22 :                                          castArray->Value(m_nIdxInBatch)))
    3974             :                 {
    3975           6 :                     return true;
    3976             :                 }
    3977          16 :                 break;
    3978             :             }
    3979          45 :             case arrow::Type::STRING:
    3980             :             {
    3981          45 :                 const auto castArray =
    3982             :                     static_cast<const arrow::StringArray *>(array);
    3983          45 :                 int out_length = 0;
    3984             :                 const uint8_t *data =
    3985          45 :                     castArray->GetValue(m_nIdxInBatch, &out_length);
    3986          45 :                 if (!ConstraintEvaluator(
    3987             :                         constraint,
    3988          90 :                         std::string_view(reinterpret_cast<const char *>(data),
    3989             :                                          out_length)))
    3990             :                 {
    3991          13 :                     return true;
    3992             :                 }
    3993          32 :                 break;
    3994             :             }
    3995             : 
    3996             : #if ARROW_VERSION_MAJOR >= 18
    3997           0 :             case arrow::Type::DECIMAL32:
    3998             :             {
    3999           0 :                 const auto castArray =
    4000             :                     static_cast<const arrow::Decimal32Array *>(array);
    4001           0 :                 if (!ConstraintEvaluator(
    4002             :                         constraint,
    4003           0 :                         CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
    4004             :                 {
    4005           0 :                     return true;
    4006             :                 }
    4007           0 :                 break;
    4008             :             }
    4009             : 
    4010           0 :             case arrow::Type::DECIMAL64:
    4011             :             {
    4012           0 :                 const auto castArray =
    4013             :                     static_cast<const arrow::Decimal64Array *>(array);
    4014           0 :                 if (!ConstraintEvaluator(
    4015             :                         constraint,
    4016           0 :                         CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
    4017             :                 {
    4018           0 :                     return true;
    4019             :                 }
    4020           0 :                 break;
    4021             :             }
    4022             : #endif
    4023             : 
    4024           4 :             case arrow::Type::DECIMAL128:
    4025             :             {
    4026           4 :                 const auto castArray =
    4027             :                     static_cast<const arrow::Decimal128Array *>(array);
    4028           4 :                 if (!ConstraintEvaluator(
    4029             :                         constraint,
    4030           8 :                         CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
    4031             :                 {
    4032           2 :                     return true;
    4033             :                 }
    4034           2 :                 break;
    4035             :             }
    4036             : 
    4037           4 :             case arrow::Type::DECIMAL256:
    4038             :             {
    4039           4 :                 const auto castArray =
    4040             :                     static_cast<const arrow::Decimal256Array *>(array);
    4041           4 :                 if (!ConstraintEvaluator(
    4042             :                         constraint,
    4043           8 :                         CPLAtof(castArray->FormatValue(m_nIdxInBatch).c_str())))
    4044             :                 {
    4045           2 :                     return true;
    4046             :                 }
    4047           2 :                 break;
    4048             :             }
    4049             : 
    4050          11 :             default:
    4051          11 :                 break;
    4052             :         }
    4053             :     }
    4054         238 :     return false;
    4055             : }
    4056             : 
    4057             : /************************************************************************/
    4058             : /*                           SetBatch()                                 */
    4059             : /************************************************************************/
    4060             : 
    4061             : inline void
    4062        3669 : OGRArrowLayer::SetBatch(const std::shared_ptr<arrow::RecordBatch> &poBatch)
    4063             : {
    4064        3669 :     m_poBatch = poBatch;
    4065        3669 :     m_poBatchColumns.clear();
    4066        3669 :     m_poArrayWKB = nullptr;
    4067        3669 :     m_poArrayWKBLarge = nullptr;
    4068        3669 :     m_poArrayBBOX = nullptr;
    4069        3669 :     m_poArrayXMinDouble = nullptr;
    4070        3669 :     m_poArrayYMinDouble = nullptr;
    4071        3669 :     m_poArrayXMaxDouble = nullptr;
    4072        3669 :     m_poArrayYMaxDouble = nullptr;
    4073        3669 :     m_poArrayXMinFloat = nullptr;
    4074        3669 :     m_poArrayYMinFloat = nullptr;
    4075        3669 :     m_poArrayXMaxFloat = nullptr;
    4076        3669 :     m_poArrayYMaxFloat = nullptr;
    4077             : 
    4078        3669 :     if (m_poBatch)
    4079             :     {
    4080        2640 :         m_poBatchColumns = m_poBatch->columns();
    4081        2640 :         SanityCheckOfSetBatch();
    4082             :     }
    4083             : 
    4084        3669 :     if (m_poBatch && m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
    4085             :     {
    4086             :         int iCol;
    4087         563 :         if (m_bIgnoredFields)
    4088             :         {
    4089         148 :             iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
    4090             :         }
    4091             :         else
    4092             :         {
    4093         415 :             iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
    4094             :         }
    4095        1126 :         if (iCol >= 0 &&
    4096         563 :             m_aeGeomEncoding[m_iGeomFieldFilter] == OGRArrowGeomEncoding::WKB)
    4097             :         {
    4098             :             const arrow::Array *poArrayWKB =
    4099         110 :                 GetStorageArray(m_poBatchColumns[iCol].get());
    4100         110 :             if (poArrayWKB->type_id() == arrow::Type::BINARY)
    4101         110 :                 m_poArrayWKB =
    4102             :                     static_cast<const arrow::BinaryArray *>(poArrayWKB);
    4103             :             else
    4104             :             {
    4105           0 :                 CPLAssert(poArrayWKB->type_id() == arrow::Type::LARGE_BINARY);
    4106           0 :                 m_poArrayWKBLarge =
    4107             :                     static_cast<const arrow::LargeBinaryArray *>(poArrayWKB);
    4108             :             }
    4109             :         }
    4110             : 
    4111        1126 :         if (iCol >= 0 &&
    4112         563 :             CPLTestBool(CPLGetConfigOption(
    4113        1126 :                 ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
    4114             :         {
    4115             :             const auto oIter =
    4116         563 :                 m_oMapGeomFieldIndexToGeomColBBOX.find(m_iGeomFieldFilter);
    4117         563 :             if (oIter != m_oMapGeomFieldIndexToGeomColBBOX.end())
    4118             :             {
    4119         260 :                 const int idx = m_bIgnoredFields ? oIter->second.iArrayIdx
    4120         102 :                                                  : oIter->second.iArrowCol;
    4121         158 :                 if (idx >= 0)
    4122             :                 {
    4123         130 :                     CPLAssert(static_cast<size_t>(idx) <
    4124             :                               m_poBatchColumns.size());
    4125         130 :                     m_poArrayBBOX = m_poBatchColumns[idx].get();
    4126         130 :                     CPLAssert(m_poArrayBBOX->type_id() == arrow::Type::STRUCT);
    4127         130 :                     const auto castArray =
    4128             :                         static_cast<const arrow::StructArray *>(m_poArrayBBOX);
    4129         130 :                     const auto &subArrays = castArray->fields();
    4130         130 :                     CPLAssert(
    4131             :                         static_cast<size_t>(oIter->second.iArrowSubfieldXMin) <
    4132             :                         subArrays.size());
    4133             :                     const auto xminArray =
    4134         130 :                         subArrays[oIter->second.iArrowSubfieldXMin].get();
    4135         130 :                     CPLAssert(
    4136             :                         static_cast<size_t>(oIter->second.iArrowSubfieldYMin) <
    4137             :                         subArrays.size());
    4138             :                     const auto yminArray =
    4139         130 :                         subArrays[oIter->second.iArrowSubfieldYMin].get();
    4140         130 :                     CPLAssert(
    4141             :                         static_cast<size_t>(oIter->second.iArrowSubfieldXMax) <
    4142             :                         subArrays.size());
    4143             :                     const auto xmaxArray =
    4144         130 :                         subArrays[oIter->second.iArrowSubfieldXMax].get();
    4145         130 :                     CPLAssert(
    4146             :                         static_cast<size_t>(oIter->second.iArrowSubfieldYMax) <
    4147             :                         subArrays.size());
    4148             :                     const auto ymaxArray =
    4149         130 :                         subArrays[oIter->second.iArrowSubfieldYMax].get();
    4150         130 :                     if (oIter->second.bIsFloat)
    4151             :                     {
    4152         129 :                         CPLAssert(xminArray->type_id() == arrow::Type::FLOAT);
    4153         129 :                         m_poArrayXMinFloat =
    4154             :                             static_cast<const arrow::FloatArray *>(xminArray);
    4155         129 :                         CPLAssert(yminArray->type_id() == arrow::Type::FLOAT);
    4156         129 :                         m_poArrayYMinFloat =
    4157             :                             static_cast<const arrow::FloatArray *>(yminArray);
    4158         129 :                         CPLAssert(xmaxArray->type_id() == arrow::Type::FLOAT);
    4159         129 :                         m_poArrayXMaxFloat =
    4160             :                             static_cast<const arrow::FloatArray *>(xmaxArray);
    4161         129 :                         CPLAssert(ymaxArray->type_id() == arrow::Type::FLOAT);
    4162         129 :                         m_poArrayYMaxFloat =
    4163             :                             static_cast<const arrow::FloatArray *>(ymaxArray);
    4164             :                     }
    4165             :                     else
    4166             :                     {
    4167           1 :                         CPLAssert(xminArray->type_id() == arrow::Type::DOUBLE);
    4168           1 :                         m_poArrayXMinDouble =
    4169             :                             static_cast<const arrow::DoubleArray *>(xminArray);
    4170           1 :                         CPLAssert(yminArray->type_id() == arrow::Type::DOUBLE);
    4171           1 :                         m_poArrayYMinDouble =
    4172             :                             static_cast<const arrow::DoubleArray *>(yminArray);
    4173           1 :                         CPLAssert(xmaxArray->type_id() == arrow::Type::DOUBLE);
    4174           1 :                         m_poArrayXMaxDouble =
    4175             :                             static_cast<const arrow::DoubleArray *>(xmaxArray);
    4176           1 :                         CPLAssert(ymaxArray->type_id() == arrow::Type::DOUBLE);
    4177           1 :                         m_poArrayYMaxDouble =
    4178             :                             static_cast<const arrow::DoubleArray *>(ymaxArray);
    4179             :                     }
    4180             :                 }
    4181             :             }
    4182             :         }
    4183             :     }
    4184        3669 : }
    4185             : 
    4186             : /************************************************************************/
    4187             : /*                      SanityCheckOfSetBatch()                         */
    4188             : /************************************************************************/
    4189             : 
    4190        2640 : inline void OGRArrowLayer::SanityCheckOfSetBatch() const
    4191             : {
    4192             : #ifdef DEBUG
    4193        2640 :     CPLAssert(m_poBatch);
    4194             : 
    4195        2640 :     const auto &poColumns = m_poBatch->columns();
    4196             : 
    4197             :     // Sanity checks
    4198        2640 :     CPLAssert(m_poBatch->num_columns() == (m_bIgnoredFields
    4199             :                                                ? m_nExpectedBatchColumns
    4200             :                                                : m_poSchema->num_fields()));
    4201        2640 :     const auto &fields = m_poSchema->fields();
    4202             : 
    4203       94832 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    4204             :     {
    4205             :         int iCol;
    4206       92192 :         if (m_bIgnoredFields)
    4207             :         {
    4208       15274 :             iCol = m_anMapFieldIndexToArrayIndex[i];
    4209       15274 :             if (iCol < 0)
    4210        3917 :                 continue;
    4211             :         }
    4212             :         else
    4213             :         {
    4214       76918 :             iCol = m_anMapFieldIndexToArrowColumn[i][0];
    4215             :         }
    4216       88275 :         CPL_IGNORE_RET_VAL(iCol);  // to make cppcheck happy
    4217             : 
    4218       88275 :         CPLAssert(iCol < static_cast<int>(poColumns.size()));
    4219       88275 :         CPLAssert(fields[m_anMapFieldIndexToArrowColumn[i][0]]->type()->id() ==
    4220             :                   poColumns[iCol]->type_id());
    4221             :     }
    4222             : 
    4223        5174 :     for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
    4224             :     {
    4225             :         int iCol;
    4226        2534 :         if (m_bIgnoredFields)
    4227             :         {
    4228         513 :             iCol = m_anMapGeomFieldIndexToArrayIndex[i];
    4229         513 :             if (iCol < 0)
    4230          30 :                 continue;
    4231             :         }
    4232             :         else
    4233             :         {
    4234        2021 :             iCol = m_anMapGeomFieldIndexToArrowColumn[i];
    4235             :         }
    4236        2504 :         CPL_IGNORE_RET_VAL(iCol);  // to make cppcheck happy
    4237             : 
    4238        2504 :         CPLAssert(iCol < static_cast<int>(poColumns.size()));
    4239        2504 :         CPLAssert(fields[m_anMapGeomFieldIndexToArrowColumn[i]]->type()->id() ==
    4240             :                   poColumns[iCol]->type_id());
    4241             :     }
    4242             : #else
    4243             :     CPL_IGNORE_RET_VAL(m_nExpectedBatchColumns);
    4244             : #endif
    4245        2640 : }
    4246             : 
    4247             : /************************************************************************/
    4248             : /*                        GetNextRawFeature()                           */
    4249             : /************************************************************************/
    4250             : 
    4251       11794 : inline OGRFeature *OGRArrowLayer::GetNextRawFeature()
    4252             : {
    4253       11794 :     if (m_bEOF || !m_bSpatialFilterIntersectsLayerExtent)
    4254         391 :         return nullptr;
    4255             : 
    4256       11403 :     if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
    4257             :     {
    4258        3441 :         m_bEOF = !ReadNextBatch();
    4259        3441 :         if (m_bEOF)
    4260        1388 :             return nullptr;
    4261             :     }
    4262             : 
    4263             :     // Evaluate spatial filter by computing the bounding box of each geometry
    4264             :     // but without creating a OGRGeometry
    4265       10015 :     if (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilterRect)
    4266             :     {
    4267             :         int iCol;
    4268         973 :         if (m_bIgnoredFields)
    4269             :         {
    4270         206 :             iCol = m_anMapGeomFieldIndexToArrayIndex[m_iGeomFieldFilter];
    4271             :         }
    4272             :         else
    4273             :         {
    4274         767 :             iCol = m_anMapGeomFieldIndexToArrowColumn[m_iGeomFieldFilter];
    4275             :         }
    4276             : 
    4277         973 :         if (iCol >= 0 && (m_poArrayXMinFloat || m_poArrayXMinDouble))
    4278             :         {
    4279         269 :             OGREnvelope sEnvelopeSkipToNextFeatureDueToBBOX;
    4280             :             const auto IntersectsBBOX =
    4281        2101 :                 [this, &sEnvelopeSkipToNextFeatureDueToBBOX]()
    4282             :             {
    4283         599 :                 if (m_poArrayXMinFloat &&
    4284         299 :                     !m_poArrayXMinFloat->IsNull(m_nIdxInBatch))
    4285             :                 {
    4286         299 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
    4287         299 :                         m_poArrayXMinFloat->Value(m_nIdxInBatch);
    4288         299 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
    4289         299 :                         m_poArrayYMinFloat->Value(m_nIdxInBatch);
    4290         299 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
    4291         299 :                         m_poArrayXMaxFloat->Value(m_nIdxInBatch);
    4292         299 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
    4293         299 :                         m_poArrayYMaxFloat->Value(m_nIdxInBatch);
    4294         299 :                     if (m_sFilterEnvelope.Intersects(
    4295         299 :                             sEnvelopeSkipToNextFeatureDueToBBOX))
    4296             :                     {
    4297         261 :                         return true;
    4298             :                     }
    4299             :                 }
    4300           2 :                 else if (m_poArrayXMinDouble &&
    4301           1 :                          !m_poArrayXMinDouble->IsNull(m_nIdxInBatch))
    4302             :                 {
    4303           1 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MinX =
    4304           1 :                         m_poArrayXMinDouble->Value(m_nIdxInBatch);
    4305           1 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MinY =
    4306           1 :                         m_poArrayYMinDouble->Value(m_nIdxInBatch);
    4307           1 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MaxX =
    4308           1 :                         m_poArrayXMaxDouble->Value(m_nIdxInBatch);
    4309           1 :                     sEnvelopeSkipToNextFeatureDueToBBOX.MaxY =
    4310           1 :                         m_poArrayYMaxDouble->Value(m_nIdxInBatch);
    4311           1 :                     if (m_sFilterEnvelope.Intersects(
    4312           1 :                             sEnvelopeSkipToNextFeatureDueToBBOX))
    4313             :                     {
    4314           1 :                         return true;
    4315             :                     }
    4316             :                 }
    4317          38 :                 return false;
    4318         269 :             };
    4319             : 
    4320             :             while (true)
    4321             :             {
    4322         790 :                 if (!m_poArrayBBOX->IsNull(m_nIdxInBatch) && IntersectsBBOX() &&
    4323         262 :                     (m_asAttributeFilterConstraints.empty() ||
    4324           2 :                      !SkipToNextFeatureDueToAttributeFilter()))
    4325             :                 {
    4326         261 :                     break;
    4327             :                 }
    4328             : 
    4329         265 :                 IncrFeatureIdx();
    4330         265 :                 m_nIdxInBatch++;
    4331         265 :                 if (m_nIdxInBatch == m_poBatch->num_rows())
    4332             :                 {
    4333          17 :                     m_bEOF = !ReadNextBatch();
    4334          17 :                     if (m_bEOF)
    4335           8 :                         return nullptr;
    4336             :                 }
    4337         261 :             }
    4338             :         }
    4339         704 :         else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4340             :                                   OGRArrowGeomEncoding::WKB)
    4341             :         {
    4342         131 :             CPLAssert(m_poArrayWKB || m_poArrayWKBLarge);
    4343         131 :             OGREnvelope sEnvelope;
    4344             : 
    4345             :             while (true)
    4346             :             {
    4347         220 :                 bool bMatchBBOX = false;
    4348         417 :                 if ((m_poArrayWKB && m_poArrayWKB->IsNull(m_nIdxInBatch)) ||
    4349         197 :                     (m_poArrayWKBLarge &&
    4350           0 :                      m_poArrayWKBLarge->IsNull(m_nIdxInBatch)))
    4351             :                 {
    4352             :                     // nothing to do
    4353             :                 }
    4354             :                 else
    4355             :                 {
    4356         197 :                     if (m_poArrayWKB)
    4357             :                     {
    4358         197 :                         int out_length = 0;
    4359             :                         const uint8_t *data =
    4360         197 :                             m_poArrayWKB->GetValue(m_nIdxInBatch, &out_length);
    4361         394 :                         if (OGRWKBGetBoundingBox(data, out_length, sEnvelope) &&
    4362         197 :                             m_sFilterEnvelope.Intersects(sEnvelope))
    4363             :                         {
    4364         115 :                             bMatchBBOX = true;
    4365             :                         }
    4366             :                     }
    4367             :                     else
    4368             :                     {
    4369           0 :                         CPLAssert(m_poArrayWKBLarge);
    4370           0 :                         int64_t out_length64 = 0;
    4371           0 :                         const uint8_t *data = m_poArrayWKBLarge->GetValue(
    4372             :                             m_nIdxInBatch, &out_length64);
    4373           0 :                         if (out_length64 < INT_MAX &&
    4374           0 :                             OGRWKBGetBoundingBox(data,
    4375             :                                                  static_cast<int>(out_length64),
    4376           0 :                                                  sEnvelope) &&
    4377           0 :                             m_sFilterEnvelope.Intersects(sEnvelope))
    4378             :                         {
    4379           0 :                             bMatchBBOX = true;
    4380             :                         }
    4381             :                     }
    4382             :                 }
    4383         221 :                 if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
    4384           1 :                                    !SkipToNextFeatureDueToAttributeFilter()))
    4385             :                 {
    4386         115 :                     break;
    4387             :                 }
    4388             : 
    4389         105 :                 IncrFeatureIdx();
    4390         105 :                 m_nIdxInBatch++;
    4391         105 :                 if (m_nIdxInBatch == m_poBatch->num_rows())
    4392             :                 {
    4393          25 :                     m_bEOF = !ReadNextBatch();
    4394          25 :                     if (m_bEOF)
    4395          16 :                         return nullptr;
    4396             :                 }
    4397          89 :             }
    4398             :         }
    4399        1146 :         else if (iCol >= 0 &&
    4400         573 :                  m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4401             :                      OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
    4402             :         {
    4403             :             const auto poGeomFieldDefn =
    4404          10 :                 m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    4405          10 :             const auto eGeomType = poGeomFieldDefn->GetType();
    4406          10 :             const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    4407          10 :             const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    4408          10 :             const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
    4409             : 
    4410             :             bool bReturnFeature;
    4411           0 :             do
    4412             :             {
    4413          10 :                 bReturnFeature = false;
    4414          10 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4415          10 :                 CPLAssert(array->type_id() == arrow::Type::LIST);
    4416          10 :                 auto listOfPartsArray =
    4417             :                     static_cast<const arrow::ListArray *>(array);
    4418          10 :                 CPLAssert(listOfPartsArray->values()->type_id() ==
    4419             :                           arrow::Type::LIST);
    4420             :                 auto listOfPartsValues =
    4421             :                     std::static_pointer_cast<arrow::ListArray>(
    4422          10 :                         listOfPartsArray->values());
    4423          10 :                 CPLAssert(listOfPartsValues->values()->type_id() ==
    4424             :                           arrow::Type::LIST);
    4425             :                 auto listOfRingsValues =
    4426             :                     std::static_pointer_cast<arrow::ListArray>(
    4427          10 :                         listOfPartsValues->values());
    4428          10 :                 CPLAssert(listOfRingsValues->values()->type_id() ==
    4429             :                           arrow::Type::FIXED_SIZE_LIST);
    4430             :                 auto listOfPointsValues =
    4431             :                     std::static_pointer_cast<arrow::FixedSizeListArray>(
    4432          10 :                         listOfRingsValues->values());
    4433          10 :                 CPLAssert(listOfPointsValues->values()->type_id() ==
    4434             :                           arrow::Type::DOUBLE);
    4435             :                 auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
    4436          10 :                     listOfPointsValues->values());
    4437             : 
    4438             :                 while (true)
    4439             :                 {
    4440          18 :                     bool bMatchBBOX = false;
    4441          18 :                     if (!listOfPartsArray->IsNull(m_nIdxInBatch))
    4442             :                     {
    4443          14 :                         OGREnvelope sEnvelope;
    4444             :                         const auto nParts =
    4445          14 :                             listOfPartsArray->value_length(m_nIdxInBatch);
    4446             :                         const auto nPartOffset =
    4447          14 :                             listOfPartsArray->value_offset(m_nIdxInBatch);
    4448          28 :                         for (auto j = decltype(nParts){0}; j < nParts; j++)
    4449             :                         {
    4450          14 :                             const auto nRings = listOfPartsValues->value_length(
    4451          14 :                                 nPartOffset + j);
    4452             :                             const auto nRingOffset =
    4453          14 :                                 listOfPartsValues->value_offset(nPartOffset +
    4454             :                                                                 j);
    4455          14 :                             if (nRings >= 1)
    4456             :                             {
    4457             :                                 const auto nPoints =
    4458          14 :                                     listOfRingsValues->value_length(
    4459             :                                         nRingOffset);
    4460             :                                 const auto nPointOffset =
    4461          14 :                                     listOfRingsValues->value_offset(
    4462             :                                         nRingOffset) *
    4463          14 :                                     nDim;
    4464             :                                 const double *padfRawValue =
    4465          14 :                                     pointValues->raw_values() + nPointOffset;
    4466          73 :                                 for (auto l = decltype(nPoints){0}; l < nPoints;
    4467             :                                      ++l)
    4468             :                                 {
    4469          59 :                                     sEnvelope.Merge(padfRawValue[nDim * l],
    4470          59 :                                                     padfRawValue[nDim * l + 1]);
    4471             :                                 }
    4472             :                                 // for bounding box, only the first ring matters
    4473             :                             }
    4474             :                         }
    4475             : 
    4476          24 :                         if (nParts != 0 &&
    4477          10 :                             m_sFilterEnvelope.Intersects(sEnvelope))
    4478             :                         {
    4479          10 :                             bMatchBBOX = true;
    4480             :                         }
    4481             :                     }
    4482          28 :                     if (bMatchBBOX &&
    4483          10 :                         (m_asAttributeFilterConstraints.empty() ||
    4484           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4485             :                     {
    4486          10 :                         bReturnFeature = true;
    4487          10 :                         break;
    4488             :                     }
    4489             : 
    4490           8 :                     IncrFeatureIdx();
    4491           8 :                     m_nIdxInBatch++;
    4492           8 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4493             :                     {
    4494           0 :                         m_bEOF = !ReadNextBatch();
    4495           0 :                         if (m_bEOF)
    4496           0 :                             return nullptr;
    4497           0 :                         break;
    4498             :                     }
    4499           8 :                 }
    4500          10 :             } while (!bReturnFeature);
    4501             :         }
    4502         563 :         else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4503             :                                   OGRArrowGeomEncoding::GEOARROW_STRUCT_POINT)
    4504             :         {
    4505             :             bool bReturnFeature;
    4506          10 :             do
    4507             :             {
    4508          44 :                 bReturnFeature = false;
    4509          44 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4510          44 :                 CPLAssert(array->type_id() == arrow::Type::STRUCT);
    4511          44 :                 auto pointValues =
    4512             :                     static_cast<const arrow::StructArray *>(array);
    4513          44 :                 const auto &fields = pointValues->fields();
    4514          44 :                 const auto &fieldX = fields[0];
    4515          44 :                 CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    4516             :                 const auto fieldXDouble =
    4517          44 :                     static_cast<arrow::DoubleArray *>(fieldX.get());
    4518          44 :                 const auto &fieldY = fields[1];
    4519          44 :                 CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    4520             :                 const auto fieldYDouble =
    4521          44 :                     static_cast<arrow::DoubleArray *>(fieldY.get());
    4522             : 
    4523             :                 while (true)
    4524             :                 {
    4525          98 :                     bool bMatchBBOX = false;
    4526          98 :                     if (!array->IsNull(m_nIdxInBatch))
    4527             :                     {
    4528          76 :                         const double dfX = fieldXDouble->Value(m_nIdxInBatch);
    4529          76 :                         const double dfY = fieldYDouble->Value(m_nIdxInBatch);
    4530          76 :                         if (dfX >= m_sFilterEnvelope.MinX &&
    4531          46 :                             dfY >= m_sFilterEnvelope.MinY &&
    4532          46 :                             dfX <= m_sFilterEnvelope.MaxX &&
    4533          30 :                             dfY <= m_sFilterEnvelope.MaxY)
    4534             :                         {
    4535          30 :                             bMatchBBOX = true;
    4536             :                         }
    4537             :                     }
    4538         128 :                     if (bMatchBBOX &&
    4539          30 :                         (m_asAttributeFilterConstraints.empty() ||
    4540           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4541             :                     {
    4542          30 :                         bReturnFeature = true;
    4543          30 :                         break;
    4544             :                     }
    4545             : 
    4546          68 :                     IncrFeatureIdx();
    4547          68 :                     m_nIdxInBatch++;
    4548          68 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4549             :                     {
    4550          14 :                         m_bEOF = !ReadNextBatch();
    4551          14 :                         if (m_bEOF)
    4552           4 :                             return nullptr;
    4553          10 :                         break;
    4554             :                     }
    4555          54 :                 }
    4556          40 :             } while (!bReturnFeature);
    4557             :         }
    4558        1058 :         else if (iCol >= 0 &&
    4559         529 :                  m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4560             :                      OGRArrowGeomEncoding::GEOARROW_STRUCT_LINESTRING)
    4561             :         {
    4562             :             bool bReturnFeature;
    4563           0 :             do
    4564             :             {
    4565          64 :                 bReturnFeature = false;
    4566          64 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4567          64 :                 CPLAssert(array->type_id() == arrow::Type::LIST);
    4568          64 :                 const auto listArray =
    4569             :                     static_cast<const arrow::ListArray *>(array);
    4570          64 :                 CPLAssert(listArray->values()->type_id() ==
    4571             :                           arrow::Type::STRUCT);
    4572             :                 auto pointValues = std::static_pointer_cast<arrow::StructArray>(
    4573          64 :                     listArray->values());
    4574          64 :                 const auto &fields = pointValues->fields();
    4575          64 :                 const auto &fieldX = fields[0];
    4576          64 :                 CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    4577             :                 const auto fieldXDouble =
    4578          64 :                     static_cast<arrow::DoubleArray *>(fieldX.get());
    4579          64 :                 const auto &fieldY = fields[1];
    4580          64 :                 CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    4581             :                 const auto fieldYDouble =
    4582          64 :                     static_cast<arrow::DoubleArray *>(fieldY.get());
    4583             : 
    4584             :                 while (true)
    4585             :                 {
    4586         160 :                     bool bMatchBBOX = false;
    4587         160 :                     if (!listArray->IsNull(m_nIdxInBatch))
    4588             :                     {
    4589         120 :                         OGREnvelope sEnvelope;
    4590             :                         const auto nPoints =
    4591         120 :                             listArray->value_length(m_nIdxInBatch);
    4592             :                         const auto nPointOffset =
    4593         120 :                             listArray->value_offset(m_nIdxInBatch);
    4594         120 :                         if (nPoints > 0)
    4595             :                         {
    4596             :                             const double *padfRawXValue =
    4597          80 :                                 fieldXDouble->raw_values() + nPointOffset;
    4598             :                             const double *padfRawYValue =
    4599          80 :                                 fieldYDouble->raw_values() + nPointOffset;
    4600         240 :                             for (auto l = decltype(nPoints){0}; l < nPoints;
    4601             :                                  ++l)
    4602             :                             {
    4603         160 :                                 sEnvelope.Merge(padfRawXValue[l],
    4604         160 :                                                 padfRawYValue[l]);
    4605             :                             }
    4606          80 :                             if (m_sFilterEnvelope.Intersects(sEnvelope))
    4607             :                             {
    4608          48 :                                 bMatchBBOX = true;
    4609             :                             }
    4610             :                         }
    4611             :                     }
    4612         208 :                     if (bMatchBBOX &&
    4613          48 :                         (m_asAttributeFilterConstraints.empty() ||
    4614           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4615             :                     {
    4616          48 :                         bReturnFeature = true;
    4617          48 :                         break;
    4618             :                     }
    4619             : 
    4620         112 :                     IncrFeatureIdx();
    4621         112 :                     m_nIdxInBatch++;
    4622         112 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4623             :                     {
    4624          16 :                         m_bEOF = !ReadNextBatch();
    4625          16 :                         if (m_bEOF)
    4626          16 :                             return nullptr;
    4627           0 :                         break;
    4628             :                     }
    4629          96 :                 }
    4630          48 :             } while (!bReturnFeature);
    4631             :         }
    4632         465 :         else if (iCol >= 0 && m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4633             :                                   OGRArrowGeomEncoding::GEOARROW_STRUCT_POLYGON)
    4634             :         {
    4635             :             bool bReturnFeature;
    4636           0 :             do
    4637             :             {
    4638          96 :                 bReturnFeature = false;
    4639          96 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4640          96 :                 CPLAssert(array->type_id() == arrow::Type::LIST);
    4641          96 :                 const auto listOfRingsArray =
    4642             :                     static_cast<const arrow::ListArray *>(array);
    4643          96 :                 CPLAssert(listOfRingsArray->values()->type_id() ==
    4644             :                           arrow::Type::LIST);
    4645             :                 const auto listOfRingsValues =
    4646             :                     std::static_pointer_cast<arrow::ListArray>(
    4647          96 :                         listOfRingsArray->values());
    4648          96 :                 CPLAssert(listOfRingsValues->values()->type_id() ==
    4649             :                           arrow::Type::STRUCT);
    4650             :                 auto pointValues = std::static_pointer_cast<arrow::StructArray>(
    4651          96 :                     listOfRingsValues->values());
    4652          96 :                 const auto &fields = pointValues->fields();
    4653          96 :                 const auto &fieldX = fields[0];
    4654          96 :                 CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    4655             :                 const auto fieldXDouble =
    4656          96 :                     static_cast<arrow::DoubleArray *>(fieldX.get());
    4657          96 :                 const auto &fieldY = fields[1];
    4658          96 :                 CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    4659             :                 const auto fieldYDouble =
    4660          96 :                     static_cast<arrow::DoubleArray *>(fieldY.get());
    4661             : 
    4662             :                 while (true)
    4663             :                 {
    4664         240 :                     bool bMatchBBOX = false;
    4665         240 :                     if (!listOfRingsArray->IsNull(m_nIdxInBatch))
    4666             :                     {
    4667         184 :                         OGREnvelope sEnvelope;
    4668             :                         const auto nRings =
    4669         184 :                             listOfRingsArray->value_length(m_nIdxInBatch);
    4670             :                         const auto nRingOffset =
    4671         184 :                             listOfRingsArray->value_offset(m_nIdxInBatch);
    4672         184 :                         if (nRings >= 1)
    4673             :                         {
    4674             :                             const auto nPoints =
    4675         128 :                                 listOfRingsValues->value_length(nRingOffset);
    4676             :                             const auto nPointOffset =
    4677         128 :                                 listOfRingsValues->value_offset(nRingOffset);
    4678             :                             const double *padfRawXValue =
    4679         128 :                                 fieldXDouble->raw_values() + nPointOffset;
    4680             :                             const double *padfRawYValue =
    4681         128 :                                 fieldYDouble->raw_values() + nPointOffset;
    4682         688 :                             for (auto l = decltype(nPoints){0}; l < nPoints;
    4683             :                                  ++l)
    4684             :                             {
    4685         560 :                                 sEnvelope.Merge(padfRawXValue[l],
    4686         560 :                                                 padfRawYValue[l]);
    4687             :                             }
    4688             :                             // for bounding box, only the first ring matters
    4689             : 
    4690         128 :                             if (m_sFilterEnvelope.Intersects(sEnvelope))
    4691             :                             {
    4692          72 :                                 bMatchBBOX = true;
    4693             :                             }
    4694             :                         }
    4695             :                     }
    4696         312 :                     if (bMatchBBOX &&
    4697          72 :                         (m_asAttributeFilterConstraints.empty() ||
    4698           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4699             :                     {
    4700          72 :                         bReturnFeature = true;
    4701          72 :                         break;
    4702             :                     }
    4703             : 
    4704         168 :                     IncrFeatureIdx();
    4705         168 :                     m_nIdxInBatch++;
    4706         168 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4707             :                     {
    4708          24 :                         m_bEOF = !ReadNextBatch();
    4709          24 :                         if (m_bEOF)
    4710          24 :                             return nullptr;
    4711           0 :                         break;
    4712             :                     }
    4713         144 :                 }
    4714          72 :             } while (!bReturnFeature);
    4715             :         }
    4716         738 :         else if (iCol >= 0 &&
    4717         369 :                  m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4718             :                      OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOINT)
    4719             :         {
    4720             :             bool bReturnFeature;
    4721           0 :             do
    4722             :             {
    4723          64 :                 bReturnFeature = false;
    4724          64 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4725          64 :                 CPLAssert(array->type_id() == arrow::Type::LIST);
    4726          64 :                 const auto listArray =
    4727             :                     static_cast<const arrow::ListArray *>(array);
    4728          64 :                 CPLAssert(listArray->values()->type_id() ==
    4729             :                           arrow::Type::STRUCT);
    4730             :                 auto pointValues = std::static_pointer_cast<arrow::StructArray>(
    4731          64 :                     listArray->values());
    4732          64 :                 const auto &fields = pointValues->fields();
    4733          64 :                 const auto &fieldX = fields[0];
    4734          64 :                 CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    4735             :                 const auto fieldXDouble =
    4736          64 :                     static_cast<arrow::DoubleArray *>(fieldX.get());
    4737          64 :                 const auto &fieldY = fields[1];
    4738          64 :                 CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    4739             :                 const auto fieldYDouble =
    4740          64 :                     static_cast<arrow::DoubleArray *>(fieldY.get());
    4741             : 
    4742             :                 while (true)
    4743             :                 {
    4744         160 :                     bool bMatchBBOX = false;
    4745         160 :                     if (!listArray->IsNull(m_nIdxInBatch))
    4746             :                     {
    4747             :                         const auto nPoints =
    4748         128 :                             listArray->value_length(m_nIdxInBatch);
    4749             :                         const auto nPointOffset =
    4750         128 :                             listArray->value_offset(m_nIdxInBatch);
    4751         128 :                         if (nPoints > 0)
    4752             :                         {
    4753             :                             const double *padfRawXValue =
    4754          96 :                                 fieldXDouble->raw_values() + nPointOffset;
    4755             :                             const double *padfRawYValue =
    4756          96 :                                 fieldYDouble->raw_values() + nPointOffset;
    4757         176 :                             for (auto l = decltype(nPoints){0}; l < nPoints;
    4758             :                                  ++l)
    4759             :                             {
    4760         128 :                                 if (padfRawXValue[l] >=
    4761         128 :                                         m_sFilterEnvelope.MinX &&
    4762         108 :                                     padfRawYValue[l] >=
    4763         108 :                                         m_sFilterEnvelope.MinY &&
    4764          88 :                                     padfRawXValue[l] <=
    4765          88 :                                         m_sFilterEnvelope.MaxX &&
    4766          68 :                                     padfRawYValue[l] <= m_sFilterEnvelope.MaxY)
    4767             :                                 {
    4768          48 :                                     bMatchBBOX = true;
    4769          48 :                                     break;
    4770             :                                 }
    4771             :                             }
    4772             :                         }
    4773             :                     }
    4774         208 :                     if (bMatchBBOX &&
    4775          48 :                         (m_asAttributeFilterConstraints.empty() ||
    4776           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4777             :                     {
    4778          48 :                         bReturnFeature = true;
    4779          48 :                         break;
    4780             :                     }
    4781             : 
    4782         112 :                     IncrFeatureIdx();
    4783         112 :                     m_nIdxInBatch++;
    4784         112 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4785             :                     {
    4786          16 :                         m_bEOF = !ReadNextBatch();
    4787          16 :                         if (m_bEOF)
    4788          16 :                             return nullptr;
    4789           0 :                         break;
    4790             :                     }
    4791          96 :                 }
    4792          48 :             } while (!bReturnFeature);
    4793             :         }
    4794         610 :         else if (iCol >= 0 &&
    4795         305 :                  m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4796             :                      OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTILINESTRING)
    4797             :         {
    4798             :             bool bReturnFeature;
    4799           0 :             do
    4800             :             {
    4801          88 :                 bReturnFeature = false;
    4802          88 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4803          88 :                 CPLAssert(array->type_id() == arrow::Type::LIST);
    4804          88 :                 auto listOfPartsArray =
    4805             :                     static_cast<const arrow::ListArray *>(array);
    4806          88 :                 CPLAssert(listOfPartsArray->values()->type_id() ==
    4807             :                           arrow::Type::LIST);
    4808             :                 auto listOfPartsValues =
    4809             :                     std::static_pointer_cast<arrow::ListArray>(
    4810          88 :                         listOfPartsArray->values());
    4811          88 :                 CPLAssert(listOfPartsValues->values()->type_id() ==
    4812             :                           arrow::Type::STRUCT);
    4813             :                 auto pointValues = std::static_pointer_cast<arrow::StructArray>(
    4814          88 :                     listOfPartsValues->values());
    4815          88 :                 const auto &fields = pointValues->fields();
    4816          88 :                 const auto &fieldX = fields[0];
    4817          88 :                 CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    4818             :                 const auto fieldXDouble =
    4819          88 :                     static_cast<arrow::DoubleArray *>(fieldX.get());
    4820          88 :                 const auto &fieldY = fields[1];
    4821          88 :                 CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    4822             :                 const auto fieldYDouble =
    4823          88 :                     static_cast<arrow::DoubleArray *>(fieldY.get());
    4824             : 
    4825             :                 while (true)
    4826             :                 {
    4827         200 :                     bool bMatchBBOX = false;
    4828         200 :                     if (!listOfPartsArray->IsNull(m_nIdxInBatch))
    4829             :                     {
    4830             :                         const auto nParts =
    4831         160 :                             listOfPartsArray->value_length(m_nIdxInBatch);
    4832             :                         const auto nPartOffset =
    4833         160 :                             listOfPartsArray->value_offset(m_nIdxInBatch);
    4834         312 :                         for (auto j = decltype(nParts){0};
    4835         312 :                              j < nParts && !bMatchBBOX; j++)
    4836             :                         {
    4837         152 :                             OGREnvelope sEnvelope;
    4838             :                             const auto nPoints =
    4839         152 :                                 listOfPartsValues->value_length(nPartOffset +
    4840             :                                                                 j);
    4841             :                             const auto nPointOffset =
    4842         152 :                                 listOfPartsValues->value_offset(nPartOffset +
    4843             :                                                                 j);
    4844             :                             const double *padfRawXValue =
    4845         152 :                                 fieldXDouble->raw_values() + nPointOffset;
    4846             :                             const double *padfRawYValue =
    4847         152 :                                 fieldYDouble->raw_values() + nPointOffset;
    4848         488 :                             for (auto l = decltype(nPoints){0}; l < nPoints;
    4849             :                                  ++l)
    4850             :                             {
    4851         336 :                                 sEnvelope.Merge(padfRawXValue[l],
    4852         336 :                                                 padfRawYValue[l]);
    4853             :                             }
    4854             : 
    4855         152 :                             if (m_sFilterEnvelope.Intersects(sEnvelope))
    4856             :                             {
    4857          72 :                                 bMatchBBOX = true;
    4858             :                             }
    4859             :                         }
    4860             :                     }
    4861         272 :                     if (bMatchBBOX &&
    4862          72 :                         (m_asAttributeFilterConstraints.empty() ||
    4863           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4864             :                     {
    4865          72 :                         bReturnFeature = true;
    4866          72 :                         break;
    4867             :                     }
    4868             : 
    4869         128 :                     IncrFeatureIdx();
    4870         128 :                     m_nIdxInBatch++;
    4871         128 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4872             :                     {
    4873          16 :                         m_bEOF = !ReadNextBatch();
    4874          16 :                         if (m_bEOF)
    4875          16 :                             return nullptr;
    4876           0 :                         break;
    4877             :                     }
    4878         112 :                 }
    4879          72 :             } while (!bReturnFeature);
    4880             :         }
    4881         434 :         else if (iCol >= 0 &&
    4882         217 :                  m_aeGeomEncoding[m_iGeomFieldFilter] ==
    4883             :                      OGRArrowGeomEncoding::GEOARROW_STRUCT_MULTIPOLYGON)
    4884             :         {
    4885             :             bool bReturnFeature;
    4886           0 :             do
    4887             :             {
    4888          96 :                 bReturnFeature = false;
    4889          96 :                 auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4890          96 :                 CPLAssert(array->type_id() == arrow::Type::LIST);
    4891          96 :                 auto listOfPartsArray =
    4892             :                     static_cast<const arrow::ListArray *>(array);
    4893          96 :                 CPLAssert(listOfPartsArray->values()->type_id() ==
    4894             :                           arrow::Type::LIST);
    4895             :                 auto listOfPartsValues =
    4896             :                     std::static_pointer_cast<arrow::ListArray>(
    4897          96 :                         listOfPartsArray->values());
    4898          96 :                 CPLAssert(listOfPartsValues->values()->type_id() ==
    4899             :                           arrow::Type::LIST);
    4900             :                 auto listOfRingsValues =
    4901             :                     std::static_pointer_cast<arrow::ListArray>(
    4902          96 :                         listOfPartsValues->values());
    4903          96 :                 CPLAssert(listOfRingsValues->values()->type_id() ==
    4904             :                           arrow::Type::STRUCT);
    4905             :                 auto pointValues = std::static_pointer_cast<arrow::StructArray>(
    4906          96 :                     listOfRingsValues->values());
    4907          96 :                 const auto &fields = pointValues->fields();
    4908          96 :                 const auto &fieldX = fields[0];
    4909          96 :                 CPLAssert(fieldX->type_id() == arrow::Type::DOUBLE);
    4910             :                 const auto fieldXDouble =
    4911          96 :                     static_cast<arrow::DoubleArray *>(fieldX.get());
    4912          96 :                 const auto &fieldY = fields[1];
    4913          96 :                 CPLAssert(fieldY->type_id() == arrow::Type::DOUBLE);
    4914             :                 const auto fieldYDouble =
    4915          96 :                     static_cast<arrow::DoubleArray *>(fieldY.get());
    4916             : 
    4917             :                 while (true)
    4918             :                 {
    4919         240 :                     bool bMatchBBOX = false;
    4920         240 :                     if (!listOfPartsArray->IsNull(m_nIdxInBatch))
    4921             :                     {
    4922             :                         const auto nParts =
    4923         188 :                             listOfPartsArray->value_length(m_nIdxInBatch);
    4924             :                         const auto nPartOffset =
    4925         188 :                             listOfPartsArray->value_offset(m_nIdxInBatch);
    4926         356 :                         for (auto j = decltype(nParts){0};
    4927         356 :                              j < nParts && !bMatchBBOX; j++)
    4928             :                         {
    4929         168 :                             OGREnvelope sEnvelope;
    4930         168 :                             const auto nRings = listOfPartsValues->value_length(
    4931         168 :                                 nPartOffset + j);
    4932             :                             const auto nRingOffset =
    4933         168 :                                 listOfPartsValues->value_offset(nPartOffset +
    4934             :                                                                 j);
    4935         168 :                             if (nRings >= 1)
    4936             :                             {
    4937             :                                 const auto nPoints =
    4938         168 :                                     listOfRingsValues->value_length(
    4939             :                                         nRingOffset);
    4940             :                                 const auto nPointOffset =
    4941         168 :                                     listOfRingsValues->value_offset(
    4942             :                                         nRingOffset);
    4943             :                                 const double *padfRawXValue =
    4944         168 :                                     fieldXDouble->raw_values() + nPointOffset;
    4945             :                                 const double *padfRawYValue =
    4946         168 :                                     fieldYDouble->raw_values() + nPointOffset;
    4947         888 :                                 for (auto l = decltype(nPoints){0}; l < nPoints;
    4948             :                                      ++l)
    4949             :                                 {
    4950         720 :                                     sEnvelope.Merge(padfRawXValue[l],
    4951         720 :                                                     padfRawYValue[l]);
    4952             :                                 }
    4953             : 
    4954         168 :                                 if (m_sFilterEnvelope.Intersects(sEnvelope))
    4955             :                                 {
    4956          72 :                                     bMatchBBOX = true;
    4957             :                                 }
    4958             :                                 // for bounding box, only the first ring matters
    4959             :                             }
    4960             :                         }
    4961             :                     }
    4962         312 :                     if (bMatchBBOX &&
    4963          72 :                         (m_asAttributeFilterConstraints.empty() ||
    4964           0 :                          !SkipToNextFeatureDueToAttributeFilter()))
    4965             :                     {
    4966          72 :                         bReturnFeature = true;
    4967          72 :                         break;
    4968             :                     }
    4969             : 
    4970         168 :                     IncrFeatureIdx();
    4971         168 :                     m_nIdxInBatch++;
    4972         168 :                     if (m_nIdxInBatch == m_poBatch->num_rows())
    4973             :                     {
    4974          24 :                         m_bEOF = !ReadNextBatch();
    4975          24 :                         if (m_bEOF)
    4976          24 :                             return nullptr;
    4977           0 :                         break;
    4978             :                     }
    4979         144 :                 }
    4980          72 :             } while (!bReturnFeature);
    4981             :         }
    4982         121 :         else if (iCol >= 0)
    4983             :         {
    4984         121 :             auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    4985             :             while (true)
    4986             :             {
    4987         220 :                 bool bMatchBBOX = false;
    4988             : 
    4989             :                 auto poGeometry = std::unique_ptr<OGRGeometry>(
    4990         220 :                     ReadGeometry(m_iGeomFieldFilter, array, m_nIdxInBatch));
    4991         220 :                 if (poGeometry && !poGeometry->IsEmpty())
    4992             :                 {
    4993         115 :                     OGREnvelope sEnvelope;
    4994         115 :                     poGeometry->getEnvelope(&sEnvelope);
    4995         115 :                     if (m_sFilterEnvelope.Intersects(sEnvelope))
    4996             :                     {
    4997          88 :                         bMatchBBOX = true;
    4998             :                     }
    4999             :                 }
    5000         220 :                 if (bMatchBBOX && (m_asAttributeFilterConstraints.empty() ||
    5001           0 :                                    !SkipToNextFeatureDueToAttributeFilter()))
    5002             :                 {
    5003          88 :                     break;
    5004             :                 }
    5005             : 
    5006         132 :                 IncrFeatureIdx();
    5007         132 :                 m_nIdxInBatch++;
    5008         132 :                 if (m_nIdxInBatch == m_poBatch->num_rows())
    5009             :                 {
    5010          33 :                     m_bEOF = !ReadNextBatch();
    5011          33 :                     if (m_bEOF)
    5012          33 :                         return nullptr;
    5013           0 :                     array = GetStorageArray(m_poBatchColumns[iCol].get());
    5014             :                 }
    5015          99 :             }
    5016         816 :         }
    5017             :     }
    5018             : 
    5019        9042 :     else if (!m_asAttributeFilterConstraints.empty())
    5020             :     {
    5021             :         while (true)
    5022             :         {
    5023         438 :             if (!SkipToNextFeatureDueToAttributeFilter())
    5024             :             {
    5025         236 :                 break;
    5026             :             }
    5027             : 
    5028         202 :             IncrFeatureIdx();
    5029         202 :             m_nIdxInBatch++;
    5030         202 :             if (m_nIdxInBatch == m_poBatch->num_rows())
    5031             :             {
    5032          90 :                 m_bEOF = !ReadNextBatch();
    5033          90 :                 if (m_bEOF)
    5034          52 :                     return nullptr;
    5035             :             }
    5036             :         }
    5037             :     }
    5038             : 
    5039        9806 :     auto poFeature = ReadFeature(m_nIdxInBatch, m_poBatchColumns);
    5040             : 
    5041        9806 :     if (m_iFIDArrowColumn < 0)
    5042        6412 :         poFeature->SetFID(m_nFeatureIdx);
    5043             : 
    5044        9806 :     IncrFeatureIdx();
    5045        9806 :     m_nIdxInBatch++;
    5046             : 
    5047        9806 :     return poFeature;
    5048             : }
    5049             : 
    5050             : /************************************************************************/
    5051             : /*                       GetExtentFromMetadata()                        */
    5052             : /************************************************************************/
    5053             : 
    5054             : inline OGRErr
    5055         756 : OGRArrowLayer::GetExtentFromMetadata(const CPLJSONObject &oJSONDef,
    5056             :                                      OGREnvelope3D *psExtent)
    5057             : {
    5058        2268 :     const auto oBBox = oJSONDef.GetArray("bbox");
    5059         756 :     if (oBBox.IsValid() && oBBox.Size() == 4)
    5060             :     {
    5061         451 :         psExtent->MinX = oBBox[0].ToDouble();
    5062         451 :         psExtent->MinY = oBBox[1].ToDouble();
    5063         451 :         psExtent->MinZ = std::numeric_limits<double>::infinity();
    5064         451 :         psExtent->MaxX = oBBox[2].ToDouble();
    5065         451 :         psExtent->MaxY = oBBox[3].ToDouble();
    5066         451 :         psExtent->MaxZ = -std::numeric_limits<double>::infinity();
    5067         451 :         if (psExtent->MinX <= psExtent->MaxX)
    5068         451 :             return OGRERR_NONE;
    5069             :     }
    5070         305 :     else if (oBBox.IsValid() && oBBox.Size() == 6)
    5071             :     {
    5072         236 :         psExtent->MinX = oBBox[0].ToDouble();
    5073         236 :         psExtent->MinY = oBBox[1].ToDouble();
    5074         236 :         psExtent->MinZ = oBBox[2].ToDouble();
    5075         236 :         psExtent->MaxX = oBBox[3].ToDouble();
    5076         236 :         psExtent->MaxY = oBBox[4].ToDouble();
    5077         236 :         psExtent->MaxZ = oBBox[5].ToDouble();
    5078         236 :         if (psExtent->MinX <= psExtent->MaxX)
    5079         236 :             return OGRERR_NONE;
    5080             :     }
    5081          69 :     return OGRERR_FAILURE;
    5082             : }
    5083             : 
    5084             : /************************************************************************/
    5085             : /*                        ISetSpatialFilter()                           */
    5086             : /************************************************************************/
    5087             : 
    5088        1328 : inline OGRErr OGRArrowLayer::ISetSpatialFilter(int iGeomField,
    5089             :                                                const OGRGeometry *poGeomIn)
    5090             : 
    5091             : {
    5092             :     // When changing filters, we need to invalidate cached batches, as
    5093             :     // PostFilterArrowArray() has potentially modified array contents
    5094        1328 :     if (m_poFilterGeom)
    5095         777 :         InvalidateCachedBatches();
    5096             : 
    5097        1328 :     m_bSpatialFilterIntersectsLayerExtent = true;
    5098        1328 :     if (iGeomField < GetLayerDefn()->GetGeomFieldCount())
    5099             :     {
    5100        1306 :         m_iGeomFieldFilter = iGeomField;
    5101        1306 :         if (InstallFilter(poGeomIn))
    5102        1105 :             ResetReading();
    5103        1306 :         if (m_poFilterGeom != nullptr)
    5104             :         {
    5105        1041 :             OGREnvelope sLayerExtent;
    5106        1041 :             if (FastGetExtent(iGeomField, &sLayerExtent))
    5107             :             {
    5108         619 :                 m_bSpatialFilterIntersectsLayerExtent =
    5109         619 :                     m_sFilterEnvelope.Intersects(sLayerExtent);
    5110             :             }
    5111             :         }
    5112             :     }
    5113             : 
    5114        1328 :     SetBatch(m_poBatch);
    5115        1328 :     return OGRERR_NONE;
    5116             : }
    5117             : 
    5118             : /************************************************************************/
    5119             : /*                         FastGetExtent()                              */
    5120             : /************************************************************************/
    5121             : 
    5122         723 : inline bool OGRArrowLayer::FastGetExtent(int iGeomField,
    5123             :                                          OGREnvelope *psExtent) const
    5124             : {
    5125             :     {
    5126         723 :         const auto oIter = m_oMapExtents.find(iGeomField);
    5127         723 :         if (oIter != m_oMapExtents.end())
    5128             :         {
    5129           5 :             *psExtent = oIter->second;
    5130           5 :             return true;
    5131             :         }
    5132             :     }
    5133             : 
    5134             :     const char *pszGeomFieldName =
    5135         718 :         m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
    5136         718 :     const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
    5137        1434 :     if (oIter != m_oMapGeometryColumns.end() &&
    5138         716 :         CPLTestBool(CPLGetConfigOption(
    5139        1434 :             ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
    5140             :     {
    5141         710 :         const auto &oJSONDef = oIter->second;
    5142         710 :         OGREnvelope3D sEnvelope3D;
    5143         710 :         if (GetExtentFromMetadata(oJSONDef, &sEnvelope3D) == OGRERR_NONE)
    5144             :         {
    5145         651 :             *psExtent = sEnvelope3D;
    5146         651 :             return true;
    5147             :         }
    5148             :     }
    5149          67 :     return false;
    5150             : }
    5151             : 
    5152             : /************************************************************************/
    5153             : /*                           IGetExtent()                               */
    5154             : /************************************************************************/
    5155             : 
    5156          62 : inline OGRErr OGRArrowLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    5157             :                                         bool bForce)
    5158             : {
    5159          62 :     if (FastGetExtent(iGeomField, psExtent))
    5160             :     {
    5161          41 :         return OGRERR_NONE;
    5162             :     }
    5163             : 
    5164          21 :     if (!bForce && !CanRunNonForcedGetExtent())
    5165             :     {
    5166           0 :         return OGRERR_FAILURE;
    5167             :     }
    5168             : 
    5169             :     int iCol;
    5170          21 :     if (m_bIgnoredFields)
    5171             :     {
    5172           0 :         iCol = m_anMapGeomFieldIndexToArrayIndex[iGeomField];
    5173             :     }
    5174             :     else
    5175             :     {
    5176          21 :         iCol = m_anMapGeomFieldIndexToArrowColumn[iGeomField];
    5177             :     }
    5178          21 :     if (iCol < 0)
    5179             :     {
    5180           0 :         return OGRERR_FAILURE;
    5181             :     }
    5182             : 
    5183          21 :     if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB)
    5184             :     {
    5185           7 :         ResetReading();
    5186           7 :         if (m_poBatch == nullptr)
    5187             :         {
    5188           6 :             m_bEOF = !ReadNextBatch();
    5189           6 :             if (m_bEOF)
    5190           0 :                 return OGRERR_FAILURE;
    5191             :         }
    5192           7 :         *psExtent = OGREnvelope();
    5193             : 
    5194           7 :         auto array = GetStorageArray(m_poBatchColumns[iCol].get());
    5195           7 :         const arrow::BinaryArray *smallArray = nullptr;
    5196           7 :         const arrow::LargeBinaryArray *largeArray = nullptr;
    5197           7 :         if (array->type_id() == arrow::Type::BINARY)
    5198           7 :             smallArray = static_cast<const arrow::BinaryArray *>(array);
    5199             :         else
    5200             :         {
    5201           0 :             CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
    5202           0 :             largeArray = static_cast<const arrow::LargeBinaryArray *>(array);
    5203             :         }
    5204           7 :         OGREnvelope sEnvelope;
    5205             :         while (true)
    5206             :         {
    5207          32 :             if (!array->IsNull(m_nIdxInBatch))
    5208             :             {
    5209          30 :                 if (smallArray)
    5210             :                 {
    5211          30 :                     int out_length = 0;
    5212             :                     const uint8_t *data =
    5213          30 :                         smallArray->GetValue(m_nIdxInBatch, &out_length);
    5214          30 :                     if (OGRWKBGetBoundingBox(data, out_length, sEnvelope))
    5215             :                     {
    5216          30 :                         psExtent->Merge(sEnvelope);
    5217             :                     }
    5218             :                 }
    5219             :                 else
    5220             :                 {
    5221           0 :                     assert(largeArray);
    5222           0 :                     int64_t out_length = 0;
    5223             :                     const uint8_t *data =
    5224           0 :                         largeArray->GetValue(m_nIdxInBatch, &out_length);
    5225           0 :                     if (out_length < INT_MAX &&
    5226           0 :                         OGRWKBGetBoundingBox(data, static_cast<int>(out_length),
    5227             :                                              sEnvelope))
    5228             :                     {
    5229           0 :                         psExtent->Merge(sEnvelope);
    5230             :                     }
    5231             :                 }
    5232             :             }
    5233             : 
    5234          32 :             m_nIdxInBatch++;
    5235          32 :             if (m_nIdxInBatch == m_poBatch->num_rows())
    5236             :             {
    5237           7 :                 m_bEOF = !ReadNextBatch();
    5238           7 :                 if (m_bEOF)
    5239             :                 {
    5240           7 :                     ResetReading();
    5241           7 :                     if (psExtent->IsInit())
    5242             :                     {
    5243           7 :                         m_oMapExtents[iGeomField] = *psExtent;
    5244           7 :                         return OGRERR_NONE;
    5245             :                     }
    5246           0 :                     return OGRERR_FAILURE;
    5247             :                 }
    5248           0 :                 array = GetStorageArray(m_poBatchColumns[iCol].get());
    5249           0 :                 if (array->type_id() == arrow::Type::BINARY)
    5250           0 :                     smallArray = static_cast<const arrow::BinaryArray *>(array);
    5251             :                 else
    5252             :                 {
    5253           0 :                     CPLAssert(array->type_id() == arrow::Type::LARGE_BINARY);
    5254           0 :                     largeArray =
    5255             :                         static_cast<const arrow::LargeBinaryArray *>(array);
    5256             :                 }
    5257             :             }
    5258          25 :         }
    5259             :     }
    5260          14 :     else if (m_aeGeomEncoding[iGeomField] ==
    5261             :              OGRArrowGeomEncoding::GEOARROW_FSL_MULTIPOLYGON)
    5262             :     {
    5263           0 :         ResetReading();
    5264           0 :         if (m_poBatch == nullptr)
    5265             :         {
    5266           0 :             m_bEOF = !ReadNextBatch();
    5267           0 :             if (m_bEOF)
    5268           0 :                 return OGRERR_FAILURE;
    5269             :         }
    5270           0 :         *psExtent = OGREnvelope();
    5271             : 
    5272             :         const auto poGeomFieldDefn =
    5273           0 :             m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
    5274           0 :         const auto eGeomType = poGeomFieldDefn->GetType();
    5275           0 :         const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeomType));
    5276           0 :         const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeomType));
    5277           0 :         const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
    5278             : 
    5279           0 :     begin_multipolygon:
    5280           0 :         auto array = m_poBatchColumns[iCol].get();
    5281           0 :         CPLAssert(array->type_id() == arrow::Type::LIST);
    5282           0 :         auto listOfPartsArray = static_cast<const arrow::ListArray *>(array);
    5283           0 :         CPLAssert(listOfPartsArray->values()->type_id() == arrow::Type::LIST);
    5284             :         auto listOfPartsValues = std::static_pointer_cast<arrow::ListArray>(
    5285           0 :             listOfPartsArray->values());
    5286           0 :         CPLAssert(listOfPartsValues->values()->type_id() == arrow::Type::LIST);
    5287             :         auto listOfRingsValues = std::static_pointer_cast<arrow::ListArray>(
    5288           0 :             listOfPartsValues->values());
    5289           0 :         CPLAssert(listOfRingsValues->values()->type_id() ==
    5290             :                   arrow::Type::FIXED_SIZE_LIST);
    5291             :         auto listOfPointsValues =
    5292             :             std::static_pointer_cast<arrow::FixedSizeListArray>(
    5293           0 :                 listOfRingsValues->values());
    5294           0 :         CPLAssert(listOfPointsValues->values()->type_id() ==
    5295             :                   arrow::Type::DOUBLE);
    5296             :         auto pointValues = std::static_pointer_cast<arrow::DoubleArray>(
    5297           0 :             listOfPointsValues->values());
    5298             : 
    5299             :         while (true)
    5300             :         {
    5301           0 :             if (!listOfPartsArray->IsNull(m_nIdxInBatch))
    5302             :             {
    5303             :                 const auto nParts =
    5304           0 :                     listOfPartsArray->value_length(m_nIdxInBatch);
    5305             :                 const auto nPartOffset =
    5306           0 :                     listOfPartsArray->value_offset(m_nIdxInBatch);
    5307           0 :                 for (auto j = decltype(nParts){0}; j < nParts; j++)
    5308             :                 {
    5309             :                     const auto nRings =
    5310           0 :                         listOfPartsValues->value_length(nPartOffset + j);
    5311             :                     const auto nRingOffset =
    5312           0 :                         listOfPartsValues->value_offset(nPartOffset + j);
    5313           0 :                     if (nRings >= 1)
    5314             :                     {
    5315             :                         const auto nPoints =
    5316           0 :                             listOfRingsValues->value_length(nRingOffset);
    5317             :                         const auto nPointOffset =
    5318           0 :                             listOfRingsValues->value_offset(nRingOffset) * nDim;
    5319             :                         const double *padfRawValue =
    5320           0 :                             pointValues->raw_values() + nPointOffset;
    5321           0 :                         for (auto l = decltype(nPoints){0}; l < nPoints; ++l)
    5322             :                         {
    5323           0 :                             psExtent->Merge(padfRawValue[nDim * l],
    5324           0 :                                             padfRawValue[nDim * l + 1]);
    5325             :                         }
    5326             :                         // for bounding box, only the first ring matters
    5327             :                     }
    5328             :                 }
    5329             :             }
    5330             : 
    5331           0 :             m_nIdxInBatch++;
    5332           0 :             if (m_nIdxInBatch == m_poBatch->num_rows())
    5333             :             {
    5334           0 :                 m_bEOF = !ReadNextBatch();
    5335           0 :                 if (m_bEOF)
    5336             :                 {
    5337           0 :                     ResetReading();
    5338           0 :                     if (psExtent->IsInit())
    5339             :                     {
    5340           0 :                         m_oMapExtents[iGeomField] = *psExtent;
    5341           0 :                         return OGRERR_NONE;
    5342             :                     }
    5343           0 :                     return OGRERR_FAILURE;
    5344             :                 }
    5345           0 :                 goto begin_multipolygon;
    5346             :             }
    5347           0 :         }
    5348             :     }
    5349             : 
    5350          14 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    5351             : }
    5352             : 
    5353             : /************************************************************************/
    5354             : /*                        FastGetExtent3D()                             */
    5355             : /************************************************************************/
    5356             : 
    5357          38 : inline bool OGRArrowLayer::FastGetExtent3D(int iGeomField,
    5358             :                                            OGREnvelope3D *psExtent) const
    5359             : {
    5360             :     {
    5361          38 :         const auto oIter = m_oMapExtents3D.find(iGeomField);
    5362          38 :         if (oIter != m_oMapExtents3D.end())
    5363             :         {
    5364           0 :             *psExtent = oIter->second;
    5365           0 :             return true;
    5366             :         }
    5367             :     }
    5368             : 
    5369             :     const char *pszGeomFieldName =
    5370          38 :         m_poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetNameRef();
    5371          38 :     const auto oIter = m_oMapGeometryColumns.find(pszGeomFieldName);
    5372          76 :     if (oIter != m_oMapGeometryColumns.end() &&
    5373          38 :         CPLTestBool(CPLGetConfigOption(
    5374          76 :             ("OGR_" + GetDriverUCName() + "_USE_BBOX").c_str(), "YES")))
    5375             :     {
    5376          36 :         const auto &oJSONDef = oIter->second;
    5377          64 :         if (GetExtentFromMetadata(oJSONDef, psExtent) == OGRERR_NONE &&
    5378          28 :             psExtent->Is3D())
    5379             :         {
    5380           2 :             return true;
    5381             :         }
    5382             :     }
    5383          36 :     return false;
    5384             : }
    5385             : 
    5386             : /************************************************************************/
    5387             : /*                          IGetExtent3D()                              */
    5388             : /************************************************************************/
    5389             : 
    5390          19 : inline OGRErr OGRArrowLayer::IGetExtent3D(int iGeomField,
    5391             :                                           OGREnvelope3D *psExtent, bool bForce)
    5392             : {
    5393          19 :     if (FastGetExtent3D(iGeomField, psExtent))
    5394             :     {
    5395           1 :         return OGRERR_NONE;
    5396             :     }
    5397             : 
    5398          18 :     return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
    5399             : }
    5400             : 
    5401             : /************************************************************************/
    5402             : /*                  OverrideArrowSchemaRelease()                        */
    5403             : /************************************************************************/
    5404             : 
    5405             : template <class T>
    5406         609 : static void OverrideArrowRelease(OGRArrowDataset *poDS, T *obj)
    5407             : {
    5408             :     // We override the release callback, since it can use the memory pool,
    5409             :     // and we need to make sure it is still alive when the object (ArrowArray
    5410             :     // or ArrowSchema) is deleted
    5411         609 :     struct OverriddenPrivate
    5412             :     {
    5413             :         OverriddenPrivate() = default;
    5414             :         OverriddenPrivate(const OverriddenPrivate &) = delete;
    5415             :         OverriddenPrivate &operator=(const OverriddenPrivate &) = delete;
    5416             : 
    5417             :         std::shared_ptr<arrow::MemoryPool> poMemoryPool{};
    5418             :         void (*pfnPreviousRelease)(T *) = nullptr;
    5419             :         void *pPreviousPrivateData = nullptr;
    5420             : 
    5421         609 :         static void release(T *l_obj)
    5422             :         {
    5423         609 :             OverriddenPrivate *myPrivate =
    5424             :                 static_cast<OverriddenPrivate *>(l_obj->private_data);
    5425         609 :             l_obj->private_data = myPrivate->pPreviousPrivateData;
    5426         609 :             l_obj->release = myPrivate->pfnPreviousRelease;
    5427         609 :             l_obj->release(l_obj);
    5428         609 :             delete myPrivate;
    5429         609 :         }
    5430             :     };
    5431             : 
    5432         609 :     auto overriddenPrivate = new OverriddenPrivate();
    5433         609 :     overriddenPrivate->poMemoryPool = poDS->GetSharedMemoryPool();
    5434         609 :     overriddenPrivate->pPreviousPrivateData = obj->private_data;
    5435         609 :     overriddenPrivate->pfnPreviousRelease = obj->release;
    5436             : 
    5437         609 :     obj->release = OverriddenPrivate::release;
    5438         609 :     obj->private_data = overriddenPrivate;
    5439         609 : }
    5440             : 
    5441             : /************************************************************************/
    5442             : /*                   UseRecordBatchBaseImplementation()                 */
    5443             : /************************************************************************/
    5444             : 
    5445         474 : inline bool OGRArrowLayer::UseRecordBatchBaseImplementation() const
    5446             : {
    5447         474 :     if (CPLTestBool(CPLGetConfigOption("OGR_ARROW_STREAM_BASE_IMPL", "NO")))
    5448             :     {
    5449           4 :         return true;
    5450             :     }
    5451             : 
    5452         470 :     if (m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
    5453             :                                                false))
    5454             :     {
    5455           1 :         const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    5456          14 :         for (int i = 0; i < nFieldCount; ++i)
    5457             :         {
    5458          14 :             const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    5459          28 :             if (!poFieldDefn->IsIgnored() &&
    5460          14 :                 poFieldDefn->GetType() == OFTDateTime)
    5461             :             {
    5462           1 :                 CPLDebug("ARROW",
    5463             :                          "DATETIME_AS_STRING=YES not compatible of fast "
    5464             :                          "Arrow implementation");
    5465           1 :                 return true;
    5466             :             }
    5467             :         }
    5468             :     }
    5469             : 
    5470         469 :     if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
    5471             :                   "GEOMETRY_ENCODING", ""),
    5472             :               "WKB"))
    5473             :     {
    5474         131 :         const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
    5475         181 :         for (int i = 0; i < nGeomFieldCount; i++)
    5476             :         {
    5477         146 :             if (!m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored() &&
    5478         259 :                 m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB &&
    5479         113 :                 m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKT)
    5480             :             {
    5481          96 :                 CPLDebug("ARROW", "Geometry encoding not compatible of fast "
    5482             :                                   "Arrow implementation");
    5483          96 :                 return true;
    5484             :             }
    5485             :         }
    5486             :     }
    5487             : 
    5488         373 :     if (m_bIgnoredFields)
    5489             :     {
    5490             :         std::vector<int> ignoredState(m_anMapFieldIndexToArrowColumn.size(),
    5491         106 :                                       -1);
    5492        7658 :         for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
    5493             :         {
    5494        7553 :             const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
    5495        7553 :             if (nArrowCol >= static_cast<int>(ignoredState.size()))
    5496           1 :                 ignoredState.resize(nArrowCol + 1, -1);
    5497             :             const auto bIsIgnored =
    5498        7553 :                 m_poFeatureDefn->GetFieldDefn(static_cast<int>(i))->IsIgnored();
    5499        7553 :             if (ignoredState[nArrowCol] < 0)
    5500             :             {
    5501        7095 :                 ignoredState[nArrowCol] = static_cast<int>(bIsIgnored);
    5502             :             }
    5503             :             else
    5504             :             {
    5505             :                 // struct fields will point to the same arrow column
    5506         458 :                 if (ignoredState[nArrowCol] != static_cast<int>(bIsIgnored))
    5507             :                 {
    5508           1 :                     CPLDebug("ARROW",
    5509             :                              "Inconsistent ignore state for Arrow Columns");
    5510           1 :                     return true;
    5511             :                 }
    5512             :             }
    5513             :         }
    5514             :     }
    5515             : 
    5516         372 :     if (m_poAttrQuery || m_poFilterGeom)
    5517             :     {
    5518         163 :         struct ArrowSchema *psSchema = &m_sCachedSchema;
    5519         163 :         if (psSchema->release)
    5520         104 :             psSchema->release(psSchema);
    5521         163 :         memset(psSchema, 0, sizeof(*psSchema));
    5522             : 
    5523         326 :         const bool bCanPostFilter = GetArrowSchemaInternal(psSchema) == 0 &&
    5524         163 :                                     CanPostFilterArrowArray(psSchema);
    5525         163 :         if (!bCanPostFilter)
    5526          11 :             return true;
    5527             :     }
    5528             : 
    5529         361 :     return false;
    5530             : }
    5531             : 
    5532             : /************************************************************************/
    5533             : /*                          GetArrowStream()                            */
    5534             : /************************************************************************/
    5535             : 
    5536         293 : inline bool OGRArrowLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
    5537             :                                           CSLConstList papszOptions)
    5538             : {
    5539         293 :     if (!OGRLayer::GetArrowStream(out_stream, papszOptions))
    5540           0 :         return false;
    5541             : 
    5542         293 :     m_bUseRecordBatchBaseImplementation = UseRecordBatchBaseImplementation();
    5543         293 :     return true;
    5544             : }
    5545             : 
    5546             : /************************************************************************/
    5547             : /*                         GetArrowSchema()                             */
    5548             : /************************************************************************/
    5549             : 
    5550         433 : inline int OGRArrowLayer::GetArrowSchema(struct ArrowArrayStream *stream,
    5551             :                                          struct ArrowSchema *out_schema)
    5552             : {
    5553         433 :     if (m_bUseRecordBatchBaseImplementation)
    5554         209 :         return OGRLayer::GetArrowSchema(stream, out_schema);
    5555             : 
    5556         224 :     return GetArrowSchemaInternal(out_schema);
    5557             : }
    5558             : 
    5559             : /************************************************************************/
    5560             : /*                     GetArrowSchemaInternal()                         */
    5561             : /************************************************************************/
    5562             : 
    5563       10800 : static bool IsSilentlyIgnoredFormatForGetArrowSchemaArray(const char *format)
    5564             : {
    5565             :     // n: null
    5566       10800 :     return strcmp(format, "n") == 0;
    5567             : }
    5568             : 
    5569             : inline int
    5570         387 : OGRArrowLayer::GetArrowSchemaInternal(struct ArrowSchema *out_schema) const
    5571             : {
    5572         774 :     auto status = arrow::ExportSchema(*m_poSchema, out_schema);
    5573         387 :     if (!status.ok())
    5574             :     {
    5575           0 :         CPLError(CE_Failure, CPLE_AppDefined, "ExportSchema() failed with %s",
    5576           0 :                  status.message().c_str());
    5577           0 :         return EIO;
    5578             :     }
    5579             : 
    5580         387 :     CPLAssert(out_schema->n_children == m_poSchema->num_fields());
    5581             : 
    5582             :     // Remove ignored fields from the ArrowSchema.
    5583             : 
    5584             :     struct FieldDesc
    5585             :     {
    5586             :         bool bIsRegularField =
    5587             :             false;  // true = attribute field, false = geometry field
    5588             :         int nIdx = -1;
    5589             :     };
    5590             : 
    5591             :     // cppcheck-suppress unreadVariable
    5592             :     std::vector<FieldDesc> fieldDesc(
    5593         774 :         static_cast<size_t>(out_schema->n_children));
    5594       21816 :     for (size_t i = 0; i < m_anMapFieldIndexToArrowColumn.size(); i++)
    5595             :     {
    5596       21429 :         const int nArrowCol = m_anMapFieldIndexToArrowColumn[i][0];
    5597       21429 :         if (fieldDesc[nArrowCol].nIdx < 0)
    5598             :         {
    5599       20148 :             fieldDesc[nArrowCol].bIsRegularField = true;
    5600       20148 :             fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
    5601             :         }
    5602             :     }
    5603         788 :     for (size_t i = 0; i < m_anMapGeomFieldIndexToArrowColumn.size(); i++)
    5604             :     {
    5605         401 :         const int nArrowCol = m_anMapGeomFieldIndexToArrowColumn[i];
    5606         401 :         CPLAssert(fieldDesc[nArrowCol].nIdx < 0);
    5607         401 :         fieldDesc[nArrowCol].bIsRegularField = false;
    5608         401 :         fieldDesc[nArrowCol].nIdx = static_cast<int>(i);
    5609             :     }
    5610             : 
    5611         387 :     int j = 0;
    5612             :     const char *pszReqGeomEncoding =
    5613         387 :         m_aosArrowArrayStreamOptions.FetchNameValueDef("GEOMETRY_ENCODING", "");
    5614             : 
    5615         387 :     const char *pszExtensionName = EXTENSION_NAME_OGC_WKB;
    5616         387 :     if (EQUAL(pszReqGeomEncoding, "WKB") || EQUAL(pszReqGeomEncoding, ""))
    5617             :     {
    5618             :         const char *const pszGeometryMetadataEncoding =
    5619         387 :             m_aosArrowArrayStreamOptions.FetchNameValue(
    5620             :                 "GEOMETRY_METADATA_ENCODING");
    5621         387 :         if (pszGeometryMetadataEncoding)
    5622             :         {
    5623           0 :             if (EQUAL(pszGeometryMetadataEncoding, "OGC"))
    5624           0 :                 pszExtensionName = EXTENSION_NAME_OGC_WKB;
    5625           0 :             else if (EQUAL(pszGeometryMetadataEncoding, "GEOARROW"))
    5626           0 :                 pszExtensionName = EXTENSION_NAME_GEOARROW_WKB;
    5627             :             else
    5628           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    5629             :                          "Unsupported GEOMETRY_METADATA_ENCODING value: %s",
    5630             :                          pszGeometryMetadataEncoding);
    5631             :         }
    5632             :     }
    5633             : 
    5634       21294 :     for (int i = 0; i < out_schema->n_children; ++i)
    5635             :     {
    5636       20907 :         if (fieldDesc[i].nIdx < 0)
    5637             :         {
    5638         358 :             if (m_iFIDArrowColumn == i)
    5639             :             {
    5640          21 :                 out_schema->children[j] = out_schema->children[i];
    5641          21 :                 ++j;
    5642             :             }
    5643         337 :             else if (cpl::contains(m_oSetBBoxArrowColumns, i))
    5644             :             {
    5645             :                 // Remove bounding box columns from exported schema
    5646          82 :                 out_schema->children[i]->release(out_schema->children[i]);
    5647          82 :                 out_schema->children[i] = nullptr;
    5648             :             }
    5649         255 :             else if (IsSilentlyIgnoredFormatForGetArrowSchemaArray(
    5650         255 :                          out_schema->children[i]->format))
    5651             :             {
    5652             :                 // Silently ignore columns with null data type...
    5653         255 :                 out_schema->children[i]->release(out_schema->children[i]);
    5654             :             }
    5655             :             else
    5656             :             {
    5657             :                 // can happen with data types we don't support
    5658           0 :                 if (m_aosArrowArrayStreamOptions.FetchBool(
    5659             :                         "SILENCE_GET_SCHEMA_ERROR", false))
    5660             :                 {
    5661           0 :                     CPLDebug(GetDriverUCName().c_str(),
    5662             :                              "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
    5663             :                              "not expected: name=%s, format=%s",
    5664           0 :                              i, out_schema->children[i]->name,
    5665           0 :                              out_schema->children[i]->format);
    5666             :                 }
    5667             :                 else
    5668             :                 {
    5669           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    5670             :                              "GetArrowSchema() error: fieldDesc[%d].nIdx < 0 "
    5671             :                              "not expected: name=%s, format=%s",
    5672           0 :                              i, out_schema->children[i]->name,
    5673           0 :                              out_schema->children[i]->format);
    5674             :                 }
    5675           0 :                 for (; i < out_schema->n_children; ++i, ++j)
    5676           0 :                     out_schema->children[j] = out_schema->children[i];
    5677           0 :                 out_schema->n_children = j;
    5678             : 
    5679           0 :                 out_schema->release(out_schema);
    5680             : 
    5681           0 :                 return EIO;
    5682             :             }
    5683         358 :             continue;
    5684             :         }
    5685             : 
    5686             :         const auto bIsIgnored =
    5687       20549 :             fieldDesc[i].bIsRegularField
    5688       20549 :                 ? m_poFeatureDefn->GetFieldDefn(fieldDesc[i].nIdx)->IsIgnored()
    5689         401 :                 : m_poFeatureDefn->GetGeomFieldDefn(fieldDesc[i].nIdx)
    5690         401 :                       ->IsIgnored();
    5691       20549 :         if (bIsIgnored)
    5692             :         {
    5693        2911 :             out_schema->children[i]->release(out_schema->children[i]);
    5694             :         }
    5695             :         else
    5696             :         {
    5697       18021 :             if (!fieldDesc[i].bIsRegularField &&
    5698         383 :                 EQUAL(pszReqGeomEncoding, "WKB"))
    5699             :             {
    5700          64 :                 const int iGeomField = fieldDesc[i].nIdx;
    5701          64 :                 if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKT)
    5702             :                 {
    5703             :                     const auto poGeomFieldDefn =
    5704          17 :                         m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
    5705          17 :                     CPLAssert(strcmp(out_schema->children[i]->name,
    5706             :                                      poGeomFieldDefn->GetNameRef()) == 0);
    5707          17 :                     auto poSchema = CreateSchemaForWKBGeometryColumn(
    5708             :                         poGeomFieldDefn, "z", pszExtensionName);
    5709          17 :                     out_schema->children[i]->release(out_schema->children[i]);
    5710          17 :                     *(out_schema->children[j]) = *poSchema;
    5711          17 :                     CPLFree(poSchema);
    5712             :                 }
    5713          47 :                 else if (m_aeGeomEncoding[iGeomField] !=
    5714             :                          OGRArrowGeomEncoding::WKB)
    5715             :                 {
    5716             :                     // Shouldn't happen if UseRecordBatchBaseImplementation()
    5717             :                     // is up to date
    5718           0 :                     CPLAssert(false);
    5719             :                 }
    5720             :                 else
    5721             :                 {
    5722          47 :                     out_schema->children[j] = out_schema->children[i];
    5723             :                 }
    5724             :             }
    5725             :             else
    5726             :             {
    5727       17574 :                 out_schema->children[j] = out_schema->children[i];
    5728             :             }
    5729             : 
    5730       18021 :             if (!fieldDesc[i].bIsRegularField &&
    5731         383 :                 (EQUAL(pszReqGeomEncoding, "WKB") ||
    5732         319 :                  EQUAL(pszReqGeomEncoding, "")))
    5733             :             {
    5734         383 :                 const int iGeomField = fieldDesc[i].nIdx;
    5735         383 :                 const char *pszFormat = out_schema->children[j]->format;
    5736         383 :                 if (m_aeGeomEncoding[iGeomField] == OGRArrowGeomEncoding::WKB &&
    5737         695 :                     !out_schema->children[j]->metadata &&
    5738         312 :                     (strcmp(pszFormat, "z") == 0 ||
    5739           1 :                      strcmp(pszFormat, "Z") == 0))
    5740             :                 {
    5741             :                     const auto poGeomFieldDefn =
    5742         312 :                         m_poFeatureDefn->GetGeomFieldDefn(iGeomField);
    5743             :                     // Set ARROW:extension:name = ogc:wkb
    5744         312 :                     auto poSchema = CreateSchemaForWKBGeometryColumn(
    5745             :                         poGeomFieldDefn, pszFormat, pszExtensionName);
    5746         312 :                     out_schema->children[i]->release(out_schema->children[i]);
    5747         312 :                     *(out_schema->children[j]) = *poSchema;
    5748         312 :                     CPLFree(poSchema);
    5749             :                 }
    5750             :             }
    5751             : 
    5752       17638 :             ++j;
    5753             :         }
    5754             :     }
    5755             : 
    5756         387 :     out_schema->n_children = j;
    5757             : 
    5758         387 :     OverrideArrowRelease(m_poArrowDS, out_schema);
    5759             : 
    5760         387 :     return 0;
    5761             : }
    5762             : 
    5763             : /************************************************************************/
    5764             : /*                       GetNextArrowArray()                            */
    5765             : /************************************************************************/
    5766             : 
    5767         604 : inline int OGRArrowLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
    5768             :                                             struct ArrowArray *out_array)
    5769             : {
    5770         604 :     if (m_bUseRecordBatchBaseImplementation)
    5771         224 :         return OGRLayer::GetNextArrowArray(stream, out_array);
    5772             : 
    5773             :     while (true)
    5774             :     {
    5775         398 :         if (m_bEOF)
    5776             :         {
    5777          10 :             memset(out_array, 0, sizeof(*out_array));
    5778         176 :             return 0;
    5779             :         }
    5780             : 
    5781         388 :         if (m_poBatch == nullptr || m_nIdxInBatch == m_poBatch->num_rows())
    5782             :         {
    5783         363 :             if (!ReadNextBatch())
    5784             :             {
    5785         166 :                 if (m_poAttrQuery || m_poFilterGeom)
    5786             :                 {
    5787          97 :                     InvalidateCachedBatches();
    5788             :                 }
    5789         166 :                 m_bEOF = true;
    5790         166 :                 memset(out_array, 0, sizeof(*out_array));
    5791         166 :                 return 0;
    5792             :             }
    5793             :         }
    5794             : 
    5795         222 :         const bool bNeedsPostFilter =
    5796         333 :             (m_poAttrQuery && !m_bBaseArrowIgnoreAttributeFilter) ||
    5797         111 :             (m_poFilterGeom && !m_bBaseArrowIgnoreSpatialFilter);
    5798             : 
    5799             :         struct ArrowSchema schema;
    5800         222 :         memset(&schema, 0, sizeof(schema));
    5801         222 :         auto status = arrow::ExportRecordBatch(*m_poBatch, out_array, &schema);
    5802         222 :         m_nIdxInBatch = m_poBatch->num_rows();
    5803         222 :         if (!status.ok())
    5804             :         {
    5805           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    5806             :                      "ExportRecordBatch() failed with %s",
    5807           0 :                      status.message().c_str());
    5808           0 :             return EIO;
    5809             :         }
    5810             : 
    5811             :         // Remove bounding box columns from exported array, or columns
    5812             :         // of unsupported data types that we voluntarily strip off.
    5813             :         const auto RemoveBBoxOrUnsupportedColumns =
    5814       32653 :             [out_array, &schema](const std::set<int> &oSetBBoxArrayIndex)
    5815             :         {
    5816         222 :             int j = 0;
    5817       10817 :             for (int i = 0; i < static_cast<int>(schema.n_children); ++i)
    5818             :             {
    5819       21140 :                 if (cpl::contains(oSetBBoxArrayIndex, i) ||
    5820       10545 :                     IsSilentlyIgnoredFormatForGetArrowSchemaArray(
    5821       10545 :                         schema.children[i]->format))
    5822             :                 {
    5823         126 :                     out_array->children[i]->release(out_array->children[i]);
    5824         126 :                     out_array->children[i] = nullptr;
    5825             : 
    5826         126 :                     schema.children[i]->release(schema.children[i]);
    5827         126 :                     schema.children[i] = nullptr;
    5828             :                 }
    5829             :                 else
    5830             :                 {
    5831       10469 :                     out_array->children[j] = out_array->children[i];
    5832       10469 :                     schema.children[j] = schema.children[i];
    5833       10469 :                     ++j;
    5834             :                 }
    5835             :             }
    5836         222 :             out_array->n_children = j;
    5837         222 :             schema.n_children = j;
    5838         222 :         };
    5839             : 
    5840         222 :         if (m_bIgnoredFields)
    5841             :         {
    5842         178 :             std::set<int> oSetBBoxArrayIndex;
    5843         126 :             for (const auto &iter : m_oMapGeomFieldIndexToGeomColBBOX)
    5844             :             {
    5845          37 :                 if (iter.second.iArrayIdx >= 0)
    5846          20 :                     oSetBBoxArrayIndex.insert(iter.second.iArrayIdx);
    5847             :             }
    5848          89 :             RemoveBBoxOrUnsupportedColumns(oSetBBoxArrayIndex);
    5849             :         }
    5850             :         else
    5851             :         {
    5852         133 :             RemoveBBoxOrUnsupportedColumns(m_oSetBBoxArrowColumns);
    5853             :         }
    5854             : 
    5855         222 :         if (EQUAL(m_aosArrowArrayStreamOptions.FetchNameValueDef(
    5856             :                       "GEOMETRY_ENCODING", ""),
    5857             :                   "WKB"))
    5858             :         {
    5859          40 :             const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
    5860          95 :             for (int i = 0; i < nGeomFieldCount; i++)
    5861             :             {
    5862             :                 const auto poGeomFieldDefn =
    5863          55 :                     m_poFeatureDefn->GetGeomFieldDefn(i);
    5864          55 :                 if (!poGeomFieldDefn->IsIgnored())
    5865             :                 {
    5866          47 :                     if (m_aeGeomEncoding[i] == OGRArrowGeomEncoding::WKT)
    5867             :                     {
    5868             :                         const int nArrayIdx =
    5869          18 :                             m_bIgnoredFields
    5870          26 :                                 ? m_anMapGeomFieldIndexToArrayIndex[i]
    5871           8 :                                 : m_anMapGeomFieldIndexToArrowColumn[i];
    5872          18 :                         auto sourceArray = out_array->children[nArrayIdx];
    5873             :                         auto targetArray =
    5874          18 :                             strcmp(schema.children[nArrayIdx]->format, "u") == 0
    5875          18 :                                 ? CreateWKBArrayFromWKTArray<uint32_t>(
    5876             :                                       sourceArray)
    5877           0 :                                 : CreateWKBArrayFromWKTArray<uint64_t>(
    5878          18 :                                       sourceArray);
    5879          18 :                         if (targetArray)
    5880             :                         {
    5881          18 :                             sourceArray->release(sourceArray);
    5882          18 :                             *(out_array->children[nArrayIdx]) = *targetArray;
    5883          18 :                             CPLFree(targetArray);
    5884             :                         }
    5885             :                         else
    5886             :                         {
    5887           0 :                             out_array->release(out_array);
    5888           0 :                             memset(out_array, 0, sizeof(*out_array));
    5889           0 :                             if (schema.release)
    5890           0 :                                 schema.release(&schema);
    5891           0 :                             return ENOMEM;
    5892             :                         }
    5893             :                     }
    5894          29 :                     else if (m_aeGeomEncoding[i] != OGRArrowGeomEncoding::WKB)
    5895             :                     {
    5896             :                         // Shouldn't happen if UseRecordBatchBaseImplementation()
    5897             :                         // is up to date
    5898           0 :                         CPLAssert(false);
    5899             :                     }
    5900             :                 }
    5901             :             }
    5902             :         }
    5903             : 
    5904         222 :         if (schema.release)
    5905         222 :             schema.release(&schema);
    5906             : 
    5907         222 :         OverrideArrowRelease(m_poArrowDS, out_array);
    5908             : 
    5909         222 :         const auto nFeatureIdxCur = m_nFeatureIdx;
    5910             :         // TODO: We likely have an issue regarding FIDs based on m_nFeatureIdx
    5911             :         // when m_iFIDArrowColumn < 0, only a subset of row groups is
    5912             :         // selected, and this batch goes across non consecutive row groups.
    5913        1119 :         for (int64_t i = 0; i < m_nIdxInBatch; ++i)
    5914         897 :             IncrFeatureIdx();
    5915             : 
    5916         222 :         if (bNeedsPostFilter)
    5917             :         {
    5918         130 :             CPLStringList aosOptions;
    5919         130 :             if (m_iFIDArrowColumn < 0)
    5920             :                 aosOptions.SetNameValue(
    5921             :                     "BASE_SEQUENTIAL_FID",
    5922             :                     CPLSPrintf(CPL_FRMT_GIB,
    5923         122 :                                static_cast<GIntBig>(nFeatureIdxCur)));
    5924             : 
    5925             :             // If there might be more than one record batch, it is more
    5926             :             // prudent to clone the array before modifying it.
    5927         130 :             if (nFeatureIdxCur > 0 || !TestCapability(OLCFastFeatureCount) ||
    5928           0 :                 out_array->length < GetFeatureCount(false))
    5929             :             {
    5930             :                 struct ArrowArray new_array;
    5931         130 :                 if (!OGRCloneArrowArray(&m_sCachedSchema, out_array,
    5932             :                                         &new_array))
    5933             :                 {
    5934           0 :                     if (out_array->release)
    5935           0 :                         out_array->release(out_array);
    5936           0 :                     memset(out_array, 0, sizeof(*out_array));
    5937           0 :                     return ENOMEM;
    5938             :                 }
    5939         130 :                 if (out_array->release)
    5940         130 :                     out_array->release(out_array);
    5941         130 :                 memcpy(out_array, &new_array, sizeof(new_array));
    5942             :             }
    5943             : 
    5944         130 :             PostFilterArrowArray(&m_sCachedSchema, out_array,
    5945         130 :                                  aosOptions.List());
    5946         130 :             if (out_array->length == 0)
    5947             :             {
    5948          18 :                 if (out_array->release)
    5949          18 :                     out_array->release(out_array);
    5950          18 :                 memset(out_array, 0, sizeof(*out_array));
    5951             :                 // If there are no records after filtering, start again
    5952             :                 // with a new batch
    5953          18 :                 continue;
    5954             :             }
    5955             :         }
    5956             : 
    5957         204 :         break;
    5958          18 :     }
    5959             : 
    5960         204 :     return 0;
    5961             : }
    5962             : 
    5963             : /************************************************************************/
    5964             : /*                    OGRArrowLayerAppendBuffer                         */
    5965             : /************************************************************************/
    5966             : 
    5967             : class OGRArrowLayerAppendBuffer : public OGRAppendBuffer
    5968             : {
    5969             :   public:
    5970          18 :     OGRArrowLayerAppendBuffer(struct ArrowArray *targetArrayIn,
    5971             :                               size_t nInitialCapacityIn)
    5972          18 :         : m_psTargetArray(targetArrayIn)
    5973             :     {
    5974          18 :         m_nCapacity = nInitialCapacityIn;
    5975          18 :         m_pRawBuffer = const_cast<void *>(m_psTargetArray->buffers[2]);
    5976          18 :     }
    5977             : 
    5978             :   protected:
    5979           4 :     bool Grow(size_t nItemSize) override
    5980             :     {
    5981           4 :         constexpr uint32_t MAX_SIZE_SINT32 =
    5982             :             static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
    5983           4 :         if (nItemSize > MAX_SIZE_SINT32 - m_nSize)
    5984             :         {
    5985           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Too large WKT content");
    5986           0 :             return false;
    5987             :         }
    5988           4 :         size_t nNewCapacity = m_nSize + nItemSize;
    5989           4 :         CPLAssert(m_nCapacity <= MAX_SIZE_SINT32);
    5990             :         const size_t nDoubleCapacity =
    5991           4 :             std::min<size_t>(MAX_SIZE_SINT32, 2 * m_nCapacity);
    5992           4 :         if (nNewCapacity < nDoubleCapacity)
    5993           4 :             nNewCapacity = nDoubleCapacity;
    5994           4 :         CPLAssert(nNewCapacity <= MAX_SIZE_SINT32);
    5995           4 :         void *newBuffer = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nNewCapacity);
    5996           4 :         if (newBuffer == nullptr)
    5997             :         {
    5998           0 :             return false;
    5999             :         }
    6000           4 :         m_nCapacity = nNewCapacity;
    6001           4 :         memcpy(newBuffer, m_pRawBuffer, m_nSize);
    6002           4 :         VSIFreeAligned(m_pRawBuffer);
    6003           4 :         m_pRawBuffer = newBuffer;
    6004           4 :         m_psTargetArray->buffers[2] = m_pRawBuffer;
    6005           4 :         return true;
    6006             :     }
    6007             : 
    6008             :   private:
    6009             :     struct ArrowArray *m_psTargetArray;
    6010             : 
    6011             :     OGRArrowLayerAppendBuffer(const OGRArrowLayerAppendBuffer &) = delete;
    6012             :     OGRArrowLayerAppendBuffer &
    6013             :     operator=(const OGRArrowLayerAppendBuffer &) = delete;
    6014             : };
    6015             : 
    6016             : /************************************************************************/
    6017             : /*                    CreateWKBArrayFromWKTArray()                      */
    6018             : /************************************************************************/
    6019             : 
    6020             : template <typename SourceOffset>
    6021             : inline struct ArrowArray *
    6022          18 : OGRArrowLayer::CreateWKBArrayFromWKTArray(const struct ArrowArray *sourceArray)
    6023             : {
    6024          18 :     CPLAssert(sourceArray->n_buffers == 3);
    6025          18 :     CPLAssert(sourceArray->buffers[1] != nullptr);
    6026          18 :     CPLAssert(sourceArray->buffers[2] != nullptr);
    6027             : 
    6028          18 :     const size_t nLength = static_cast<size_t>(sourceArray->length);
    6029             :     auto targetArray = static_cast<struct ArrowArray *>(
    6030          18 :         CPLCalloc(1, sizeof(struct ArrowArray)));
    6031          18 :     targetArray->release = OGRLayer::ReleaseArray;
    6032          18 :     targetArray->length = nLength;
    6033             : 
    6034          18 :     targetArray->n_buffers = 3;
    6035          18 :     targetArray->buffers =
    6036          18 :         static_cast<const void **>(CPLCalloc(3, sizeof(void *)));
    6037             : 
    6038             :     // Allocate validity map buffer if needed
    6039          18 :     const auto sourceNull =
    6040          18 :         static_cast<const uint8_t *>(sourceArray->buffers[0]);
    6041          18 :     const size_t nOffset = static_cast<size_t>(sourceArray->offset);
    6042          18 :     uint8_t *targetNull = nullptr;
    6043          18 :     if (sourceArray->null_count && sourceNull)
    6044             :     {
    6045           6 :         targetArray->buffers[0] =
    6046           3 :             VSI_MALLOC_ALIGNED_AUTO_VERBOSE((nLength + 7) / 8);
    6047           3 :         if (targetArray->buffers[0])
    6048             :         {
    6049           3 :             targetArray->null_count = sourceArray->null_count;
    6050           3 :             targetNull = static_cast<uint8_t *>(
    6051           3 :                 const_cast<void *>(targetArray->buffers[0]));
    6052           3 :             if (nOffset == 0)
    6053             :             {
    6054           3 :                 memcpy(targetNull, sourceNull, (nLength + 7) / 8);
    6055             :             }
    6056             :             else
    6057             :             {
    6058           0 :                 memset(targetNull, 0, (nLength + 7) / 8);
    6059           0 :                 for (size_t i = 0; i < nLength; ++i)
    6060             :                 {
    6061           0 :                     if ((sourceNull[(i + nOffset) / 8] >> ((i + nOffset) % 8)) &
    6062             :                         1)
    6063           0 :                         targetNull[i / 8] |= (1 << (i % 8));
    6064             :                 }
    6065             :             }
    6066             :         }
    6067             :     }
    6068             : 
    6069             :     // Allocate offset buffer
    6070          36 :     targetArray->buffers[1] =
    6071          18 :         VSI_MALLOC_ALIGNED_AUTO_VERBOSE(sizeof(uint32_t) * (1 + nLength));
    6072             : 
    6073             :     // Allocate data (WKB) buffer
    6074          18 :     constexpr size_t DEFAULT_WKB_SIZE = 100;
    6075          18 :     uint32_t nInitialCapacity = static_cast<uint32_t>(std::min<size_t>(
    6076          18 :         std::numeric_limits<int32_t>::max(), DEFAULT_WKB_SIZE * nLength));
    6077          18 :     targetArray->buffers[2] = VSI_MALLOC_ALIGNED_AUTO_VERBOSE(nInitialCapacity);
    6078             : 
    6079             :     // Check buffers have been allocated
    6080          18 :     if ((sourceArray->null_count && sourceNull && !targetNull) ||
    6081          18 :         targetArray->buffers[1] == nullptr ||
    6082          18 :         targetArray->buffers[2] == nullptr)
    6083             :     {
    6084           0 :         targetArray->release(targetArray);
    6085           0 :         return nullptr;
    6086             :     }
    6087             : 
    6088          36 :     OGRArrowLayerAppendBuffer oOGRAppendBuffer(targetArray, nInitialCapacity);
    6089          18 :     OGRWKTToWKBTranslator oTranslator(oOGRAppendBuffer);
    6090             : 
    6091          18 :     const auto sourceOffsets =
    6092          18 :         static_cast<const SourceOffset *>(sourceArray->buffers[1]) + nOffset;
    6093          18 :     auto sourceBytes =
    6094          18 :         static_cast<char *>(const_cast<void *>(sourceArray->buffers[2]));
    6095          18 :     auto targetOffsets =
    6096          18 :         static_cast<uint32_t *>(const_cast<void *>(targetArray->buffers[1]));
    6097         159 :     for (size_t i = 0; i < nLength; ++i)
    6098             :     {
    6099         141 :         targetOffsets[i] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
    6100             : 
    6101         141 :         if (targetNull && ((targetNull[i / 8] >> (i % 8)) & 1) == 0)
    6102             :         {
    6103           3 :             continue;
    6104             :         }
    6105             : 
    6106         276 :         const size_t nWKBSize = oTranslator.TranslateWKT(
    6107         138 :             sourceBytes + sourceOffsets[i],
    6108         138 :             static_cast<size_t>(sourceOffsets[i + 1] - sourceOffsets[i]),
    6109         138 :             sourceOffsets[i + 1] < sourceOffsets[nLength]);
    6110         138 :         if (nWKBSize == static_cast<size_t>(-1))
    6111             :         {
    6112           0 :             targetArray->release(targetArray);
    6113           0 :             return nullptr;
    6114             :         }
    6115             :     }
    6116          18 :     targetOffsets[nLength] = static_cast<uint32_t>(oOGRAppendBuffer.GetSize());
    6117             : 
    6118          18 :     return targetArray;
    6119             : }
    6120             : 
    6121             : /************************************************************************/
    6122             : /*                         TestCapability()                             */
    6123             : /************************************************************************/
    6124             : 
    6125         873 : inline int OGRArrowLayer::TestCapability(const char *pszCap)
    6126             : {
    6127             : 
    6128         873 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    6129         488 :         return true;
    6130             : 
    6131         566 :     else if (EQUAL(pszCap, OLCFastGetArrowStream) &&
    6132         181 :              !UseRecordBatchBaseImplementation())
    6133             :     {
    6134         181 :         return true;
    6135             :     }
    6136             : 
    6137         204 :     if (EQUAL(pszCap, OLCFastGetExtent))
    6138             :     {
    6139          29 :         OGREnvelope sEnvelope;
    6140          44 :         for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    6141             :         {
    6142          29 :             if (!FastGetExtent(i, &sEnvelope))
    6143          14 :                 return false;
    6144             :         }
    6145          15 :         return true;
    6146             :     }
    6147             : 
    6148         175 :     if (EQUAL(pszCap, OLCFastGetExtent3D))
    6149             :     {
    6150          19 :         OGREnvelope3D sEnvelope;
    6151          20 :         for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
    6152             :         {
    6153          19 :             if (!FastGetExtent3D(i, &sEnvelope))
    6154          18 :                 return false;
    6155             :         }
    6156           1 :         return true;
    6157             :     }
    6158             : 
    6159         156 :     return false;
    6160             : }
    6161             : 
    6162             : #if defined(__clang__)
    6163             : #pragma clang diagnostic pop
    6164             : #endif
    6165             : 
    6166             : #endif /* OGARROWLAYER_HPP_INCLUDED */

Generated by: LCOV version 1.14