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

Generated by: LCOV version 1.14