LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/arrow - ogrfeatherlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 300 349 86.0 %
Date: 2024-11-21 22:18:42 Functions: 19 20 95.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Feather Translator
       4             :  * Purpose:  Implements OGRFeatherDriver.
       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             : #include "cpl_json.h"
      14             : #include "cpl_time.h"
      15             : #include "gdal_pam.h"
      16             : #include "ogrsf_frmts.h"
      17             : #include "ogr_p.h"
      18             : 
      19             : #include <cinttypes>
      20             : #include <limits>
      21             : #include <map>
      22             : #include <set>
      23             : #include <utility>
      24             : 
      25             : #include "ogr_feather.h"
      26             : 
      27             : #include "../arrow_common/ograrrowlayer.hpp"
      28             : #include "../arrow_common/ograrrowdataset.hpp"
      29             : 
      30             : /************************************************************************/
      31             : /*                        OGRFeatherLayer()                             */
      32             : /************************************************************************/
      33             : 
      34         535 : OGRFeatherLayer::OGRFeatherLayer(
      35             :     OGRFeatherDataset *poDS, const char *pszLayerName,
      36         535 :     std::shared_ptr<arrow::ipc::RecordBatchFileReader> &poRecordBatchFileReader)
      37             :     : OGRArrowLayer(poDS, pszLayerName), m_poDS(poDS),
      38         535 :       m_poRecordBatchFileReader(poRecordBatchFileReader)
      39             : {
      40         535 :     EstablishFeatureDefn();
      41         535 :     CPLAssert(static_cast<int>(m_aeGeomEncoding.size()) ==
      42             :               m_poFeatureDefn->GetGeomFieldCount());
      43         535 : }
      44             : 
      45             : /************************************************************************/
      46             : /*                        OGRFeatherLayer()                             */
      47             : /************************************************************************/
      48             : 
      49          10 : OGRFeatherLayer::OGRFeatherLayer(
      50             :     OGRFeatherDataset *poDS, const char *pszLayerName,
      51             :     std::shared_ptr<arrow::io::RandomAccessFile> poFile, bool bSeekable,
      52             :     const arrow::ipc::IpcReadOptions &oOptions,
      53             :     std::shared_ptr<arrow::ipc::RecordBatchStreamReader>
      54          10 :         &poRecordBatchStreamReader)
      55             :     : OGRArrowLayer(poDS, pszLayerName), m_poDS(poDS),
      56          10 :       m_poFile(std::move(poFile)), m_bSeekable(bSeekable), m_oOptions(oOptions),
      57          10 :       m_poRecordBatchReader(poRecordBatchStreamReader)
      58             : {
      59          10 :     EstablishFeatureDefn();
      60          10 :     CPLAssert(static_cast<int>(m_aeGeomEncoding.size()) ==
      61             :               m_poFeatureDefn->GetGeomFieldCount());
      62          10 : }
      63             : 
      64             : /************************************************************************/
      65             : /*                           GetDataset()                               */
      66             : /************************************************************************/
      67             : 
      68           5 : GDALDataset *OGRFeatherLayer::GetDataset()
      69             : {
      70           5 :     return m_poDS;
      71             : }
      72             : 
      73             : /************************************************************************/
      74             : /*                          LoadGeoMetadata()                           */
      75             : /************************************************************************/
      76             : 
      77         545 : void OGRFeatherLayer::LoadGeoMetadata(
      78             :     const arrow::KeyValueMetadata *kv_metadata, const std::string &key)
      79             : {
      80         545 :     if (kv_metadata && kv_metadata->Contains(key))
      81             :     {
      82        1060 :         auto geo = kv_metadata->Get(key);
      83         530 :         if (geo.ok())
      84             :         {
      85        1060 :             CPLJSONDocument oDoc;
      86         530 :             if (oDoc.LoadMemory(*geo))
      87             :             {
      88        1060 :                 auto oRoot = oDoc.GetRoot();
      89        1590 :                 const auto osVersion = oRoot.GetString("schema_version");
      90         530 :                 if (key != GDAL_GEO_FOOTER_KEY && osVersion != "0.1.0")
      91             :                 {
      92         397 :                     CPLDebug("FEATHER",
      93             :                              "schema_version = %s not explicitly handled by "
      94             :                              "the driver",
      95             :                              osVersion.c_str());
      96             :                 }
      97        1590 :                 auto oColumns = oRoot.GetObj("columns");
      98         530 :                 if (oColumns.IsValid())
      99             :                 {
     100        1060 :                     for (const auto &oColumn : oColumns.GetChildren())
     101             :                     {
     102         530 :                         m_oMapGeometryColumns[oColumn.GetName()] = oColumn;
     103             :                     }
     104             :                 }
     105             :             }
     106             :             else
     107             :             {
     108           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     109             :                          "Cannot parse 'geo' metadata");
     110             :             }
     111             :         }
     112             :     }
     113         545 : }
     114             : 
     115             : /************************************************************************/
     116             : /*                        EstablishFeatureDefn()                        */
     117             : /************************************************************************/
     118             : 
     119         545 : void OGRFeatherLayer::EstablishFeatureDefn()
     120             : {
     121        1090 :     m_poSchema = m_poRecordBatchFileReader ? m_poRecordBatchFileReader->schema()
     122        1090 :                                            : m_poRecordBatchReader->schema();
     123         545 :     const auto &kv_metadata = m_poSchema->metadata();
     124             : 
     125             : #ifdef DEBUG
     126         545 :     if (kv_metadata)
     127             :     {
     128        1073 :         for (const auto &keyValue : kv_metadata->sorted_pairs())
     129             :         {
     130         538 :             CPLDebug("FEATHER", "%s = %s", keyValue.first.c_str(),
     131             :                      keyValue.second.c_str());
     132             :         }
     133             :     }
     134             : #endif
     135             : 
     136             :     auto poFooterMetadata = m_poRecordBatchFileReader
     137         535 :                                 ? m_poRecordBatchFileReader->metadata()
     138        1625 :                                 : nullptr;
     139         669 :     if (poFooterMetadata && poFooterMetadata->Contains(GDAL_GEO_FOOTER_KEY) &&
     140         124 :         CPLTestBool(CPLGetConfigOption("OGR_ARROW_READ_GDAL_FOOTER", "YES")))
     141             :     {
     142         124 :         LoadGeoMetadata(poFooterMetadata.get(), GDAL_GEO_FOOTER_KEY);
     143             :     }
     144             :     else
     145             :     {
     146         421 :         LoadGeoMetadata(kv_metadata.get(), "geo");
     147             :     }
     148             :     const auto oMapFieldNameToGDALSchemaFieldDefn =
     149        1090 :         LoadGDALSchema(kv_metadata.get());
     150             : 
     151         545 :     const auto &fields = m_poSchema->fields();
     152        4082 :     for (int i = 0; i < m_poSchema->num_fields(); ++i)
     153             :     {
     154        3537 :         const auto &field = fields[i];
     155        3537 :         const auto &fieldName = field->name();
     156             : 
     157        3537 :         const auto &field_kv_metadata = field->metadata();
     158        3537 :         std::string osExtensionName;
     159        3537 :         if (field_kv_metadata)
     160             :         {
     161             :             auto extension_name =
     162        1294 :                 field_kv_metadata->Get(ARROW_EXTENSION_NAME_KEY);
     163         647 :             if (extension_name.ok())
     164             :             {
     165         518 :                 osExtensionName = *extension_name;
     166             :             }
     167             : #ifdef DEBUG
     168         647 :             CPLDebug("FEATHER", "Metadata field %s:", fieldName.c_str());
     169        1551 :             for (const auto &keyValue : field_kv_metadata->sorted_pairs())
     170             :             {
     171         904 :                 CPLDebug("FEATHER", "  %s = %s", keyValue.first.c_str(),
     172             :                          keyValue.second.c_str());
     173             :             }
     174             : #endif
     175             :         }
     176             : 
     177        3537 :         if (!m_osFIDColumn.empty() && fieldName == m_osFIDColumn)
     178             :         {
     179           6 :             m_iFIDArrowColumn = i;
     180           6 :             continue;
     181             :         }
     182             : 
     183        3531 :         bool bRegularField = true;
     184        3531 :         auto oIter = m_oMapGeometryColumns.find(fieldName);
     185        3531 :         if (oIter != m_oMapGeometryColumns.end() || !osExtensionName.empty())
     186             :         {
     187        1066 :             CPLJSONObject oJSONDef;
     188         533 :             if (oIter != m_oMapGeometryColumns.end())
     189         530 :                 oJSONDef = oIter->second;
     190        1599 :             auto osEncoding = oJSONDef.GetString("encoding");
     191         533 :             if (osEncoding.empty() && !osExtensionName.empty())
     192           3 :                 osEncoding = std::move(osExtensionName);
     193             : 
     194         533 :             OGRwkbGeometryType eGeomType = wkbUnknown;
     195         533 :             auto eGeomEncoding = OGRArrowGeomEncoding::WKB;
     196         533 :             if (IsValidGeometryEncoding(field, osEncoding,
     197        1066 :                                         oIter != m_oMapGeometryColumns.end(),
     198             :                                         eGeomType, eGeomEncoding))
     199             :             {
     200         531 :                 bRegularField = false;
     201        1062 :                 OGRGeomFieldDefn oField(fieldName.c_str(), wkbUnknown);
     202             : 
     203        1593 :                 const auto osWKT = oJSONDef.GetString("crs");
     204         531 :                 if (osWKT.empty())
     205             :                 {
     206             : #if 0
     207             :                     CPLError(CE_Warning, CPLE_AppDefined,
     208             :                              "Missing required 'crs' field for geometry column %s",
     209             :                              fieldName.c_str());
     210             : #endif
     211             :                 }
     212             :                 else
     213             :                 {
     214         136 :                     OGRSpatialReference *poSRS = new OGRSpatialReference();
     215         136 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     216             : 
     217         136 :                     if (poSRS->importFromWkt(osWKT.c_str()) == OGRERR_NONE)
     218             :                     {
     219         136 :                         const double dfCoordEpoch = oJSONDef.GetDouble("epoch");
     220         136 :                         if (dfCoordEpoch > 0)
     221           2 :                             poSRS->SetCoordinateEpoch(dfCoordEpoch);
     222             : 
     223         136 :                         oField.SetSpatialRef(poSRS);
     224             :                     }
     225         136 :                     poSRS->Release();
     226             :                 }
     227             : 
     228             :                 // m_aeGeomEncoding be filled before calling
     229             :                 // ComputeGeometryColumnType()
     230         531 :                 m_aeGeomEncoding.push_back(eGeomEncoding);
     231         531 :                 if (eGeomType == wkbUnknown)
     232             :                 {
     233         705 :                     auto osType = oJSONDef.GetString("geometry_type");
     234         235 :                     if (osType.empty())
     235         235 :                         osType = oJSONDef.GetString("gdal:geometry_type");
     236         470 :                     if (m_bSeekable && osType.empty() &&
     237         235 :                         CPLTestBool(CPLGetConfigOption(
     238             :                             "OGR_ARROW_COMPUTE_GEOMETRY_TYPE", "YES")))
     239             :                     {
     240         235 :                         eGeomType = ComputeGeometryColumnType(
     241         235 :                             m_poFeatureDefn->GetGeomFieldCount(), i);
     242         235 :                         if (m_poRecordBatchReader)
     243           0 :                             ResetRecordBatchReader();
     244             :                     }
     245             :                     else
     246           0 :                         eGeomType = GetGeometryTypeFromString(osType);
     247             :                 }
     248             : 
     249         531 :                 oField.SetType(eGeomType);
     250         531 :                 oField.SetNullable(field->nullable());
     251         531 :                 m_poFeatureDefn->AddGeomFieldDefn(&oField);
     252         531 :                 m_anMapGeomFieldIndexToArrowColumn.push_back(i);
     253             :             }
     254             :         }
     255             : 
     256        3531 :         if (bRegularField)
     257             :         {
     258        3000 :             CreateFieldFromSchema(field, {i},
     259             :                                   oMapFieldNameToGDALSchemaFieldDefn);
     260             :         }
     261             :     }
     262             : 
     263         545 :     CPLAssert(static_cast<int>(m_anMapFieldIndexToArrowColumn.size()) ==
     264             :               m_poFeatureDefn->GetFieldCount());
     265         545 :     CPLAssert(static_cast<int>(m_anMapGeomFieldIndexToArrowColumn.size()) ==
     266             :               m_poFeatureDefn->GetGeomFieldCount());
     267         545 : }
     268             : 
     269             : /************************************************************************/
     270             : /*                       ResetRecordBatchReader()                       */
     271             : /************************************************************************/
     272             : 
     273          12 : bool OGRFeatherLayer::ResetRecordBatchReader()
     274             : {
     275          12 :     const auto nPos = *(m_poFile->Tell());
     276          12 :     CPL_IGNORE_RET_VAL(m_poFile->Seek(0));
     277             :     auto result =
     278          24 :         arrow::ipc::RecordBatchStreamReader::Open(m_poFile, m_oOptions);
     279          12 :     if (!result.ok())
     280             :     {
     281           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     282             :                  "RecordBatchStreamReader::Open() failed with %s",
     283           0 :                  result.status().message().c_str());
     284           0 :         CPL_IGNORE_RET_VAL(m_poFile->Seek(nPos));
     285           0 :         return false;
     286             :     }
     287             :     else
     288             :     {
     289          12 :         m_poRecordBatchReader = *result;
     290          12 :         return true;
     291             :     }
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                     ComputeGeometryColumnType()                      */
     296             : /************************************************************************/
     297             : 
     298         235 : OGRwkbGeometryType OGRFeatherLayer::ComputeGeometryColumnType(int iGeomCol,
     299             :                                                               int iCol) const
     300             : {
     301             :     // Compute type of geometry column by iterating over each geometry, and
     302             :     // looking at the WKB geometry type in the first 5 bytes of each geometry.
     303             : 
     304         235 :     OGRwkbGeometryType eGeomType = wkbNone;
     305             : 
     306         235 :     if (m_poRecordBatchReader != nullptr)
     307             :     {
     308           0 :         std::shared_ptr<arrow::RecordBatch> poBatch;
     309             :         while (true)
     310             :         {
     311           0 :             auto status = m_poRecordBatchReader->ReadNext(&poBatch);
     312           0 :             if (!status.ok())
     313             :             {
     314           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "ReadNext() failed: %s",
     315           0 :                          status.message().c_str());
     316           0 :                 break;
     317             :             }
     318           0 :             else if (!poBatch)
     319           0 :                 break;
     320           0 :             eGeomType = ComputeGeometryColumnTypeProcessBatch(poBatch, iGeomCol,
     321             :                                                               iCol, eGeomType);
     322           0 :             if (eGeomType == wkbUnknown)
     323           0 :                 break;
     324           0 :         }
     325             :     }
     326             :     else
     327             :     {
     328         470 :         for (int iBatch = 0;
     329         470 :              iBatch < m_poRecordBatchFileReader->num_record_batches(); ++iBatch)
     330             :         {
     331         235 :             auto result = m_poRecordBatchFileReader->ReadRecordBatch(iBatch);
     332         235 :             if (!result.ok())
     333             :             {
     334           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     335             :                          "ReadRecordBatch() failed: %s",
     336           0 :                          result.status().message().c_str());
     337           0 :                 break;
     338             :             }
     339         235 :             eGeomType = ComputeGeometryColumnTypeProcessBatch(*result, iGeomCol,
     340             :                                                               iCol, eGeomType);
     341         235 :             if (eGeomType == wkbUnknown)
     342           0 :                 break;
     343             :         }
     344             :     }
     345             : 
     346         235 :     return eGeomType == wkbNone ? wkbUnknown : eGeomType;
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                          BuildDomain()                               */
     351             : /************************************************************************/
     352             : 
     353             : std::unique_ptr<OGRFieldDomain>
     354          19 : OGRFeatherLayer::BuildDomain(const std::string &osDomainName,
     355             :                              int iFieldIndex) const
     356             : {
     357          19 :     const int iArrowCol = m_anMapFieldIndexToArrowColumn[iFieldIndex][0];
     358          19 :     CPLAssert(m_poSchema->fields()[iArrowCol]->type()->id() ==
     359             :               arrow::Type::DICTIONARY);
     360             : 
     361          19 :     if (m_poRecordBatchReader)
     362             :     {
     363           6 :         if (m_poBatch)
     364             :         {
     365           6 :             return BuildDomainFromBatch(osDomainName, m_poBatch, iArrowCol);
     366             :         }
     367             :     }
     368          13 :     else if (m_poRecordBatchFileReader)
     369             :     {
     370          13 :         auto result = m_poRecordBatchFileReader->ReadRecordBatch(0);
     371          13 :         if (!result.ok())
     372             :         {
     373           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     374             :                      "ReadRecordBatch() failed: %s",
     375           0 :                      result.status().message().c_str());
     376             :         }
     377          13 :         auto poBatch = *result;
     378          13 :         if (poBatch)
     379             :         {
     380          13 :             return BuildDomainFromBatch(osDomainName, poBatch, iArrowCol);
     381             :         }
     382             :     }
     383             : 
     384           0 :     return nullptr;
     385             : }
     386             : 
     387             : /************************************************************************/
     388             : /*                           ResetReading()                             */
     389             : /************************************************************************/
     390             : 
     391         820 : void OGRFeatherLayer::ResetReading()
     392             : {
     393         820 :     if (m_poRecordBatchReader != nullptr && m_iRecordBatch > 0)
     394             :     {
     395          17 :         if (m_iRecordBatch == 1 && m_poBatchIdx1)
     396             :         {
     397             :             // do nothing
     398             :         }
     399             :         else
     400             :         {
     401          16 :             m_bResetRecordBatchReaderAsked = true;
     402             :         }
     403             :     }
     404         820 :     OGRArrowLayer::ResetReading();
     405         820 : }
     406             : 
     407             : /************************************************************************/
     408             : /*                           ReadNextBatch()                            */
     409             : /************************************************************************/
     410             : 
     411        1006 : bool OGRFeatherLayer::ReadNextBatch()
     412             : {
     413        1006 :     if (m_poRecordBatchFileReader == nullptr)
     414             :     {
     415         119 :         return ReadNextBatchStream();
     416             :     }
     417             :     else
     418             :     {
     419         887 :         return ReadNextBatchFile();
     420             :     }
     421             : }
     422             : 
     423             : /************************************************************************/
     424             : /*                         ReadNextBatchFile()                          */
     425             : /************************************************************************/
     426             : 
     427         887 : bool OGRFeatherLayer::ReadNextBatchFile()
     428             : {
     429             :     while (true)
     430             :     {
     431         887 :         ++m_iRecordBatch;
     432         887 :         if (m_iRecordBatch == m_poRecordBatchFileReader->num_record_batches())
     433             :         {
     434         449 :             if (m_iRecordBatch == 1)
     435         446 :                 m_iRecordBatch = 0;
     436             :             else
     437           3 :                 m_poBatch.reset();
     438         449 :             return false;
     439             :         }
     440             : 
     441         438 :         m_nIdxInBatch = 0;
     442             : 
     443             :         auto result =
     444         438 :             m_poRecordBatchFileReader->ReadRecordBatch(m_iRecordBatch);
     445         438 :         if (!result.ok())
     446             :         {
     447           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     448             :                      "ReadRecordBatch() failed: %s",
     449           0 :                      result.status().message().c_str());
     450           0 :             m_poBatch.reset();
     451           0 :             return false;
     452             :         }
     453         438 :         if ((*result)->num_rows() != 0)
     454             :         {
     455         438 :             SetBatch(*result);
     456         438 :             break;
     457             :         }
     458           0 :     }
     459             : 
     460         438 :     return true;
     461             : }
     462             : 
     463             : /************************************************************************/
     464             : /*                         ReadNextBatchStream()                        */
     465             : /************************************************************************/
     466             : 
     467         154 : bool OGRFeatherLayer::ReadNextBatchStream()
     468             : {
     469         154 :     m_nIdxInBatch = 0;
     470             : 
     471         308 :     std::shared_ptr<arrow::RecordBatch> poNextBatch;
     472           0 :     do
     473             :     {
     474         154 :         if (m_iRecordBatch == 0 && m_poBatchIdx0)
     475             :         {
     476           1 :             SetBatch(m_poBatchIdx0);
     477           1 :             m_iRecordBatch = 1;
     478         101 :             return true;
     479             :         }
     480             : 
     481         153 :         else if (m_iRecordBatch == 1 && m_poBatchIdx1)
     482             :         {
     483           1 :             SetBatch(m_poBatchIdx1);
     484           1 :             m_iRecordBatch = 2;
     485           1 :             return true;
     486             :         }
     487             : 
     488         152 :         else if (m_bSingleBatch)
     489             :         {
     490          81 :             CPLAssert(m_iRecordBatch == 0);
     491          81 :             CPLAssert(m_poBatch != nullptr);
     492          81 :             return false;
     493             :         }
     494             : 
     495          71 :         if (m_bResetRecordBatchReaderAsked)
     496             :         {
     497          13 :             if (!m_bSeekable)
     498             :             {
     499           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
     500             :                          "Attempting to rewind non-seekable stream");
     501           1 :                 return false;
     502             :             }
     503          12 :             if (!ResetRecordBatchReader())
     504           0 :                 return false;
     505          12 :             m_bResetRecordBatchReaderAsked = false;
     506             :         }
     507             : 
     508          70 :         CPLAssert(m_poRecordBatchReader);
     509             : 
     510          70 :         ++m_iRecordBatch;
     511             : 
     512          70 :         poNextBatch.reset();
     513          70 :         auto status = m_poRecordBatchReader->ReadNext(&poNextBatch);
     514          70 :         if (!status.ok())
     515             :         {
     516           0 :             CPLError(CE_Failure, CPLE_AppDefined, "ReadNext() failed: %s",
     517           0 :                      status.message().c_str());
     518           0 :             poNextBatch.reset();
     519             :         }
     520          70 :         if (poNextBatch == nullptr)
     521             :         {
     522          17 :             if (m_iRecordBatch == 1)
     523             :             {
     524           3 :                 m_iRecordBatch = 0;
     525           3 :                 m_bSingleBatch = true;
     526             :             }
     527             :             else
     528             :             {
     529          14 :                 m_poBatch.reset();
     530          14 :                 m_poBatchColumns.clear();
     531             :             }
     532          17 :             return false;
     533             :         }
     534          53 :     } while (poNextBatch->num_rows() == 0);
     535             : 
     536          53 :     SetBatch(poNextBatch);
     537             : 
     538          53 :     return true;
     539             : }
     540             : 
     541             : /************************************************************************/
     542             : /*                     TryToCacheFirstTwoBatches()                      */
     543             : /************************************************************************/
     544             : 
     545           1 : void OGRFeatherLayer::TryToCacheFirstTwoBatches()
     546             : {
     547           2 :     if (m_poRecordBatchReader != nullptr && m_iRecordBatch <= 0 &&
     548           2 :         !m_bSingleBatch && m_poBatchIdx0 == nullptr)
     549             :     {
     550           1 :         ResetReading();
     551           1 :         if (!m_poBatch)
     552             :         {
     553           0 :             CPL_IGNORE_RET_VAL(ReadNextBatchStream());
     554             :         }
     555           1 :         if (m_poBatch)
     556             :         {
     557           2 :             auto poBatchIdx0 = m_poBatch;
     558           1 :             if (ReadNextBatchStream())
     559             :             {
     560           1 :                 CPLAssert(m_iRecordBatch == 1);
     561           1 :                 m_poBatchIdx0 = poBatchIdx0;
     562           1 :                 m_poBatchIdx1 = m_poBatch;
     563           1 :                 SetBatch(poBatchIdx0);
     564           1 :                 ResetReading();
     565             :             }
     566           1 :             ResetReading();
     567             :         }
     568             :     }
     569           1 : }
     570             : 
     571             : /************************************************************************/
     572             : /*                          CanPostFilterArrowArray()                   */
     573             : /************************************************************************/
     574             : 
     575          20 : bool OGRFeatherLayer::CanPostFilterArrowArray(
     576             :     const struct ArrowSchema *schema) const
     577             : {
     578          20 :     if (m_poRecordBatchReader)
     579          10 :         return false;
     580          10 :     return OGRArrowLayer::CanPostFilterArrowArray(schema);
     581             : }
     582             : 
     583             : /************************************************************************/
     584             : /*                     InvalidateCachedBatches()                        */
     585             : /************************************************************************/
     586             : 
     587         109 : void OGRFeatherLayer::InvalidateCachedBatches()
     588             : {
     589         109 :     if (m_poRecordBatchFileReader)
     590             :     {
     591          63 :         m_iRecordBatch = -1;
     592          63 :         ResetReading();
     593             :     }
     594         109 : }
     595             : 
     596             : /************************************************************************/
     597             : /*                        GetFeatureCount()                             */
     598             : /************************************************************************/
     599             : 
     600         336 : GIntBig OGRFeatherLayer::GetFeatureCount(int bForce)
     601             : {
     602         628 :     if (m_poRecordBatchFileReader != nullptr && m_poAttrQuery == nullptr &&
     603         292 :         m_poFilterGeom == nullptr)
     604             :     {
     605         288 :         auto result = m_poRecordBatchFileReader->CountRows();
     606         288 :         if (result.ok())
     607         288 :             return *result;
     608             :     }
     609          48 :     else if (m_poRecordBatchReader != nullptr)
     610             :     {
     611          36 :         if (!m_bSeekable && !bForce)
     612             :         {
     613           1 :             if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
     614             :             {
     615           1 :                 TryToCacheFirstTwoBatches();
     616             :             }
     617             : 
     618           1 :             if (!m_bSingleBatch)
     619             :             {
     620           1 :                 CPLError(
     621             :                     CE_Failure, CPLE_AppDefined,
     622             :                     "GetFeatureCount() cannot be run in non-forced mode on "
     623             :                     "a non-seekable file made of several batches");
     624           1 :                 return -1;
     625             :             }
     626             :         }
     627             : 
     628          35 :         if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
     629             :         {
     630          23 :             GIntBig nFeatures = 0;
     631          23 :             ResetReading();
     632          23 :             if (!m_poBatch)
     633           3 :                 ReadNextBatchStream();
     634          31 :             while (m_poBatch)
     635             :             {
     636          31 :                 nFeatures += m_poBatch->num_rows();
     637          31 :                 if (!ReadNextBatchStream())
     638          23 :                     break;
     639             :             }
     640          23 :             ResetReading();
     641          23 :             return nFeatures;
     642             :         }
     643             :     }
     644          24 :     return OGRLayer::GetFeatureCount(bForce);
     645             : }
     646             : 
     647             : /************************************************************************/
     648             : /*                       CanRunNonForcedGetExtent()                     */
     649             : /************************************************************************/
     650             : 
     651           0 : bool OGRFeatherLayer::CanRunNonForcedGetExtent()
     652             : {
     653           0 :     if (m_bSeekable)
     654           0 :         return true;
     655           0 :     TryToCacheFirstTwoBatches();
     656           0 :     if (!m_bSingleBatch)
     657             :     {
     658           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     659             :                  "GetExtent() cannot be run in non-forced mode on "
     660             :                  "a non-seekable file made of several batches");
     661           0 :         return false;
     662             :     }
     663           0 :     return true;
     664             : }
     665             : 
     666             : /************************************************************************/
     667             : /*                         TestCapability()                             */
     668             : /************************************************************************/
     669             : 
     670         292 : int OGRFeatherLayer::TestCapability(const char *pszCap)
     671             : {
     672         292 :     if (EQUAL(pszCap, OLCFastFeatureCount))
     673             :     {
     674          28 :         return m_bSeekable && m_poAttrQuery == nullptr &&
     675          28 :                m_poFilterGeom == nullptr;
     676             :     }
     677             : 
     678         274 :     if (EQUAL(pszCap, OLCMeasuredGeometries))
     679          16 :         return true;
     680         258 :     if (EQUAL(pszCap, OLCZGeometries))
     681          12 :         return true;
     682             : 
     683         246 :     return OGRArrowLayer::TestCapability(pszCap);
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*                         GetMetadataItem()                            */
     688             : /************************************************************************/
     689             : 
     690         259 : const char *OGRFeatherLayer::GetMetadataItem(const char *pszName,
     691             :                                              const char *pszDomain)
     692             : {
     693             :     // Mostly for unit test purposes
     694         259 :     if (pszDomain != nullptr && EQUAL(pszDomain, "_ARROW_"))
     695             :     {
     696           9 :         if (EQUAL(pszName, "FORMAT"))
     697             :         {
     698           5 :             return m_poRecordBatchFileReader ? "FILE" : "STREAM";
     699             :         }
     700           4 :         if (m_poRecordBatchFileReader != nullptr)
     701             :         {
     702           4 :             int iBatch = -1;
     703           4 :             if (EQUAL(pszName, "NUM_RECORD_BATCHES"))
     704             :             {
     705           1 :                 return CPLSPrintf(
     706           5 :                     "%d", m_poRecordBatchFileReader->num_record_batches());
     707             :             }
     708           6 :             else if (sscanf(pszName, "RECORD_BATCHES[%d]", &iBatch) == 1 &&
     709           3 :                      strstr(pszName, ".NUM_ROWS"))
     710             :             {
     711             :                 auto result =
     712           6 :                     m_poRecordBatchFileReader->ReadRecordBatch(iBatch);
     713           3 :                 if (!result.ok())
     714             :                 {
     715           0 :                     return nullptr;
     716             :                 }
     717           3 :                 return CPLSPrintf("%" PRId64, (*result)->num_rows());
     718             :             }
     719             :         }
     720           0 :         return nullptr;
     721             :     }
     722         250 :     else if (pszDomain != nullptr && EQUAL(pszDomain, "_ARROW_METADATA_"))
     723             :     {
     724             :         const auto kv_metadata =
     725           5 :             (m_poRecordBatchFileReader ? m_poRecordBatchFileReader->schema()
     726           8 :                                        : m_poRecordBatchReader->schema())
     727          10 :                 ->metadata();
     728           5 :         if (kv_metadata && kv_metadata->Contains(pszName))
     729             :         {
     730           5 :             auto metadataItem = kv_metadata->Get(pszName);
     731           5 :             if (metadataItem.ok())
     732             :             {
     733           5 :                 return CPLSPrintf("%s", metadataItem->c_str());
     734             :             }
     735             :         }
     736           0 :         return nullptr;
     737             :     }
     738         476 :     else if (m_poRecordBatchFileReader != nullptr && pszDomain != nullptr &&
     739         231 :              EQUAL(pszDomain, "_ARROW_FOOTER_METADATA_"))
     740             :     {
     741           2 :         const auto kv_metadata = m_poRecordBatchFileReader->metadata();
     742           1 :         if (kv_metadata && kv_metadata->Contains(pszName))
     743             :         {
     744           1 :             auto metadataItem = kv_metadata->Get(pszName);
     745           1 :             if (metadataItem.ok())
     746             :             {
     747           1 :                 return CPLSPrintf("%s", metadataItem->c_str());
     748             :             }
     749             :         }
     750           0 :         return nullptr;
     751             :     }
     752         244 :     return OGRLayer::GetMetadataItem(pszName, pszDomain);
     753             : }
     754             : 
     755             : /************************************************************************/
     756             : /*                           GetMetadata()                              */
     757             : /************************************************************************/
     758             : 
     759          34 : char **OGRFeatherLayer::GetMetadata(const char *pszDomain)
     760             : {
     761             :     // Mostly for unit test purposes
     762          34 :     if (pszDomain != nullptr && EQUAL(pszDomain, "_ARROW_METADATA_"))
     763             :     {
     764           5 :         m_aosFeatherMetadata.Clear();
     765             :         const auto kv_metadata =
     766           5 :             (m_poRecordBatchFileReader ? m_poRecordBatchFileReader->schema()
     767           8 :                                        : m_poRecordBatchReader->schema())
     768          10 :                 ->metadata();
     769           5 :         if (kv_metadata)
     770             :         {
     771          11 :             for (const auto &kv : kv_metadata->sorted_pairs())
     772             :             {
     773             :                 m_aosFeatherMetadata.SetNameValue(kv.first.c_str(),
     774           6 :                                                   kv.second.c_str());
     775             :             }
     776             :         }
     777           5 :         return m_aosFeatherMetadata.List();
     778             :     }
     779          41 :     if (m_poRecordBatchFileReader != nullptr && pszDomain != nullptr &&
     780          12 :         EQUAL(pszDomain, "_ARROW_FOOTER_METADATA_"))
     781             :     {
     782           2 :         m_aosFeatherMetadata.Clear();
     783           4 :         const auto kv_metadata = m_poRecordBatchFileReader->metadata();
     784           2 :         if (kv_metadata)
     785             :         {
     786           3 :             for (const auto &kv : kv_metadata->sorted_pairs())
     787             :             {
     788             :                 m_aosFeatherMetadata.SetNameValue(kv.first.c_str(),
     789           1 :                                                   kv.second.c_str());
     790             :             }
     791             :         }
     792           2 :         return m_aosFeatherMetadata.List();
     793             :     }
     794          27 :     return OGRLayer::GetMetadata(pszDomain);
     795             : }

Generated by: LCOV version 1.14