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

Generated by: LCOV version 1.14