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: 2755 3130 88.0 %
Date: 2025-12-08 00:14:33 Functions: 128 135 94.8 %

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

Generated by: LCOV version 1.14