LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/adbc - ogradbclayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 608 0.0 %
Date: 2026-03-26 23:25:44 Functions: 0 33 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Arrow Database Connectivity driver
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
       9             :  * Copyright (c) 2024, Dewey Dunnington <dewey@voltrondata.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogr_adbc.h"
      15             : #include "ogr_spatialref.h"
      16             : #include "ogr_p.h"
      17             : #include "cpl_json.h"
      18             : 
      19             : #include <cassert>
      20             : #include <cmath>
      21             : #include <limits>
      22             : #include <map>
      23             : #include <set>
      24             : 
      25             : #define ADBC_CALL(func, ...) m_poDS->m_driver.func(__VA_ARGS__)
      26             : 
      27           0 : OGRArrowArrayToOGRFeatureAdapterLayer::~OGRArrowArrayToOGRFeatureAdapterLayer()
      28             : {
      29           0 :     m_poLayerDefn->Release();
      30           0 : }
      31             : 
      32             : /************************************************************************/
      33             : /*                     GetGeometryTypeFromString()                      */
      34             : /************************************************************************/
      35             : 
      36           0 : static OGRwkbGeometryType GetGeometryTypeFromString(const std::string &osType)
      37             : {
      38           0 :     OGRwkbGeometryType eGeomType = wkbUnknown;
      39           0 :     OGRReadWKTGeometryType(osType.c_str(), &eGeomType);
      40           0 :     if (eGeomType == wkbUnknown && !osType.empty())
      41             :     {
      42           0 :         CPLDebug("ADBC", "Unknown geometry type: %s", osType.c_str());
      43             :     }
      44           0 :     return eGeomType;
      45             : }
      46             : 
      47             : /************************************************************************/
      48             : /*                            OGRADBCLayer()                            */
      49             : /************************************************************************/
      50             : 
      51           0 : OGRADBCLayer::OGRADBCLayer(OGRADBCDataset *poDS, const char *pszName,
      52           0 :                            const std::string &osStatement, bool bInternalUse)
      53             :     : m_poDS(poDS), m_osBaseStatement(osStatement),
      54           0 :       m_osModifiedBaseStatement(m_osBaseStatement), m_bInternalUse(bInternalUse)
      55             : {
      56           0 :     SetDescription(pszName);
      57             : 
      58           0 :     memset(&m_schema, 0, sizeof(m_schema));
      59           0 : }
      60             : 
      61             : /************************************************************************/
      62             : /*                            OGRADBCLayer()                            */
      63             : /************************************************************************/
      64             : 
      65           0 : OGRADBCLayer::OGRADBCLayer(OGRADBCDataset *poDS, const char *pszName,
      66             :                            std::unique_ptr<OGRArrowArrayStream> poStream,
      67           0 :                            ArrowSchema *schema, bool bInternalUse)
      68           0 :     : m_poDS(poDS), m_stream(std::move(poStream)), m_bInternalUse(bInternalUse)
      69             : {
      70           0 :     SetDescription(pszName);
      71             : 
      72           0 :     memcpy(&m_schema, schema, sizeof(m_schema));
      73           0 :     schema->release = nullptr;
      74           0 : }
      75             : 
      76             : /************************************************************************/
      77             : /*                            GetLayerDefn()                            */
      78             : /************************************************************************/
      79             : 
      80           0 : const OGRFeatureDefn *OGRADBCLayer::GetLayerDefn() const
      81             : {
      82           0 :     if (!m_poAdapterLayer)
      83           0 :         const_cast<OGRADBCLayer *>(this)->BuildLayerDefn();
      84           0 :     assert(m_poAdapterLayer);
      85           0 :     return m_poAdapterLayer->GetLayerDefn();
      86             : }
      87             : 
      88             : /************************************************************************/
      89             : /*                            GetFIDColumn()                            */
      90             : /************************************************************************/
      91             : 
      92           0 : const char *OGRADBCLayer::GetFIDColumn() const
      93             : {
      94           0 :     if (!m_poAdapterLayer)
      95           0 :         const_cast<OGRADBCLayer *>(this)->BuildLayerDefn();
      96           0 :     return m_osFIDColName.c_str();
      97             : }
      98             : 
      99             : /************************************************************************/
     100             : /*                              GotError()                              */
     101             : /************************************************************************/
     102             : 
     103           0 : bool OGRADBCLayer::GotError()
     104             : {
     105           0 :     if (!m_poAdapterLayer)
     106           0 :         BuildLayerDefn();
     107           0 :     return m_bLayerDefinitionError;
     108             : }
     109             : 
     110             : /************************************************************************/
     111             : /*                    ParseGeometryColumnCovering()                     */
     112             : /************************************************************************/
     113             : 
     114             : //! Parse bounding box column definition
     115           0 : static bool ParseGeometryColumnCovering(const CPLJSONObject &oJSONDef,
     116             :                                         std::string &osBBOXColumn,
     117             :                                         std::string &osXMin,
     118             :                                         std::string &osYMin,
     119             :                                         std::string &osXMax,
     120             :                                         std::string &osYMax)
     121             : {
     122           0 :     const auto oCovering = oJSONDef["covering"];
     123           0 :     if (oCovering.IsValid() &&
     124           0 :         oCovering.GetType() == CPLJSONObject::Type::Object)
     125             :     {
     126           0 :         const auto oBBOX = oCovering["bbox"];
     127           0 :         if (oBBOX.IsValid() && oBBOX.GetType() == CPLJSONObject::Type::Object)
     128             :         {
     129           0 :             const auto oXMin = oBBOX["xmin"];
     130           0 :             const auto oYMin = oBBOX["ymin"];
     131           0 :             const auto oXMax = oBBOX["xmax"];
     132           0 :             const auto oYMax = oBBOX["ymax"];
     133           0 :             if (oXMin.IsValid() && oYMin.IsValid() && oXMax.IsValid() &&
     134           0 :                 oYMax.IsValid() &&
     135           0 :                 oXMin.GetType() == CPLJSONObject::Type::Array &&
     136           0 :                 oYMin.GetType() == CPLJSONObject::Type::Array &&
     137           0 :                 oXMax.GetType() == CPLJSONObject::Type::Array &&
     138           0 :                 oYMax.GetType() == CPLJSONObject::Type::Array)
     139             :             {
     140           0 :                 const auto osXMinArray = oXMin.ToArray();
     141           0 :                 const auto osYMinArray = oYMin.ToArray();
     142           0 :                 const auto osXMaxArray = oXMax.ToArray();
     143           0 :                 const auto osYMaxArray = oYMax.ToArray();
     144           0 :                 if (osXMinArray.Size() == 2 && osYMinArray.Size() == 2 &&
     145           0 :                     osXMaxArray.Size() == 2 && osYMaxArray.Size() == 2 &&
     146           0 :                     osXMinArray[0].GetType() == CPLJSONObject::Type::String &&
     147           0 :                     osXMinArray[1].GetType() == CPLJSONObject::Type::String &&
     148           0 :                     osYMinArray[0].GetType() == CPLJSONObject::Type::String &&
     149           0 :                     osYMinArray[1].GetType() == CPLJSONObject::Type::String &&
     150           0 :                     osXMaxArray[0].GetType() == CPLJSONObject::Type::String &&
     151           0 :                     osXMaxArray[1].GetType() == CPLJSONObject::Type::String &&
     152           0 :                     osYMaxArray[0].GetType() == CPLJSONObject::Type::String &&
     153           0 :                     osYMaxArray[1].GetType() == CPLJSONObject::Type::String &&
     154           0 :                     osXMinArray[0].ToString() == osYMinArray[0].ToString() &&
     155           0 :                     osXMinArray[0].ToString() == osXMaxArray[0].ToString() &&
     156           0 :                     osXMinArray[0].ToString() == osYMaxArray[0].ToString())
     157             :                 {
     158           0 :                     osBBOXColumn = osXMinArray[0].ToString();
     159           0 :                     osXMin = osXMinArray[1].ToString();
     160           0 :                     osYMin = osYMinArray[1].ToString();
     161           0 :                     osXMax = osXMaxArray[1].ToString();
     162           0 :                     osYMax = osYMaxArray[1].ToString();
     163           0 :                     return true;
     164             :                 }
     165             :             }
     166             :         }
     167             :     }
     168           0 :     return false;
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                       ParseGeoParquetColumn()                        */
     173             : /************************************************************************/
     174             : 
     175           0 : static void ParseGeoParquetColumn(
     176             :     const CPLJSONObject &oColumn,
     177             :     std::map<std::string, OGRwkbGeometryType> &oMapType,
     178             :     std::map<std::string, OGREnvelope3D> &oMapExtent,
     179             :     std::map<std::string, OGRADBCLayer::GeomColBBOX>
     180             :         &oMapGeomColumnToCoveringBBOXColumn,
     181             :     std::map<std::string, std::unique_ptr<OGRSpatialReference>>
     182             :         &oMapGeomColumnsFromGeoParquet,
     183             :     std::set<std::string> &oSetCoveringBBoxColumn)
     184             : {
     185           0 :     auto oCrs = oColumn.GetObj("crs");
     186           0 :     if (!oCrs.IsValid())
     187             :     {
     188             :         // WGS 84 is implied if no crs member is found.
     189           0 :         auto poSRS = std::make_unique<OGRSpatialReference>();
     190           0 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     191           0 :         poSRS->importFromEPSG(4326);
     192           0 :         oMapGeomColumnsFromGeoParquet[oColumn.GetName()] = std::move(poSRS);
     193             :     }
     194           0 :     else if (oCrs.GetType() == CPLJSONObject::Type::Object)
     195             :     {
     196             :         // CRS encoded as PROJJSON (extension)
     197           0 :         const auto oType = oCrs["type"];
     198           0 :         if (oType.IsValid() && oType.GetType() == CPLJSONObject::Type::String)
     199             :         {
     200           0 :             const auto osType = oType.ToString();
     201           0 :             if (osType.find("CRS") != std::string::npos)
     202             :             {
     203           0 :                 auto poSRS = std::make_unique<OGRSpatialReference>();
     204           0 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     205             : 
     206           0 :                 if (poSRS->SetFromUserInput(oCrs.ToString().c_str()) ==
     207             :                     OGRERR_NONE)
     208             :                 {
     209           0 :                     oMapGeomColumnsFromGeoParquet[oColumn.GetName()] =
     210           0 :                         std::move(poSRS);
     211             :                 }
     212             :             }
     213             :         }
     214             :     }
     215             :     else
     216             :     {
     217           0 :         oMapGeomColumnsFromGeoParquet[oColumn.GetName()] = nullptr;
     218             :     }
     219             : 
     220           0 :     OGRwkbGeometryType eGeomType = wkbUnknown;
     221           0 :     auto oType = oColumn.GetObj("geometry_types");
     222           0 :     if (oType.GetType() == CPLJSONObject::Type::Array)
     223             :     {
     224           0 :         const auto oTypeArray = oType.ToArray();
     225           0 :         if (oTypeArray.Size() == 1)
     226             :         {
     227           0 :             eGeomType = GetGeometryTypeFromString(oTypeArray[0].ToString());
     228             :         }
     229           0 :         else if (oTypeArray.Size() > 1)
     230             :         {
     231           0 :             const auto PromoteToCollection = [](OGRwkbGeometryType eType)
     232             :             {
     233           0 :                 if (eType == wkbPoint)
     234           0 :                     return wkbMultiPoint;
     235           0 :                 if (eType == wkbLineString)
     236           0 :                     return wkbMultiLineString;
     237           0 :                 if (eType == wkbPolygon)
     238           0 :                     return wkbMultiPolygon;
     239           0 :                 return eType;
     240             :             };
     241           0 :             bool bMixed = false;
     242           0 :             bool bHasMulti = false;
     243           0 :             bool bHasZ = false;
     244           0 :             bool bHasM = false;
     245           0 :             const auto eFirstType = OGR_GT_Flatten(
     246           0 :                 GetGeometryTypeFromString(oTypeArray[0].ToString()));
     247           0 :             const auto eFirstTypeCollection = PromoteToCollection(eFirstType);
     248           0 :             for (int i = 0; i < oTypeArray.Size(); ++i)
     249             :             {
     250             :                 const auto eThisGeom =
     251           0 :                     GetGeometryTypeFromString(oTypeArray[i].ToString());
     252           0 :                 if (PromoteToCollection(OGR_GT_Flatten(eThisGeom)) !=
     253             :                     eFirstTypeCollection)
     254             :                 {
     255           0 :                     bMixed = true;
     256           0 :                     break;
     257             :                 }
     258           0 :                 bHasZ |= OGR_GT_HasZ(eThisGeom) != FALSE;
     259           0 :                 bHasM |= OGR_GT_HasM(eThisGeom) != FALSE;
     260           0 :                 bHasMulti |= (PromoteToCollection(OGR_GT_Flatten(eThisGeom)) ==
     261           0 :                               OGR_GT_Flatten(eThisGeom));
     262             :             }
     263           0 :             if (!bMixed)
     264             :             {
     265           0 :                 if (eFirstTypeCollection == wkbMultiPolygon ||
     266             :                     eFirstTypeCollection == wkbMultiLineString)
     267             :                 {
     268           0 :                     if (bHasMulti)
     269           0 :                         eGeomType = OGR_GT_SetModifier(eFirstTypeCollection,
     270             :                                                        bHasZ, bHasM);
     271             :                     else
     272             :                         eGeomType =
     273           0 :                             OGR_GT_SetModifier(eFirstType, bHasZ, bHasM);
     274             :                 }
     275             :             }
     276             :         }
     277             :     }
     278             : 
     279           0 :     oMapType[oColumn.GetName()] = eGeomType;
     280             : 
     281           0 :     OGREnvelope3D sExtent;
     282           0 :     const auto oBBox = oColumn.GetArray("bbox");
     283           0 :     if (oBBox.IsValid() && oBBox.Size() == 4)
     284             :     {
     285           0 :         sExtent.MinX = oBBox[0].ToDouble();
     286           0 :         sExtent.MinY = oBBox[1].ToDouble();
     287           0 :         sExtent.MinZ = std::numeric_limits<double>::infinity();
     288           0 :         sExtent.MaxX = oBBox[2].ToDouble();
     289           0 :         sExtent.MaxY = oBBox[3].ToDouble();
     290           0 :         sExtent.MaxZ = -std::numeric_limits<double>::infinity();
     291           0 :         if (sExtent.MinX <= sExtent.MaxX)
     292             :         {
     293           0 :             oMapExtent[oColumn.GetName()] = sExtent;
     294             :         }
     295             :     }
     296           0 :     else if (oBBox.IsValid() && oBBox.Size() == 6)
     297             :     {
     298           0 :         sExtent.MinX = oBBox[0].ToDouble();
     299           0 :         sExtent.MinY = oBBox[1].ToDouble();
     300           0 :         sExtent.MinZ = oBBox[2].ToDouble();
     301           0 :         sExtent.MaxX = oBBox[3].ToDouble();
     302           0 :         sExtent.MaxY = oBBox[4].ToDouble();
     303           0 :         sExtent.MaxZ = oBBox[5].ToDouble();
     304           0 :         if (sExtent.MinX <= sExtent.MaxX)
     305             :         {
     306           0 :             oMapExtent[oColumn.GetName()] = sExtent;
     307             :         }
     308             :     }
     309             : 
     310           0 :     std::string osBBOXColumn;
     311           0 :     std::string osXMin, osYMin, osXMax, osYMax;
     312           0 :     if (ParseGeometryColumnCovering(oColumn, osBBOXColumn, osXMin, osYMin,
     313             :                                     osXMax, osYMax))
     314             :     {
     315           0 :         OGRADBCLayer::GeomColBBOX geomColBBOX;
     316             :         const std::string osPrefix =
     317           0 :             std::string("\"")
     318           0 :                 .append(OGRDuplicateCharacter(osBBOXColumn, '"'))
     319           0 :                 .append("\".\"");
     320             : 
     321           0 :         const auto BuildColName = [&osPrefix](const std::string &s)
     322             :         {
     323           0 :             return std::string(osPrefix)
     324           0 :                 .append(OGRDuplicateCharacter(s, '"'))
     325           0 :                 .append("\"");
     326           0 :         };
     327             : 
     328           0 :         geomColBBOX.osXMin = BuildColName(osXMin);
     329           0 :         geomColBBOX.osYMin = BuildColName(osYMin);
     330           0 :         geomColBBOX.osXMax = BuildColName(osXMax);
     331           0 :         geomColBBOX.osYMax = BuildColName(osYMax);
     332           0 :         oMapGeomColumnToCoveringBBOXColumn[oColumn.GetName()] =
     333           0 :             std::move(geomColBBOX);
     334           0 :         oSetCoveringBBoxColumn.insert(std::move(osBBOXColumn));
     335             :     }
     336           0 : }
     337             : 
     338             : /************************************************************************/
     339             : /*                         BuildLayerDefnInit()                         */
     340             : /************************************************************************/
     341             : 
     342           0 : bool OGRADBCLayer::BuildLayerDefnInit(bool bCreateStream)
     343             : {
     344           0 :     CPLAssert(!m_poAdapterLayer);
     345             : 
     346           0 :     m_bLayerDefinitionError = true;
     347           0 :     m_poAdapterLayer = std::make_unique<OGRArrowArrayToOGRFeatureAdapterLayer>(
     348           0 :         GetDescription());
     349             : 
     350           0 :     m_statement = std::make_unique<AdbcStatement>();
     351           0 :     if (!m_osBaseStatement.empty())
     352             :     {
     353           0 :         OGRADBCError error;
     354           0 :         if (ADBC_CALL(StatementNew, m_poDS->m_connection.get(),
     355           0 :                       m_statement.get(), error) != ADBC_STATUS_OK)
     356             :         {
     357           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     358             :                      "AdbcStatementNew() failed: %s", error.message());
     359           0 :             return false;
     360             :         }
     361             : 
     362           0 :         if (ADBC_CALL(StatementSetSqlQuery, m_statement.get(),
     363           0 :                       m_osBaseStatement.c_str(), error) != ADBC_STATUS_OK)
     364             :         {
     365           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     366             :                      "AdbcStatementSetSqlQuery() failed: %s", error.message());
     367           0 :             return false;
     368             :         }
     369             :     }
     370             : 
     371           0 :     if (!m_stream && bCreateStream)
     372             :     {
     373           0 :         OGRADBCError error;
     374           0 :         m_stream = std::make_unique<OGRArrowArrayStream>();
     375           0 :         int64_t rows_affected = -1;
     376           0 :         if (ADBC_CALL(StatementExecuteQuery, m_statement.get(), m_stream->get(),
     377           0 :                       &rows_affected, error) != ADBC_STATUS_OK)
     378             :         {
     379           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     380             :                      "AdbcStatementExecuteQuery() failed: %s", error.message());
     381           0 :             return false;
     382             :         }
     383             : 
     384           0 :         if (m_stream->get_schema(&m_schema) != 0)
     385             :         {
     386           0 :             CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
     387           0 :             return false;
     388             :         }
     389             :     }
     390             : 
     391           0 :     m_bLayerDefinitionError = false;
     392           0 :     return true;
     393             : }
     394             : 
     395             : /************************************************************************/
     396             : /*                           BuildLayerDefn()                           */
     397             : /************************************************************************/
     398             : 
     399           0 : void OGRADBCLayer::BuildLayerDefn()
     400             : {
     401             :     const bool bIsUserLayerDuckDB =
     402           0 :         !m_bInternalUse &&
     403           0 :         STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT ") &&
     404           0 :         m_poDS->m_bIsDuckDBDriver;
     405           0 :     if (!BuildLayerDefnInit(!bIsUserLayerDuckDB))
     406           0 :         return;
     407             : 
     408             :     // Identify geometry columns for Parquet files, and query them with
     409             :     // ST_AsWKB() to avoid getting duckdb_spatial own geometry encoding
     410             :     // (https://github.com/duckdb/duckdb_spatial/blob/a60aa3733741a99c49baaf33390c0f7c8a9598a3/spatial/src/spatial/core/geometry/geometry_serialization.cpp#L11)
     411           0 :     std::map<std::string, std::unique_ptr<OGRSpatialReference>> oMapGeomColumns;
     412           0 :     std::map<std::string, OGRwkbGeometryType> oMapType;
     413           0 :     std::map<std::string, OGREnvelope3D> oMapExtent;
     414           0 :     std::map<std::string, GeomColBBOX> oMapGeomColumnToCoveringBBOXColumn;
     415             : 
     416           0 :     if (bIsUserLayerDuckDB)
     417             :     {
     418             :         // Try to read GeoParquet 'geo' metadata
     419             :         std::map<std::string, std::unique_ptr<OGRSpatialReference>>
     420           0 :             oMapGeomColumnsFromGeoParquet;
     421           0 :         std::set<std::string> oSetCoveringBBoxColumn;
     422             : 
     423           0 :         std::string osGeoParquetMD;
     424           0 :         if (!m_poDS->m_osParquetFilename.empty())
     425             :         {
     426           0 :             auto poMetadataLayer = m_poDS->CreateInternalLayer(
     427           0 :                 std::string("SELECT value FROM parquet_kv_metadata('")
     428           0 :                     .append(OGRDuplicateCharacter(m_poDS->m_osParquetFilename,
     429           0 :                                                   '\''))
     430           0 :                     .append("') WHERE key = 'geo'")
     431           0 :                     .c_str());
     432           0 :             if (!poMetadataLayer->GotError())
     433             :             {
     434             :                 auto f = std::unique_ptr<OGRFeature>(
     435           0 :                     poMetadataLayer->GetNextFeature());
     436           0 :                 if (f)
     437             :                 {
     438           0 :                     int nBytes = 0;
     439           0 :                     const GByte *pabyData = f->GetFieldAsBinary(0, &nBytes);
     440             :                     osGeoParquetMD.assign(
     441           0 :                         reinterpret_cast<const char *>(pabyData), nBytes);
     442             :                     // CPLDebug("ADBC", "%s", osGeoParquetMD.c_str());
     443             :                 }
     444             :             }
     445             :         }
     446           0 :         CPLJSONDocument oDoc;
     447           0 :         if (!osGeoParquetMD.empty() && oDoc.LoadMemory(osGeoParquetMD))
     448             :         {
     449           0 :             const auto oColumns = oDoc.GetRoot().GetObj("columns");
     450           0 :             for (const auto &oColumn : oColumns.GetChildren())
     451             :             {
     452           0 :                 if (oColumn.GetString("encoding") == "WKB")
     453             :                 {
     454           0 :                     ParseGeoParquetColumn(oColumn, oMapType, oMapExtent,
     455             :                                           oMapGeomColumnToCoveringBBOXColumn,
     456             :                                           oMapGeomColumnsFromGeoParquet,
     457             :                                           oSetCoveringBBoxColumn);
     458             :                 }
     459             :             }
     460             :         }
     461             : 
     462           0 :         auto poDescribeLayer = m_poDS->CreateInternalLayer(
     463           0 :             std::string("DESCRIBE ").append(m_osBaseStatement).c_str());
     464           0 :         std::string osNewStatement;
     465           0 :         bool bNewStatement = false;
     466           0 :         if (!poDescribeLayer->GotError() &&
     467           0 :             (m_poDS->m_bIsDuckDBDriver ||
     468             :              // cppcheck-suppress knownConditionTrueFalse
     469           0 :              !oMapGeomColumnsFromGeoParquet.empty()))
     470             :         {
     471           0 :             for (auto &&f : *poDescribeLayer)
     472             :             {
     473           0 :                 const char *pszColName = f->GetFieldAsString("column_name");
     474           0 :                 if (cpl::contains(oSetCoveringBBoxColumn, pszColName))
     475             :                 {
     476           0 :                     bNewStatement = true;
     477           0 :                     continue;
     478             :                 }
     479             : 
     480             :                 // f->DumpReadable(stdout);
     481           0 :                 if (osNewStatement.empty())
     482           0 :                     osNewStatement = "SELECT ";
     483             :                 else
     484           0 :                     osNewStatement += ", ";
     485             : 
     486           0 :                 auto oIter = oMapGeomColumnsFromGeoParquet.find(pszColName);
     487           0 :                 if (oIter != oMapGeomColumnsFromGeoParquet.end())
     488             :                 {
     489           0 :                     oMapGeomColumns[pszColName] = std::move(oIter->second);
     490             :                 }
     491           0 :                 const char *pszColType = f->GetFieldAsString("column_type");
     492           0 :                 if (STARTS_WITH_CI(pszColType, "GEOMETRY") &&
     493           0 :                     m_poDS->m_bSpatialLoaded)
     494             :                 {
     495           0 :                     bNewStatement = true;
     496           0 :                     osNewStatement += "ST_AsWKB(\"";
     497           0 :                     osNewStatement += OGRDuplicateCharacter(pszColName, '"');
     498           0 :                     osNewStatement += "\") AS ";
     499           0 :                     if (oIter == oMapGeomColumnsFromGeoParquet.end())
     500             :                     {
     501           0 :                         oMapGeomColumns[pszColName] = nullptr;
     502             :                         // Below is with DuckDB >= 1.5
     503           0 :                         if (STARTS_WITH_CI(pszColType, "GEOMETRY('EPSG:"))
     504             :                         {
     505             :                             auto poSRS =
     506           0 :                                 std::make_unique<OGRSpatialReference>();
     507           0 :                             poSRS->SetAxisMappingStrategy(
     508             :                                 OAMS_TRADITIONAL_GIS_ORDER);
     509           0 :                             if (poSRS->importFromEPSG(atoi(
     510           0 :                                     pszColType + strlen("GEOMETRY('EPSG:"))) ==
     511             :                                 OGRERR_NONE)
     512             :                             {
     513           0 :                                 oMapGeomColumns[pszColName] = std::move(poSRS);
     514             :                             }
     515             :                         }
     516             :                     }
     517             :                 }
     518           0 :                 osNewStatement += '"';
     519           0 :                 osNewStatement += OGRDuplicateCharacter(pszColName, '"');
     520           0 :                 osNewStatement += '"';
     521             :             }
     522           0 :             m_osModifiedSelect = osNewStatement;
     523           0 :             osNewStatement += " FROM (";
     524           0 :             osNewStatement += m_osBaseStatement;
     525           0 :             osNewStatement += " )";
     526             :         }
     527             : 
     528           0 :         if (bNewStatement)
     529             :         {
     530             :             // CPLDebug("ADBC", "%s -> %s", m_osBaseStatement.c_str(), osNewStatement.c_str());
     531           0 :             if (ReplaceStatement(osNewStatement.c_str()))
     532             :             {
     533           0 :                 m_osModifiedBaseStatement = std::move(osNewStatement);
     534             :             }
     535             :             else
     536             :             {
     537           0 :                 m_osModifiedSelect.clear();
     538           0 :                 oMapGeomColumns.clear();
     539             :             }
     540             :         }
     541             : 
     542           0 :         if (!m_stream)
     543             :         {
     544           0 :             auto stream = std::make_unique<OGRArrowArrayStream>();
     545           0 :             if (!GetArrowStreamInternal(stream->get()))
     546             :             {
     547           0 :                 return;
     548             :             }
     549           0 :             m_stream = std::move(stream);
     550           0 :             if (m_stream->get_schema(&m_schema) != 0)
     551             :             {
     552           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
     553           0 :                 return;
     554             :             }
     555             :         }
     556             :     }
     557             : 
     558           0 :     for (int i = 0; i < m_schema.n_children; ++i)
     559             :     {
     560           0 :         const char *pszColName = m_schema.children[i]->name;
     561           0 :         auto oIter = oMapGeomColumns.find(pszColName);
     562           0 :         if (oIter != oMapGeomColumns.end())
     563             :         {
     564           0 :             OGRGeomFieldDefn oGeomFieldDefn(pszColName, oMapType[pszColName]);
     565           0 :             auto poSRS = std::move(oIter->second).release();
     566           0 :             if (poSRS)
     567             :             {
     568           0 :                 oGeomFieldDefn.SetSpatialRef(poSRS);
     569           0 :                 poSRS->Release();
     570             :             }
     571           0 :             m_poAdapterLayer->m_poLayerDefn->AddGeomFieldDefn(&oGeomFieldDefn);
     572             : 
     573           0 :             m_extents.push_back(oMapExtent[pszColName]);
     574           0 :             m_geomColBBOX.push_back(
     575           0 :                 oMapGeomColumnToCoveringBBOXColumn[pszColName]);
     576             :         }
     577             :         else
     578             :         {
     579           0 :             m_poAdapterLayer->CreateFieldFromArrowSchema(m_schema.children[i]);
     580             :         }
     581             :     }
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*                           ~OGRADBCLayer()                            */
     586             : /************************************************************************/
     587             : 
     588           0 : OGRADBCLayer::~OGRADBCLayer()
     589             : {
     590           0 :     OGRADBCError error;
     591           0 :     if (m_statement)
     592           0 :         ADBC_CALL(StatementRelease, m_statement.get(), error);
     593           0 :     if (m_schema.release)
     594           0 :         m_schema.release(&m_schema);
     595           0 : }
     596             : 
     597             : /************************************************************************/
     598             : /*                          ReplaceStatement()                          */
     599             : /************************************************************************/
     600             : 
     601           0 : bool OGRADBCLayer::ReplaceStatement(const char *pszNewStatement)
     602             : {
     603             :     // CPLDebug("ADBC", "%s", pszNewStatement);
     604           0 :     OGRADBCError error;
     605           0 :     auto statement = std::make_unique<AdbcStatement>();
     606           0 :     if (ADBC_CALL(StatementNew, m_poDS->m_connection.get(), statement.get(),
     607           0 :                   error) != ADBC_STATUS_OK)
     608             :     {
     609           0 :         CPLError(CE_Failure, CPLE_AppDefined, "AdbcStatementNew() failed: %s",
     610             :                  error.message());
     611           0 :         ADBC_CALL(StatementRelease, statement.get(), error);
     612             :     }
     613           0 :     else if (ADBC_CALL(StatementSetSqlQuery, statement.get(), pszNewStatement,
     614           0 :                        error) != ADBC_STATUS_OK)
     615             :     {
     616           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     617             :                  "AdbcStatementSetSqlQuery() failed: %s", error.message());
     618           0 :         error.clear();
     619           0 :         ADBC_CALL(StatementRelease, statement.get(), error);
     620             :     }
     621             :     else
     622             :     {
     623           0 :         auto stream = std::make_unique<OGRArrowArrayStream>();
     624           0 :         int64_t rows_affected = -1;
     625             :         ArrowSchema newSchema;
     626           0 :         memset(&newSchema, 0, sizeof(newSchema));
     627           0 :         if (ADBC_CALL(StatementExecuteQuery, statement.get(), stream->get(),
     628           0 :                       &rows_affected, error) != ADBC_STATUS_OK)
     629             :         {
     630           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     631             :                      "AdbcStatementExecuteQuery() failed: %s", error.message());
     632           0 :             error.clear();
     633           0 :             ADBC_CALL(StatementRelease, statement.get(), error);
     634             :         }
     635           0 :         else if (stream->get_schema(&newSchema) != 0)
     636             :         {
     637           0 :             CPLError(CE_Failure, CPLE_AppDefined, "get_schema() failed");
     638           0 :             ADBC_CALL(StatementRelease, statement.get(), error);
     639             :         }
     640             :         else
     641             :         {
     642           0 :             if (m_schema.release)
     643           0 :                 m_schema.release(&m_schema);
     644           0 :             memcpy(&m_schema, &newSchema, sizeof(newSchema));
     645             : 
     646           0 :             if (m_statement)
     647           0 :                 ADBC_CALL(StatementRelease, m_statement.get(), error);
     648           0 :             m_statement = std::move(statement);
     649             : 
     650           0 :             m_stream = std::move(stream);
     651             : 
     652           0 :             return true;
     653             :         }
     654             :     }
     655           0 :     return false;
     656             : }
     657             : 
     658             : /************************************************************************/
     659             : /*                         GetNextRawFeature()                          */
     660             : /************************************************************************/
     661             : 
     662           0 : OGRFeature *OGRADBCLayer::GetNextRawFeature()
     663             : {
     664           0 :     if (!m_poAdapterLayer)
     665           0 :         BuildLayerDefn();
     666           0 :     RunDeferredCreation();
     667             : 
     668           0 :     if (m_bEOF || m_bLayerDefinitionError)
     669           0 :         return nullptr;
     670             : 
     671           0 :     if (m_nIdx == m_poAdapterLayer->m_apoFeatures.size())
     672             :     {
     673           0 :         m_nIdx = 0;
     674           0 :         m_poAdapterLayer->m_apoFeatures.clear();
     675             : 
     676             :         struct ArrowArray array;
     677           0 :         for (int i = 0; i < 2; ++i)
     678             :         {
     679           0 :             if (!m_stream)
     680             :             {
     681           0 :                 auto stream = std::make_unique<OGRArrowArrayStream>();
     682           0 :                 if (!GetArrowStreamInternal(stream->get()))
     683             :                 {
     684           0 :                     m_bEOF = true;
     685           0 :                     return nullptr;
     686             :                 }
     687           0 :                 m_stream = std::move(stream);
     688             :             }
     689             : 
     690           0 :             memset(&array, 0, sizeof(array));
     691           0 :             if (m_stream->get_next(&array) != 0)
     692             :             {
     693           0 :                 if (m_bNextStreamUsageMaybeInvalid)
     694             :                 {
     695           0 :                     m_bNextStreamUsageMaybeInvalid = false;
     696           0 :                     m_stream.reset();
     697             :                 }
     698             :                 else
     699             :                 {
     700           0 :                     m_bEOF = true;
     701           0 :                     return nullptr;
     702             :                 }
     703             :             }
     704             :             else
     705             :             {
     706           0 :                 break;
     707             :             }
     708             :         }
     709           0 :         m_bGetNextArrayHasRun = true;
     710             :         const bool bOK =
     711           0 :             array.length
     712           0 :                 ? m_poAdapterLayer->WriteArrowBatch(&m_schema, &array, nullptr)
     713           0 :                 : false;
     714           0 :         if (array.release)
     715           0 :             array.release(&array);
     716           0 :         if (!bOK)
     717             :         {
     718           0 :             m_bEOF = true;
     719           0 :             return nullptr;
     720             :         }
     721             :     }
     722             : 
     723           0 :     auto poFeature = m_poAdapterLayer->m_apoFeatures[m_nIdx++].release();
     724             :     const int nGeomFieldCount =
     725           0 :         m_poAdapterLayer->m_poLayerDefn->GetFieldCount();
     726           0 :     for (int i = 0; i < nGeomFieldCount; ++i)
     727             :     {
     728           0 :         auto poGeom = poFeature->GetGeomFieldRef(i);
     729           0 :         if (poGeom)
     730           0 :             poGeom->assignSpatialReference(
     731           0 :                 m_poAdapterLayer->m_poLayerDefn->GetGeomFieldDefn(i)
     732           0 :                     ->GetSpatialRef());
     733             :     }
     734           0 :     if (m_osFIDColName.empty())
     735           0 :         poFeature->SetFID(m_nFeatureID++);
     736             :     else
     737           0 :         poFeature->SetFID(
     738           0 :             poFeature->GetFieldAsInteger64(m_osFIDColName.c_str()));
     739           0 :     return poFeature;
     740             : }
     741             : 
     742             : /************************************************************************/
     743             : /*                            ResetReading()                            */
     744             : /************************************************************************/
     745             : 
     746           0 : void OGRADBCLayer::ResetReading()
     747             : {
     748           0 :     if (m_nIdx > 0 || m_bEOF)
     749             :     {
     750           0 :         m_poAdapterLayer->m_apoFeatures.clear();
     751           0 :         m_stream.reset();
     752           0 :         m_bEOF = false;
     753           0 :         m_nIdx = 0;
     754           0 :         m_nFeatureID = 0;
     755           0 :         m_bGetNextArrayHasRun = false;
     756             :     }
     757           0 : }
     758             : 
     759             : /************************************************************************/
     760             : /*                             IGetExtent()                             */
     761             : /************************************************************************/
     762             : 
     763           0 : OGRErr OGRADBCLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
     764             :                                 bool bForce)
     765             : {
     766           0 :     if (!m_poAdapterLayer)
     767           0 :         BuildLayerDefn();
     768             : 
     769           0 :     *psExtent = m_extents[iGeomField];
     770           0 :     if (psExtent->IsInit())
     771           0 :         return OGRERR_NONE;
     772             : 
     773           0 :     return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
     774             : }
     775             : 
     776             : /************************************************************************/
     777             : /*                            IGetExtent3D()                            */
     778             : /************************************************************************/
     779             : 
     780           0 : OGRErr OGRADBCLayer::IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
     781             :                                   bool bForce)
     782             : {
     783           0 :     if (!m_poAdapterLayer)
     784           0 :         BuildLayerDefn();
     785             : 
     786           0 :     *psExtent = m_extents[iGeomField];
     787           0 :     if (psExtent->IsInit())
     788           0 :         return OGRERR_NONE;
     789             : 
     790           0 :     return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
     791             : }
     792             : 
     793             : /************************************************************************/
     794             : /*                        GetCurrentStatement()                         */
     795             : /************************************************************************/
     796             : 
     797           0 : std::string OGRADBCLayer::GetCurrentStatement() const
     798             : {
     799           0 :     if (m_poDS->m_bIsDuckDBDriver && !m_osModifiedSelect.empty() &&
     800           0 :         STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") &&
     801           0 :         (!m_osAttributeFilter.empty() ||
     802           0 :          (m_poFilterGeom &&
     803           0 :           (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty() ||
     804           0 :            m_poDS->m_bSpatialLoaded))))
     805             :     {
     806           0 :         std::string osStatement(m_osModifiedSelect);
     807           0 :         osStatement.append(" FROM (")
     808           0 :             .append(m_osBaseStatement)
     809           0 :             .append(") WHERE ");
     810             : 
     811           0 :         bool bAddAnd = false;
     812           0 :         if (m_poFilterGeom)
     813             :         {
     814           0 :             const double dfMinX = std::isinf(m_sFilterEnvelope.MinX)
     815           0 :                                       ? -std::numeric_limits<double>::max()
     816           0 :                                       : m_sFilterEnvelope.MinX;
     817           0 :             const double dfMinY = std::isinf(m_sFilterEnvelope.MinY)
     818           0 :                                       ? -std::numeric_limits<double>::max()
     819           0 :                                       : m_sFilterEnvelope.MinY;
     820           0 :             const double dfMaxX = std::isinf(m_sFilterEnvelope.MaxX)
     821           0 :                                       ? std::numeric_limits<double>::max()
     822           0 :                                       : m_sFilterEnvelope.MaxX;
     823           0 :             const double dfMaxY = std::isinf(m_sFilterEnvelope.MaxY)
     824           0 :                                       ? std::numeric_limits<double>::max()
     825           0 :                                       : m_sFilterEnvelope.MaxY;
     826           0 :             if (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty())
     827             :             {
     828           0 :                 bAddAnd = true;
     829           0 :                 osStatement.append(m_geomColBBOX[m_iGeomFieldFilter].osXMin)
     830           0 :                     .append(" <= ")
     831           0 :                     .append(CPLSPrintf("%.17g", dfMaxX))
     832           0 :                     .append(" AND ")
     833           0 :                     .append(m_geomColBBOX[m_iGeomFieldFilter].osXMax)
     834           0 :                     .append(" >= ")
     835           0 :                     .append(CPLSPrintf("%.17g", dfMinX))
     836           0 :                     .append(" AND ")
     837           0 :                     .append(m_geomColBBOX[m_iGeomFieldFilter].osYMin)
     838           0 :                     .append(" <= ")
     839           0 :                     .append(CPLSPrintf("%.17g", dfMaxY))
     840           0 :                     .append(" AND ")
     841           0 :                     .append(m_geomColBBOX[m_iGeomFieldFilter].osYMax)
     842           0 :                     .append(" >= ")
     843           0 :                     .append(CPLSPrintf("%.17g", dfMinY));
     844             :             }
     845           0 :             if (m_poDS->m_bSpatialLoaded)
     846             :             {
     847           0 :                 if (bAddAnd)
     848           0 :                     osStatement.append(" AND ");
     849           0 :                 bAddAnd = true;
     850           0 :                 osStatement.append("ST_Intersects(\"")
     851           0 :                     .append(OGRDuplicateCharacter(
     852           0 :                         m_poAdapterLayer->m_poLayerDefn
     853           0 :                             ->GetGeomFieldDefn(m_iGeomFieldFilter)
     854             :                             ->GetNameRef(),
     855           0 :                         '"'))
     856             :                     .append(CPLSPrintf(
     857             :                         "\", ST_MakeEnvelope(%.17g,%.17g,%.17g,%.17g))", dfMinX,
     858           0 :                         dfMinY, dfMaxX, dfMaxY));
     859             :             }
     860             :         }
     861           0 :         if (!m_osAttributeFilter.empty())
     862             :         {
     863           0 :             if (bAddAnd)
     864           0 :                 osStatement.append(" AND ");
     865           0 :             osStatement.append("(");
     866           0 :             osStatement.append(m_osAttributeFilter);
     867           0 :             osStatement.append(")");
     868             :         }
     869             : 
     870           0 :         return osStatement;
     871             :     }
     872             :     else
     873             :     {
     874           0 :         return m_osModifiedBaseStatement;
     875             :     }
     876             : }
     877             : 
     878             : /************************************************************************/
     879             : /*                          UpdateStatement()                           */
     880             : /************************************************************************/
     881             : 
     882           0 : bool OGRADBCLayer::UpdateStatement()
     883             : {
     884           0 :     return ReplaceStatement(GetCurrentStatement().c_str());
     885             : }
     886             : 
     887             : /************************************************************************/
     888             : /*                         SetAttributeFilter()                         */
     889             : /************************************************************************/
     890             : 
     891           0 : OGRErr OGRADBCLayer::SetAttributeFilter(const char *pszFilter)
     892             : {
     893           0 :     if (!m_osModifiedSelect.empty() && m_poDS->m_bIsDuckDBDriver &&
     894           0 :         STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM "))
     895             :     {
     896           0 :         m_osAttributeFilter = pszFilter ? pszFilter : "";
     897           0 :         return UpdateStatement() ? OGRERR_NONE : OGRERR_FAILURE;
     898             :     }
     899             :     else
     900             :     {
     901           0 :         return OGRLayer::SetAttributeFilter(pszFilter);
     902             :     }
     903             : }
     904             : 
     905             : /************************************************************************/
     906             : /*                         ISetSpatialFilter()                          */
     907             : /************************************************************************/
     908             : 
     909           0 : OGRErr OGRADBCLayer::ISetSpatialFilter(int iGeomField,
     910             :                                        const OGRGeometry *poGeomIn)
     911             : 
     912             : {
     913           0 :     if (iGeomField < GetLayerDefn()->GetGeomFieldCount())
     914             :     {
     915           0 :         m_iGeomFieldFilter = iGeomField;
     916           0 :         if (InstallFilter(poGeomIn))
     917           0 :             ResetReading();
     918           0 :         UpdateStatement();
     919             :     }
     920           0 :     return OGRERR_NONE;
     921             : }
     922             : 
     923             : /************************************************************************/
     924             : /*                           TestCapability()                           */
     925             : /************************************************************************/
     926             : 
     927           0 : int OGRADBCLayer::TestCapability(const char *pszCap) const
     928             : {
     929           0 :     if (!m_poAdapterLayer)
     930           0 :         const_cast<OGRADBCLayer *>(this)->BuildLayerDefn();
     931             : 
     932           0 :     if (EQUAL(pszCap, OLCFastGetArrowStream))
     933             :     {
     934           0 :         return !m_poFilterGeom && !m_poAttrQuery && m_osAttributeFilter.empty();
     935             :     }
     936           0 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
     937             :     {
     938           0 :         return !m_poFilterGeom && !m_poAttrQuery &&
     939           0 :                m_osAttributeFilter.empty() && m_bIsParquetLayer;
     940             :     }
     941           0 :     else if (EQUAL(pszCap, OLCFastGetExtent))
     942             :     {
     943           0 :         return !m_extents.empty() && m_extents[0].IsInit();
     944             :     }
     945           0 :     else if (EQUAL(pszCap, OLCFastSpatialFilter) && m_iGeomFieldFilter >= 0 &&
     946           0 :              m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount())
     947             :     {
     948           0 :         if (m_poDS->m_bSpatialLoaded && m_poDS->m_bIsDuckDBDataset)
     949             :         {
     950             :             const char *pszGeomColName =
     951           0 :                 m_poAdapterLayer->m_poLayerDefn
     952           0 :                     ->GetGeomFieldDefn(m_iGeomFieldFilter)
     953           0 :                     ->GetNameRef();
     954           0 :             auto poTmpLayer = m_poDS->CreateInternalLayer(CPLSPrintf(
     955             :                 "SELECT 1 FROM sqlite_master WHERE tbl_name = '%s' AND type = "
     956             :                 "'index' AND (sql LIKE '%%USING RTREE (%s)%%' OR sql LIKE "
     957             :                 "'%%USING RTREE (\"%s\")%%')",
     958           0 :                 OGRDuplicateCharacter(GetDescription(), '\'').c_str(),
     959             :                 pszGeomColName,
     960           0 :                 OGRDuplicateCharacter(pszGeomColName, '"').c_str()));
     961           0 :             return !poTmpLayer->GotError() &&
     962           0 :                    std::unique_ptr<OGRFeature>(poTmpLayer->GetNextFeature());
     963             :         }
     964           0 :         else if (!m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty())
     965             :         {
     966             :             // Let's assume that the presence of a geometry bounding box
     967             :             // column is sufficient enough to pretend to have fast spatial
     968             :             // filter capabilities
     969           0 :             return true;
     970             :         }
     971             :     }
     972           0 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     973           0 :         return true;
     974             : 
     975           0 :     return false;
     976             : }
     977             : 
     978             : /************************************************************************/
     979             : /*                             GetDataset()                             */
     980             : /************************************************************************/
     981             : 
     982           0 : GDALDataset *OGRADBCLayer::GetDataset()
     983             : {
     984           0 :     return m_poDS;
     985             : }
     986             : 
     987             : /************************************************************************/
     988             : /*                           GetArrowStream()                           */
     989             : /************************************************************************/
     990             : 
     991           0 : bool OGRADBCLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
     992             :                                   CSLConstList papszOptions)
     993             : {
     994           0 :     if (!m_poAdapterLayer)
     995           0 :         BuildLayerDefn();
     996             : 
     997           0 :     if (m_poFilterGeom || m_poAttrQuery ||
     998           0 :         CPLFetchBool(papszOptions, GAS_OPT_DATETIME_AS_STRING, false))
     999             :     {
    1000           0 :         return OGRLayer::GetArrowStream(out_stream, papszOptions);
    1001             :     }
    1002             : 
    1003           0 :     if (m_stream)
    1004             :     {
    1005           0 :         memcpy(out_stream, m_stream->get(), sizeof(*out_stream));
    1006           0 :         memset(m_stream->get(), 0, sizeof(*out_stream));
    1007           0 :         m_stream.reset();
    1008             :     }
    1009             : 
    1010           0 :     return GetArrowStreamInternal(out_stream);
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                       GetArrowStreamInternal()                       */
    1015             : /************************************************************************/
    1016             : 
    1017           0 : bool OGRADBCLayer::GetArrowStreamInternal(struct ArrowArrayStream *out_stream)
    1018             : {
    1019           0 :     OGRADBCError error;
    1020           0 :     int64_t rows_affected = -1;
    1021           0 :     if (ADBC_CALL(StatementExecuteQuery, m_statement.get(), out_stream,
    1022           0 :                   &rows_affected, error) != ADBC_STATUS_OK)
    1023             :     {
    1024           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1025             :                  "AdbcStatementExecuteQuery() failed: %s", error.message());
    1026           0 :         return false;
    1027             :     }
    1028             : 
    1029           0 :     return true;
    1030             : }
    1031             : 
    1032             : /************************************************************************/
    1033             : /*                   GetFeatureCountSelectCountStar()                   */
    1034             : /************************************************************************/
    1035             : 
    1036           0 : GIntBig OGRADBCLayer::GetFeatureCountSelectCountStar()
    1037             : {
    1038           0 :     const std::string osCurStatement = GetCurrentStatement();
    1039             : 
    1040             :     // Cf https://github.com/duckdb/duckdb/issues/21384
    1041             :     // and https://github.com/apache/arrow-adbc/blob/da58c591ed89b29c9096e4ebc0fe99d369e2bc88/docs/source/cpp/concurrency.rst
    1042           0 :     m_bNextStreamUsageMaybeInvalid = true;
    1043             : 
    1044             :     auto poCountLayer =
    1045           0 :         m_poDS->CreateInternalLayer(std::string("SELECT COUNT(*) FROM (")
    1046           0 :                                         .append(osCurStatement)
    1047           0 :                                         .append(")")
    1048           0 :                                         .c_str());
    1049           0 :     if (!poCountLayer->GotError() &&
    1050           0 :         poCountLayer->GetLayerDefn()->GetFieldCount() == 1)
    1051             :     {
    1052             :         auto poFeature =
    1053           0 :             std::unique_ptr<OGRFeature>(poCountLayer->GetNextFeature());
    1054           0 :         if (poFeature)
    1055           0 :             return poFeature->GetFieldAsInteger64(0);
    1056             :     }
    1057           0 :     if (m_bGetNextArrayHasRun)
    1058             :     {
    1059           0 :         m_bGetNextArrayHasRun = false;
    1060           0 :         return GetFeatureCountSelectCountStar();
    1061             :     }
    1062             : 
    1063           0 :     return -1;
    1064             : }
    1065             : 
    1066             : /************************************************************************/
    1067             : /*                        GetFeatureCountArrow()                        */
    1068             : /************************************************************************/
    1069             : 
    1070           0 : GIntBig OGRADBCLayer::GetFeatureCountArrow()
    1071             : {
    1072           0 :     if (m_nIdx > 0 || m_bEOF)
    1073           0 :         m_stream.reset();
    1074             : 
    1075           0 :     if (!m_stream)
    1076             :     {
    1077           0 :         auto stream = std::make_unique<OGRArrowArrayStream>();
    1078           0 :         if (!GetArrowStreamInternal(stream->get()))
    1079             :         {
    1080           0 :             return -1;
    1081             :         }
    1082           0 :         m_stream = std::move(stream);
    1083             :     }
    1084             : 
    1085           0 :     GIntBig nTotal = 0;
    1086             :     while (true)
    1087             :     {
    1088             :         struct ArrowArray array;
    1089           0 :         memset(&array, 0, sizeof(array));
    1090           0 :         if (m_stream->get_next(&array) != 0)
    1091             :         {
    1092           0 :             if (m_bNextStreamUsageMaybeInvalid)
    1093             :             {
    1094           0 :                 m_bNextStreamUsageMaybeInvalid = false;
    1095           0 :                 auto stream = std::make_unique<OGRArrowArrayStream>();
    1096           0 :                 if (!GetArrowStreamInternal(stream->get()))
    1097             :                 {
    1098           0 :                     return -1;
    1099             :                 }
    1100           0 :                 m_stream = std::move(stream);
    1101           0 :                 if (m_stream->get_next(&array) != 0)
    1102             :                 {
    1103           0 :                     m_stream.reset();
    1104           0 :                     return -1;
    1105             :                 }
    1106             :             }
    1107             :             else
    1108             :             {
    1109           0 :                 m_stream.reset();
    1110           0 :                 return -1;
    1111             :             }
    1112             :         }
    1113           0 :         m_bGetNextArrayHasRun = true;
    1114           0 :         const bool bStop = array.length == 0;
    1115           0 :         nTotal += array.length;
    1116           0 :         if (array.release)
    1117           0 :             array.release(&array);
    1118           0 :         if (bStop)
    1119           0 :             break;
    1120           0 :     }
    1121           0 :     m_stream.reset();
    1122           0 :     return nTotal;
    1123             : }
    1124             : 
    1125             : /************************************************************************/
    1126             : /*                          GetFeatureCount()                           */
    1127             : /************************************************************************/
    1128             : 
    1129           0 : GIntBig OGRADBCLayer::GetFeatureCount(int bForce)
    1130             : {
    1131           0 :     if (!m_poAdapterLayer)
    1132           0 :         BuildLayerDefn();
    1133           0 :     if (m_bLayerDefinitionError)
    1134           0 :         return 0;
    1135             : 
    1136           0 :     if (m_poFilterGeom || m_poAttrQuery || !m_osAttributeFilter.empty())
    1137             :     {
    1138           0 :         if (!m_osModifiedSelect.empty() &&
    1139           0 :             STARTS_WITH_CI(m_osBaseStatement.c_str(), "SELECT * FROM ") &&
    1140           0 :             (!m_poFilterGeom ||
    1141           0 :              !m_geomColBBOX[m_iGeomFieldFilter].osXMin.empty() ||
    1142           0 :              m_poDS->m_bSpatialLoaded))
    1143             :         {
    1144           0 :             auto nCount = GetFeatureCountSelectCountStar();
    1145           0 :             if (nCount >= 0)
    1146           0 :                 return nCount;
    1147             :         }
    1148             : 
    1149           0 :         return OGRLayer::GetFeatureCount(bForce);
    1150             :     }
    1151             : 
    1152           0 :     if (m_bIsParquetLayer)
    1153             :     {
    1154           0 :         return GetFeatureCountParquet();
    1155             :     }
    1156             : 
    1157           0 :     return GetFeatureCountArrow();
    1158             : }
    1159             : 
    1160             : /************************************************************************/
    1161             : /*                       GetFeatureCountParquet()                       */
    1162             : /************************************************************************/
    1163             : 
    1164           0 : GIntBig OGRADBCLayer::GetFeatureCountParquet()
    1165             : {
    1166             :     // Cf https://github.com/duckdb/duckdb/issues/21384
    1167             :     // and https://github.com/apache/arrow-adbc/blob/da58c591ed89b29c9096e4ebc0fe99d369e2bc88/docs/source/cpp/concurrency.rst
    1168           0 :     m_bNextStreamUsageMaybeInvalid = true;
    1169             : 
    1170             :     const std::string osSQL(CPLSPrintf(
    1171             :         "SELECT CAST(SUM(num_rows) AS BIGINT) FROM parquet_file_metadata('%s')",
    1172           0 :         OGRDuplicateCharacter(m_poDS->m_osParquetFilename, '\'').c_str()));
    1173           0 :     auto poCountLayer = m_poDS->CreateInternalLayer(osSQL.c_str());
    1174           0 :     if (!poCountLayer->GotError() &&
    1175           0 :         poCountLayer->GetLayerDefn()->GetFieldCount() == 1)
    1176             :     {
    1177             :         auto poFeature =
    1178           0 :             std::unique_ptr<OGRFeature>(poCountLayer->GetNextFeature());
    1179           0 :         if (poFeature)
    1180           0 :             return poFeature->GetFieldAsInteger64(0);
    1181             :     }
    1182           0 :     if (m_bGetNextArrayHasRun)
    1183             :     {
    1184           0 :         m_bGetNextArrayHasRun = false;
    1185           0 :         return GetFeatureCountParquet();
    1186             :     }
    1187             : 
    1188           0 :     return -1;
    1189             : }
    1190             : 
    1191             : #undef ADBC_CALL

Generated by: LCOV version 1.14