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: 2757 3137 87.9 %
Date: 2024-11-21 22:18:42 Functions: 114 121 94.2 %

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

Generated by: LCOV version 1.14