LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 517 579 89.3 %
Date: 2026-09-09 01:17:16 Functions: 16 17 94.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoPackage Translator
       4             :  * Purpose:  Implements OGRGeoPackageLayer class
       5             :  * Author:   Paul Ramsey <pramsey@boundlessgeo.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
       9             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogr_geopackage.h"
      15             : #include "ogrgeopackageutility.h"
      16             : #include "ogrsqliteutility.h"
      17             : #include "ogrlibjsonutils.h"
      18             : #include "ogr_p.h"
      19             : #include "ogr_recordbatch.h"
      20             : #include "ograrrowarrayhelper.h"
      21             : #include "ogrlayerarrow.h"
      22             : 
      23             : /************************************************************************/
      24             : /*                         OGRGeoPackageLayer()                         */
      25             : /************************************************************************/
      26             : 
      27        5269 : OGRGeoPackageLayer::OGRGeoPackageLayer(GDALGeoPackageDataset *poDS)
      28        5269 :     : m_poDS(poDS)
      29             : {
      30        5269 : }
      31             : 
      32             : /************************************************************************/
      33             : /*                        ~OGRGeoPackageLayer()                         */
      34             : /************************************************************************/
      35             : 
      36        5269 : OGRGeoPackageLayer::~OGRGeoPackageLayer()
      37             : {
      38             : 
      39        5269 :     CPLFree(m_pszFidColumn);
      40             : 
      41        5269 :     if (m_poQueryStatement)
      42         894 :         sqlite3_finalize(m_poQueryStatement);
      43             : 
      44        5269 :     if (m_poFeatureDefn)
      45        5269 :         m_poFeatureDefn->Release();
      46        5269 : }
      47             : 
      48             : /************************************************************************/
      49             : /*                            ResetReading()                            */
      50             : /************************************************************************/
      51             : 
      52       25892 : void OGRGeoPackageLayer::ResetReading()
      53             : 
      54             : {
      55       25892 :     ClearStatement();
      56       25892 :     m_iNextShapeId = 0;
      57       25892 :     m_bEOF = false;
      58       25892 : }
      59             : 
      60             : /************************************************************************/
      61             : /*                           ClearStatement()                           */
      62             : /************************************************************************/
      63             : 
      64       27376 : void OGRGeoPackageLayer::ClearStatement()
      65             : 
      66             : {
      67       27376 :     if (m_poQueryStatement != nullptr)
      68             :     {
      69         778 :         CPLDebug("GPKG", "finalize %p", m_poQueryStatement);
      70         778 :         sqlite3_finalize(m_poQueryStatement);
      71         778 :         m_poQueryStatement = nullptr;
      72             :     }
      73       27376 : }
      74             : 
      75             : /************************************************************************/
      76             : /*                           GetNextFeature()                           */
      77             : /************************************************************************/
      78             : 
      79       14171 : OGRFeature *OGRGeoPackageLayer::GetNextFeature()
      80             : 
      81             : {
      82       14171 :     if (m_bEOF)
      83           3 :         return nullptr;
      84             : 
      85       14168 :     if (m_poQueryStatement == nullptr)
      86             :     {
      87         941 :         ResetStatement();
      88         941 :         if (m_poQueryStatement == nullptr)
      89           1 :             return nullptr;
      90             :     }
      91             : 
      92             :     for (; true;)
      93             :     {
      94             :         /* --------------------------------------------------------------------
      95             :          */
      96             :         /*      Fetch a record (unless otherwise instructed) */
      97             :         /* --------------------------------------------------------------------
      98             :          */
      99       14167 :         if (m_bDoStep)
     100             :         {
     101       13675 :             int rc = sqlite3_step(m_poQueryStatement);
     102       13675 :             if (rc != SQLITE_ROW)
     103             :             {
     104         495 :                 if (rc != SQLITE_DONE)
     105             :                 {
     106           0 :                     sqlite3_reset(m_poQueryStatement);
     107           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     108             :                              "In GetNextRawFeature(): sqlite3_step() : %s",
     109           0 :                              sqlite3_errmsg(m_poDS->GetDB()));
     110             :                 }
     111             : 
     112         495 :                 ClearStatement();
     113         495 :                 m_bEOF = true;
     114             : 
     115         495 :                 return nullptr;
     116             :             }
     117             :         }
     118             :         else
     119             :         {
     120         492 :             m_bDoStep = true;
     121             :         }
     122             : 
     123       13672 :         OGRFeature *poFeature = TranslateFeature(m_poQueryStatement);
     124             : 
     125       37299 :         if ((m_poFilterGeom == nullptr ||
     126       27344 :              FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
     127       13672 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     128       13672 :             return poFeature;
     129             : 
     130           0 :         delete poFeature;
     131           0 :     }
     132             : }
     133             : 
     134             : /************************************************************************/
     135             : /*                           ParseDateField()                           */
     136             : /************************************************************************/
     137             : 
     138         464 : bool OGRGeoPackageLayer::ParseDateField(sqlite3_stmt *hStmt, int iRawField,
     139             :                                         int nSqlite3ColType, OGRField *psField,
     140             :                                         const OGRFieldDefn *poFieldDefn,
     141             :                                         GIntBig nFID)
     142             : {
     143         464 :     if (nSqlite3ColType == SQLITE_TEXT)
     144             :     {
     145             :         const char *pszTxt = reinterpret_cast<const char *>(
     146         463 :             sqlite3_column_text(hStmt, iRawField));
     147         463 :         return ParseDateField(pszTxt, psField, poFieldDefn, nFID);
     148             :     }
     149             :     else
     150             :     {
     151           1 :         constexpr int line = __LINE__;
     152           1 :         if (!m_poDS->m_oSetGPKGLayerWarnings[line])
     153             :         {
     154           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     155             :                      "Unexpected data type for record " CPL_FRMT_GIB
     156             :                      " in column %s",
     157             :                      nFID, poFieldDefn->GetNameRef());
     158           1 :             m_poDS->m_oSetGPKGLayerWarnings[line] = true;
     159             :         }
     160           1 :         return false;
     161             :     }
     162             : }
     163             : 
     164         514 : bool OGRGeoPackageLayer::ParseDateField(const char *pszTxt, OGRField *psField,
     165             :                                         const OGRFieldDefn *poFieldDefn,
     166             :                                         GIntBig nFID)
     167             : {
     168         514 :     if (pszTxt == nullptr)
     169             :     {
     170           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     171           0 :                  sqlite3_errmsg(m_poDS->GetDB()));
     172           0 :         return false;
     173             :     }
     174         514 :     const size_t nLen = strlen(pszTxt);
     175             :     // nominal format: "YYYY-MM-DD" (10 characters)
     176         514 :     const bool bNominalFormat =
     177         513 :         (nLen == 10 && pszTxt[4] == '-' && pszTxt[7] == '-' &&
     178         512 :          static_cast<unsigned>(pszTxt[0] - '0') <= 9 &&
     179         512 :          static_cast<unsigned>(pszTxt[1] - '0') <= 9 &&
     180         512 :          static_cast<unsigned>(pszTxt[2] - '0') <= 9 &&
     181         512 :          static_cast<unsigned>(pszTxt[3] - '0') <= 9 &&
     182         512 :          static_cast<unsigned>(pszTxt[5] - '0') <= 9 &&
     183         512 :          static_cast<unsigned>(pszTxt[6] - '0') <= 9 &&
     184        1539 :          static_cast<unsigned>(pszTxt[8] - '0') <= 9 &&
     185         512 :          static_cast<unsigned>(pszTxt[9] - '0') <= 9);
     186             : 
     187         514 :     bool bError = false;
     188         514 :     if (bNominalFormat)
     189             :     {
     190         512 :         psField->Date.Year = static_cast<GUInt16>(
     191         512 :             ((((pszTxt[0] - '0') * 10 + (pszTxt[1] - '0')) * 10) +
     192         512 :              (pszTxt[2] - '0')) *
     193         512 :                 10 +
     194         512 :             (pszTxt[3] - '0'));
     195         512 :         psField->Date.Month =
     196         512 :             static_cast<GByte>((pszTxt[5] - '0') * 10 + (pszTxt[6] - '0'));
     197         512 :         psField->Date.Day =
     198         512 :             static_cast<GByte>((pszTxt[8] - '0') * 10 + (pszTxt[9] - '0'));
     199         512 :         psField->Date.Hour = 0;
     200         512 :         psField->Date.Minute = 0;
     201         512 :         psField->Date.Second = 0.0f;
     202         512 :         psField->Date.TZFlag = 0;
     203         512 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
     204         512 :             psField->Date.Day == 0 || psField->Date.Day > 31)
     205             :         {
     206           0 :             bError = true;
     207             :         }
     208             :     }
     209           2 :     else if (OGRParseDate(pszTxt, psField, OGRPARSEDATE_OPTION_LAX))
     210             :     {
     211           1 :         constexpr int line = __LINE__;
     212           1 :         if (!m_poDS->m_oSetGPKGLayerWarnings[line])
     213             :         {
     214           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     215             :                      "Non-conformant content for record " CPL_FRMT_GIB
     216             :                      " in column %s, %s, "
     217             :                      "successfully parsed",
     218             :                      nFID, poFieldDefn->GetNameRef(), pszTxt);
     219           1 :             m_poDS->m_oSetGPKGLayerWarnings[line] = true;
     220             :         }
     221             :     }
     222             :     else
     223             :     {
     224           1 :         bError = true;
     225             :     }
     226             : 
     227         514 :     if (bError)
     228             :     {
     229           1 :         OGR_RawField_SetUnset(psField);
     230           1 :         constexpr int line = __LINE__;
     231           1 :         if (!m_poDS->m_oSetGPKGLayerWarnings[line])
     232             :         {
     233           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     234             :                      "Invalid content for record " CPL_FRMT_GIB
     235             :                      " in column %s: %s",
     236             :                      nFID, poFieldDefn->GetNameRef(), pszTxt);
     237           1 :             m_poDS->m_oSetGPKGLayerWarnings[line] = true;
     238             :         }
     239           1 :         return false;
     240             :     }
     241             : 
     242         513 :     return true;
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                         ParseDateTimeField()                         */
     247             : /************************************************************************/
     248             : 
     249         455 : bool OGRGeoPackageLayer::ParseDateTimeField(sqlite3_stmt *hStmt, int iRawField,
     250             :                                             int nSqlite3ColType,
     251             :                                             OGRField *psField,
     252             :                                             const OGRFieldDefn *poFieldDefn,
     253             :                                             GIntBig nFID)
     254             : {
     255         455 :     if (nSqlite3ColType == SQLITE_TEXT)
     256             :     {
     257             :         const char *pszTxt = reinterpret_cast<const char *>(
     258         454 :             sqlite3_column_text(hStmt, iRawField));
     259         454 :         return ParseDateTimeField(pszTxt, psField, poFieldDefn, nFID);
     260             :     }
     261             :     else
     262             :     {
     263           1 :         constexpr int line = __LINE__;
     264           1 :         if (!m_poDS->m_oSetGPKGLayerWarnings[line])
     265             :         {
     266           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     267             :                      "Unexpected data type for record " CPL_FRMT_GIB
     268             :                      " in column %s",
     269             :                      nFID, poFieldDefn->GetNameRef());
     270           1 :             m_poDS->m_oSetGPKGLayerWarnings[line] = true;
     271             :         }
     272           1 :         return false;
     273             :     }
     274             : }
     275             : 
     276         509 : bool OGRGeoPackageLayer::ParseDateTimeField(const char *pszTxt,
     277             :                                             OGRField *psField,
     278             :                                             const OGRFieldDefn *poFieldDefn,
     279             :                                             GIntBig nFID)
     280             : {
     281         509 :     if (pszTxt == nullptr)
     282             :     {
     283           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     284           0 :                  sqlite3_errmsg(m_poDS->GetDB()));
     285           0 :         return false;
     286             :     }
     287             : 
     288         509 :     std::string_view sInput(pszTxt);
     289             : 
     290         509 :     if (OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(sInput, psField) ||
     291         515 :         OGRParseDateTimeYYYYMMDDTHHMMSSZ(sInput, psField) ||
     292           6 :         OGRParseDateTimeYYYYMMDDTHHMMZ(sInput, psField))
     293             :     {
     294             :         // nominal format is YYYYMMDDTHHMMSSsssZ before GeoPackage 1.4
     295             :         // GeoPackage 1.4 also accepts omission of seconds and milliseconds
     296             :     }
     297           5 :     else if (OGRParseDate(pszTxt, psField, OGRPARSEDATE_OPTION_LAX))
     298             :     {
     299           4 :         constexpr int line = __LINE__;
     300           4 :         if (!m_poDS->m_oSetGPKGLayerWarnings[line])
     301             :         {
     302           3 :             CPLError(CE_Warning, CPLE_AppDefined,
     303             :                      "Non-conformant content for record " CPL_FRMT_GIB
     304             :                      " in column %s, %s, "
     305             :                      "successfully parsed",
     306             :                      nFID, poFieldDefn->GetNameRef(), pszTxt);
     307           3 :             m_poDS->m_oSetGPKGLayerWarnings[line] = true;
     308             :         }
     309             :     }
     310             :     else
     311             :     {
     312           1 :         OGR_RawField_SetUnset(psField);
     313           1 :         constexpr int line = __LINE__;
     314           1 :         if (!m_poDS->m_oSetGPKGLayerWarnings[line])
     315             :         {
     316           1 :             CPLError(CE_Warning, CPLE_AppDefined,
     317             :                      "Invalid content for record " CPL_FRMT_GIB
     318             :                      " in column %s: %s",
     319             :                      nFID, poFieldDefn->GetNameRef(), pszTxt);
     320           1 :             m_poDS->m_oSetGPKGLayerWarnings[line] = true;
     321             :         }
     322           1 :         return false;
     323             :     }
     324             : 
     325         508 :     return true;
     326             : }
     327             : 
     328             : /************************************************************************/
     329             : /*                          TranslateFeature()                          */
     330             : /************************************************************************/
     331             : 
     332       14961 : OGRFeature *OGRGeoPackageLayer::TranslateFeature(sqlite3_stmt *hStmt)
     333             : 
     334             : {
     335             :     /* -------------------------------------------------------------------- */
     336             :     /*      Create a feature from the current result.                       */
     337             :     /* -------------------------------------------------------------------- */
     338       14961 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
     339             : 
     340             :     /* -------------------------------------------------------------------- */
     341             :     /*      Set FID if we have a column to set it from.                     */
     342             :     /* -------------------------------------------------------------------- */
     343       14961 :     if (m_iFIDCol >= 0)
     344             :     {
     345       13257 :         poFeature->SetFID(sqlite3_column_int64(hStmt, m_iFIDCol));
     346       13257 :         if (m_pszFidColumn == nullptr && poFeature->GetFID() == 0)
     347             :         {
     348             :             // Might be the case for views with joins.
     349           0 :             poFeature->SetFID(m_iNextShapeId);
     350             :         }
     351             :     }
     352             :     else
     353        1704 :         poFeature->SetFID(m_iNextShapeId);
     354             : 
     355       14961 :     m_iNextShapeId++;
     356             : 
     357       14961 :     m_nFeaturesRead++;
     358             : 
     359             :     /* -------------------------------------------------------------------- */
     360             :     /*      Process Geometry if we have a column.                           */
     361             :     /* -------------------------------------------------------------------- */
     362       14961 :     if (m_iGeomCol >= 0)
     363             :     {
     364             :         OGRGeomFieldDefn *poGeomFieldDefn =
     365       13198 :             m_poFeatureDefn->GetGeomFieldDefn(0);
     366       26301 :         if (sqlite3_column_type(hStmt, m_iGeomCol) != SQLITE_NULL &&
     367       13103 :             !poGeomFieldDefn->IsIgnored())
     368             :         {
     369       13093 :             const OGRSpatialReference *poSrs = poGeomFieldDefn->GetSpatialRef();
     370       13093 :             int iGpkgSize = sqlite3_column_bytes(hStmt, m_iGeomCol);
     371             :             // coverity[tainted_data_return]
     372             :             const GByte *pabyGpkg = static_cast<const GByte *>(
     373       13093 :                 sqlite3_column_blob(hStmt, m_iGeomCol));
     374             :             OGRGeometry *poGeom =
     375       13093 :                 GPkgGeometryToOGR(pabyGpkg, iGpkgSize, nullptr);
     376       13093 :             if (poGeom == nullptr)
     377             :             {
     378             :                 // Try also spatialite geometry blobs
     379           1 :                 if (OGRSQLiteImportSpatiaLiteGeometry(pabyGpkg, iGpkgSize,
     380           1 :                                                       &poGeom) != OGRERR_NONE)
     381             :                 {
     382           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     383             :                              "Unable to read geometry");
     384             :                 }
     385             :             }
     386       13093 :             if (poGeom)
     387             :             {
     388       13093 :                 if (m_bUndoDiscardCoordLSBOnReading)
     389             :                 {
     390           3 :                     poGeom->roundCoordinates(
     391             :                         poGeomFieldDefn->GetCoordinatePrecision());
     392             :                 }
     393       13093 :                 poGeom->assignSpatialReference(poSrs);
     394             :             }
     395             : 
     396       13093 :             poFeature->SetGeometryDirectly(poGeom);
     397             :         }
     398             :     }
     399             : 
     400             :     /* -------------------------------------------------------------------- */
     401             :     /*      set the fields.                                                 */
     402             :     /* -------------------------------------------------------------------- */
     403       14961 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
     404      206646 :     for (int iField = 0; iField < nFieldCount; iField++)
     405             :     {
     406             :         const OGRFieldDefn *poFieldDefn =
     407      191685 :             m_poFeatureDefn->GetFieldDefnUnsafe(iField);
     408      191685 :         if (poFieldDefn->IsIgnored())
     409          31 :             continue;
     410             : 
     411      191654 :         const int iRawField = m_anFieldOrdinals[iField];
     412             : 
     413      191654 :         const int nSqlite3ColType = sqlite3_column_type(hStmt, iRawField);
     414      191654 :         if (nSqlite3ColType == SQLITE_NULL)
     415             :         {
     416         766 :             poFeature->SetFieldNull(iField);
     417         766 :             continue;
     418             :         }
     419             : 
     420      190888 :         switch (poFieldDefn->GetType())
     421             :         {
     422        2440 :             case OFTInteger:
     423        2440 :                 poFeature->SetFieldSameTypeUnsafe(
     424             :                     iField, sqlite3_column_int(hStmt, iRawField));
     425        2440 :                 break;
     426             : 
     427         834 :             case OFTInteger64:
     428         834 :                 poFeature->SetFieldSameTypeUnsafe(
     429             :                     iField, sqlite3_column_int64(hStmt, iRawField));
     430         834 :                 break;
     431             : 
     432        1332 :             case OFTReal:
     433        1332 :                 poFeature->SetFieldSameTypeUnsafe(
     434             :                     iField, sqlite3_column_double(hStmt, iRawField));
     435        1332 :                 break;
     436             : 
     437        2612 :             case OFTBinary:
     438             :             {
     439        2612 :                 const int nBytes = sqlite3_column_bytes(hStmt, iRawField);
     440             :                 // coverity[tainted_data_return]
     441        2612 :                 const void *pabyData = sqlite3_column_blob(hStmt, iRawField);
     442        2612 :                 if (pabyData != nullptr || nBytes == 0)
     443             :                 {
     444        2612 :                     poFeature->SetField(iField, nBytes, pabyData);
     445             :                 }
     446             :                 else
     447             :                 {
     448           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "%s",
     449           0 :                              sqlite3_errmsg(m_poDS->GetDB()));
     450             :                 }
     451        2612 :                 break;
     452             :             }
     453             : 
     454         424 :             case OFTDate:
     455             :             {
     456         424 :                 auto psField = poFeature->GetRawFieldRef(iField);
     457         424 :                 CPL_IGNORE_RET_VAL(
     458         424 :                     ParseDateField(hStmt, iRawField, nSqlite3ColType, psField,
     459             :                                    poFieldDefn, poFeature->GetFID()));
     460         424 :                 break;
     461             :             }
     462             : 
     463         415 :             case OFTDateTime:
     464             :             {
     465         415 :                 auto psField = poFeature->GetRawFieldRef(iField);
     466         415 :                 CPL_IGNORE_RET_VAL(ParseDateTimeField(
     467             :                     hStmt, iRawField, nSqlite3ColType, psField, poFieldDefn,
     468             :                     poFeature->GetFID()));
     469         415 :                 break;
     470             :             }
     471             : 
     472      182831 :             case OFTString:
     473             :             {
     474             :                 const char *pszTxt = reinterpret_cast<const char *>(
     475      182831 :                     sqlite3_column_text(hStmt, iRawField));
     476      182831 :                 if (pszTxt)
     477             :                 {
     478      365662 :                     std::string osValue = pszTxt;
     479             :                     // If the field subtype is JSON and the current value cannot
     480             :                     // be parsed as JSON, then convert it to a JSON string
     481      182831 :                     if (poFieldDefn->GetSubType() == OFSTJSON)
     482             :                     {
     483          16 :                         json_object *poObjProp = nullptr;
     484          16 :                         if (!OGRJSonParse(
     485             :                                 osValue.c_str(), &poObjProp, false,
     486          16 :                                 static_cast<int>(osValue.length() + 1)))
     487             :                         {
     488             :                             // Emit warning once
     489           1 :                             CPLErrorOnce(
     490             :                                 CE_Warning, CPLE_AppDefined,
     491             :                                 "Field %s is declared as JSON but the "
     492             :                                 "value is not a valid JSON. Converting to "
     493             :                                 "string.",
     494             :                                 poFieldDefn->GetNameRef());
     495             :                             // Escape and quote
     496           2 :                             osValue = CPLJSONObject(osValue).Format(
     497           1 :                                 CPLJSONObject::PrettyFormat::Plain);
     498             :                         }
     499             :                         else
     500             :                         {
     501          15 :                             osValue = json_object_to_json_string(poObjProp);
     502             :                         }
     503             : 
     504          16 :                         json_object_put(poObjProp);
     505             :                     }
     506             : 
     507      182831 :                     char *pszTxtDup = VSI_STRDUP_VERBOSE(osValue.c_str());
     508      182831 :                     if (pszTxtDup)
     509             :                     {
     510             : 
     511      182831 :                         poFeature->SetFieldSameTypeUnsafe(iField, pszTxtDup);
     512             :                     }
     513             :                 }
     514             :                 else
     515             :                 {
     516           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "%s",
     517           0 :                              sqlite3_errmsg(m_poDS->GetDB()));
     518             :                 }
     519      182831 :                 break;
     520             :             }
     521             : 
     522           0 :             default:
     523           0 :                 break;
     524             :         }
     525             :     }
     526             : 
     527       14961 :     return poFeature;
     528             : }
     529             : 
     530             : /************************************************************************/
     531             : /*                           GetArrowStream()                           */
     532             : /************************************************************************/
     533             : 
     534         136 : bool OGRGeoPackageLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
     535             :                                         CSLConstList papszOptions)
     536             : {
     537         272 :     CPLStringList aosOptions;
     538         136 :     aosOptions.Assign(CSLDuplicate(papszOptions), true);
     539             :     // GeoPackage are assumed to be in UTC. Even if another timezone is used,
     540             :     // we'll do the conversion to UTC
     541         136 :     if (aosOptions.FetchNameValue("TIMEZONE") == nullptr)
     542             :     {
     543         135 :         aosOptions.SetNameValue("TIMEZONE", "UTC");
     544             :     }
     545         272 :     return OGRLayer::GetArrowStream(out_stream, aosOptions.List());
     546             : }
     547             : 
     548             : /************************************************************************/
     549             : /*                         GetNextArrowArray()                          */
     550             : /************************************************************************/
     551             : 
     552         110 : int OGRGeoPackageLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
     553             :                                           struct ArrowArray *out_array)
     554             : {
     555         110 :     if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
     556             :     {
     557           6 :         return OGRLayer::GetNextArrowArray(stream, out_array);
     558             :     }
     559             : 
     560         104 :     int errorErrno = EIO;
     561         104 :     memset(out_array, 0, sizeof(*out_array));
     562             : 
     563         104 :     if (m_bEOF)
     564          20 :         return 0;
     565             : 
     566          84 :     if (m_poQueryStatement == nullptr)
     567             :     {
     568          14 :         GetLayerDefn();
     569          14 :         ResetStatement();
     570          14 :         if (m_poQueryStatement == nullptr)
     571           0 :             return 0;
     572             :     }
     573          84 :     sqlite3_stmt *hStmt = m_poQueryStatement;
     574             : 
     575          84 :     OGRArrowArrayHelper sHelper(m_poDS, m_poFeatureDefn,
     576         168 :                                 m_aosArrowArrayStreamOptions, out_array);
     577          84 :     if (out_array->release == nullptr)
     578             :     {
     579           0 :         return ENOMEM;
     580             :     }
     581             : 
     582             :     struct tm brokenDown;
     583          84 :     memset(&brokenDown, 0, sizeof(brokenDown));
     584             : 
     585          84 :     const bool bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
     586             :         GAS_OPT_DATETIME_AS_STRING, false);
     587             : 
     588          84 :     const uint32_t nMemLimit = OGRArrowArrayHelper::GetMemLimit();
     589          84 :     int iFeat = 0;
     590         304 :     while (iFeat < sHelper.m_nMaxBatchSize)
     591             :     {
     592             :         /* --------------------------------------------------------------------
     593             :          */
     594             :         /*      Fetch a record (unless otherwise instructed) */
     595             :         /* --------------------------------------------------------------------
     596             :          */
     597         302 :         if (m_bDoStep)
     598             :         {
     599         235 :             int rc = sqlite3_step(hStmt);
     600         235 :             if (rc != SQLITE_ROW)
     601             :             {
     602          20 :                 if (rc != SQLITE_DONE)
     603             :                 {
     604           0 :                     sqlite3_reset(hStmt);
     605           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     606             :                              "In GetNextArrowArray(): sqlite3_step() : %s",
     607           0 :                              sqlite3_errmsg(m_poDS->GetDB()));
     608             :                 }
     609             : 
     610          20 :                 ClearStatement();
     611          20 :                 m_bEOF = true;
     612             : 
     613          20 :                 break;
     614             :             }
     615             :         }
     616             :         else
     617             :         {
     618          67 :             m_bDoStep = true;
     619             :         }
     620             : 
     621         282 :         m_iNextShapeId++;
     622             : 
     623         282 :         m_nFeaturesRead++;
     624             : 
     625             :         GIntBig nFID;
     626         282 :         if (m_iFIDCol >= 0)
     627             :         {
     628         282 :             nFID = sqlite3_column_int64(hStmt, m_iFIDCol);
     629         282 :             if (m_pszFidColumn == nullptr && nFID == 0)
     630             :             {
     631             :                 // Might be the case for views with joins.
     632           0 :                 nFID = m_iNextShapeId;
     633             :             }
     634             :         }
     635             :         else
     636           0 :             nFID = m_iNextShapeId;
     637             : 
     638         282 :         if (sHelper.m_panFIDValues)
     639             :         {
     640         272 :             sHelper.m_panFIDValues[iFeat] = nFID;
     641             :         }
     642             : 
     643             :         /* --------------------------------------------------------------------
     644             :          */
     645             :         /*      Process Geometry if we have a column. */
     646             :         /* --------------------------------------------------------------------
     647             :          */
     648         282 :         if (m_iGeomCol >= 0 && sHelper.m_mapOGRGeomFieldToArrowField[0] >= 0)
     649             :         {
     650         282 :             const int iArrowField = sHelper.m_mapOGRGeomFieldToArrowField[0];
     651         282 :             auto psArray = out_array->children[iArrowField];
     652             : 
     653         282 :             size_t nWKBSize = 0;
     654         282 :             if (sqlite3_column_type(hStmt, m_iGeomCol) != SQLITE_NULL)
     655             :             {
     656           0 :                 std::unique_ptr<OGRGeometry> poGeom;
     657         139 :                 const GByte *pabyWkb = nullptr;
     658         139 :                 const int iGpkgSize = sqlite3_column_bytes(hStmt, m_iGeomCol);
     659             :                 // coverity[tainted_data_return]
     660             :                 const GByte *pabyGpkg = static_cast<const GByte *>(
     661         139 :                     sqlite3_column_blob(hStmt, m_iGeomCol));
     662         139 :                 if (m_poFilterGeom == nullptr && iGpkgSize >= 8 && pabyGpkg &&
     663         117 :                     pabyGpkg[0] == 'G' && pabyGpkg[1] == 'P' &&
     664         117 :                     !m_bUndoDiscardCoordLSBOnReading)
     665             :                 {
     666             :                     GPkgHeader oHeader;
     667             : 
     668             :                     /* Read header */
     669             :                     OGRErr err =
     670         117 :                         GPkgHeaderFromWKB(pabyGpkg, iGpkgSize, &oHeader);
     671         117 :                     if (err == OGRERR_NONE)
     672             :                     {
     673             :                         /* WKB pointer */
     674         117 :                         pabyWkb = pabyGpkg + oHeader.nHeaderLen;
     675         117 :                         nWKBSize = iGpkgSize - oHeader.nHeaderLen;
     676         117 :                     }
     677             :                 }
     678             :                 else
     679             :                 {
     680          22 :                     poGeom.reset(
     681             :                         GPkgGeometryToOGR(pabyGpkg, iGpkgSize, nullptr));
     682          22 :                     if (poGeom == nullptr)
     683             :                     {
     684             :                         // Try also spatialite geometry blobs
     685           0 :                         OGRGeometry *poGeomPtr = nullptr;
     686           0 :                         if (OGRSQLiteImportSpatiaLiteGeometry(
     687           0 :                                 pabyGpkg, iGpkgSize, &poGeomPtr) != OGRERR_NONE)
     688             :                         {
     689           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     690             :                                      "Unable to read geometry");
     691             :                         }
     692           0 :                         poGeom.reset(poGeomPtr);
     693             :                     }
     694          22 :                     else if (m_bUndoDiscardCoordLSBOnReading)
     695             :                     {
     696           0 :                         poGeom->roundCoordinates(
     697           0 :                             m_poFeatureDefn->GetGeomFieldDefn(0)
     698             :                                 ->GetCoordinatePrecision());
     699             :                     }
     700          22 :                     if (poGeom != nullptr)
     701             :                     {
     702          22 :                         nWKBSize = poGeom->WkbSize();
     703             :                     }
     704          44 :                     if (m_poFilterGeom != nullptr &&
     705          22 :                         !FilterGeometry(poGeom.get()))
     706             :                     {
     707           2 :                         continue;
     708             :                     }
     709             :                 }
     710             : 
     711         137 :                 if (nWKBSize != 0)
     712             :                 {
     713         137 :                     if (iFeat > 0)
     714             :                     {
     715         100 :                         auto panOffsets = static_cast<int32_t *>(
     716         100 :                             const_cast<void *>(psArray->buffers[1]));
     717         100 :                         const uint32_t nCurLength =
     718         100 :                             static_cast<uint32_t>(panOffsets[iFeat]);
     719         100 :                         if (nWKBSize <= nMemLimit &&
     720         100 :                             nWKBSize > nMemLimit - nCurLength)
     721             :                         {
     722          24 :                             m_bDoStep = false;
     723          24 :                             break;
     724             :                         }
     725             :                     }
     726             : 
     727         113 :                     GByte *outPtr = sHelper.GetPtrForStringOrBinary(
     728             :                         iArrowField, iFeat, nWKBSize);
     729         113 :                     if (outPtr == nullptr)
     730             :                     {
     731           0 :                         errorErrno = ENOMEM;
     732           0 :                         goto error;
     733             :                     }
     734         113 :                     if (poGeom)
     735             :                     {
     736          20 :                         poGeom->exportToWkb(wkbNDR, outPtr, wkbVariantIso);
     737             :                     }
     738             :                     else
     739             :                     {
     740          93 :                         memcpy(outPtr, pabyWkb, nWKBSize);
     741             :                     }
     742             :                 }
     743             :                 else
     744             :                 {
     745           0 :                     sHelper.SetEmptyStringOrBinary(psArray, iFeat);
     746             :                 }
     747             :             }
     748             : 
     749         256 :             if (nWKBSize == 0)
     750             :             {
     751         143 :                 if (!sHelper.SetNull(iArrowField, iFeat))
     752             :                 {
     753           0 :                     errorErrno = ENOMEM;
     754           0 :                     goto error;
     755             :                 }
     756             :             }
     757             :         }
     758             : 
     759        1783 :         for (int iField = 0; iField < sHelper.m_nFieldCount; iField++)
     760             :         {
     761        1565 :             const int iArrowField = sHelper.m_mapOGRFieldToArrowField[iField];
     762        1565 :             if (iArrowField < 0)
     763           0 :                 continue;
     764             :             const OGRFieldDefn *poFieldDefn =
     765        1565 :                 m_poFeatureDefn->GetFieldDefnUnsafe(iField);
     766             : 
     767        1565 :             auto psArray = out_array->children[iArrowField];
     768        1565 :             const int iRawField = m_anFieldOrdinals[iField];
     769             : 
     770        1565 :             const int nSqlite3ColType = sqlite3_column_type(hStmt, iRawField);
     771        1565 :             if (nSqlite3ColType == SQLITE_NULL)
     772             :             {
     773         260 :                 if (!sHelper.SetNull(iArrowField, iFeat))
     774             :                 {
     775           0 :                     errorErrno = ENOMEM;
     776           0 :                     goto error;
     777             :                 }
     778         260 :                 continue;
     779             :             }
     780             : 
     781        1305 :             switch (poFieldDefn->GetType())
     782             :             {
     783         876 :                 case OFTInteger:
     784             :                 {
     785         876 :                     const int nVal = sqlite3_column_int(hStmt, iRawField);
     786         876 :                     if (poFieldDefn->GetSubType() == OFSTBoolean)
     787             :                     {
     788          41 :                         if (nVal != 0)
     789             :                         {
     790          40 :                             sHelper.SetBoolOn(psArray, iFeat);
     791             :                         }
     792             :                     }
     793         835 :                     else if (poFieldDefn->GetSubType() == OFSTInt16)
     794             :                     {
     795          41 :                         sHelper.SetInt16(psArray, iFeat,
     796             :                                          static_cast<int16_t>(nVal));
     797             :                     }
     798             :                     else
     799             :                     {
     800         794 :                         sHelper.SetInt32(psArray, iFeat, nVal);
     801             :                     }
     802         876 :                     break;
     803             :                 }
     804             : 
     805          50 :                 case OFTInteger64:
     806             :                 {
     807          50 :                     sHelper.SetInt64(psArray, iFeat,
     808          50 :                                      sqlite3_column_int64(hStmt, iRawField));
     809          50 :                     break;
     810             :                 }
     811             : 
     812          90 :                 case OFTReal:
     813             :                 {
     814             :                     const double dfVal =
     815          90 :                         sqlite3_column_double(hStmt, iRawField);
     816          90 :                     if (poFieldDefn->GetSubType() == OFSTFloat32)
     817             :                     {
     818          40 :                         sHelper.SetFloat(psArray, iFeat,
     819             :                                          static_cast<float>(dfVal));
     820             :                     }
     821             :                     else
     822             :                     {
     823          50 :                         sHelper.SetDouble(psArray, iFeat, dfVal);
     824             :                     }
     825          90 :                     break;
     826             :                 }
     827             : 
     828          98 :                 case OFTBinary:
     829             :                 {
     830             :                     const uint32_t nBytes = static_cast<uint32_t>(
     831          98 :                         sqlite3_column_bytes(hStmt, iRawField));
     832             :                     // coverity[tainted_data_return]
     833             :                     const void *pabyData =
     834          98 :                         sqlite3_column_blob(hStmt, iRawField);
     835          98 :                     if (pabyData != nullptr || nBytes == 0)
     836             :                     {
     837          98 :                         if (iFeat > 0)
     838             :                         {
     839          74 :                             auto panOffsets = static_cast<int32_t *>(
     840          74 :                                 const_cast<void *>(psArray->buffers[1]));
     841          74 :                             const uint32_t nCurLength =
     842          74 :                                 static_cast<uint32_t>(panOffsets[iFeat]);
     843          74 :                             if (nBytes <= nMemLimit &&
     844          74 :                                 nBytes > nMemLimit - nCurLength)
     845             :                             {
     846          19 :                                 m_bDoStep = false;
     847          19 :                                 m_iNextShapeId--;
     848          19 :                                 m_nFeaturesRead--;
     849          19 :                                 goto after_loop;
     850             :                             }
     851             :                         }
     852             : 
     853          79 :                         GByte *outPtr = sHelper.GetPtrForStringOrBinary(
     854             :                             iArrowField, iFeat, nBytes);
     855          79 :                         if (outPtr == nullptr)
     856             :                         {
     857           0 :                             errorErrno = ENOMEM;
     858           0 :                             goto error;
     859             :                         }
     860          79 :                         if (nBytes)
     861          79 :                             memcpy(outPtr, pabyData, nBytes);
     862             :                     }
     863             :                     else
     864             :                     {
     865           0 :                         sHelper.SetEmptyStringOrBinary(psArray, iFeat);
     866             :                     }
     867          79 :                     break;
     868             :                 }
     869             : 
     870          40 :                 case OFTDate:
     871             :                 {
     872             :                     OGRField ogrField;
     873          40 :                     if (ParseDateField(hStmt, iRawField, nSqlite3ColType,
     874             :                                        &ogrField, poFieldDefn, nFID))
     875             :                     {
     876          40 :                         sHelper.SetDate(psArray, iFeat, brokenDown, ogrField);
     877             :                     }
     878          40 :                     break;
     879             :                 }
     880             : 
     881          43 :                 case OFTDateTime:
     882             :                 {
     883          43 :                     if (!bDateTimeAsString)
     884             :                     {
     885             :                         OGRField ogrField;
     886          40 :                         if (ParseDateTimeField(hStmt, iRawField,
     887             :                                                nSqlite3ColType, &ogrField,
     888             :                                                poFieldDefn, nFID))
     889             :                         {
     890          40 :                             sHelper.SetDateTime(psArray, iFeat, brokenDown,
     891          40 :                                                 sHelper.m_anTZFlags[iField],
     892             :                                                 ogrField);
     893             :                         }
     894          40 :                         break;
     895             :                     }
     896             :                     else
     897             :                     {
     898             :                         [[fallthrough]];
     899             :                     }
     900             :                 }
     901             : 
     902             :                 case OFTString:
     903             :                 {
     904             :                     const auto pszTxt = reinterpret_cast<const char *>(
     905         111 :                         sqlite3_column_text(hStmt, iRawField));
     906         111 :                     if (pszTxt != nullptr)
     907             :                     {
     908         111 :                         const size_t nBytes = strlen(pszTxt);
     909         111 :                         if (iFeat > 0)
     910             :                         {
     911          86 :                             auto panOffsets = static_cast<int32_t *>(
     912          86 :                                 const_cast<void *>(psArray->buffers[1]));
     913          86 :                             const uint32_t nCurLength =
     914          86 :                                 static_cast<uint32_t>(panOffsets[iFeat]);
     915          86 :                             if (nBytes <= nMemLimit &&
     916          86 :                                 nBytes > nMemLimit - nCurLength)
     917             :                             {
     918          19 :                                 m_bDoStep = false;
     919          19 :                                 m_iNextShapeId--;
     920          19 :                                 m_nFeaturesRead--;
     921          19 :                                 goto after_loop;
     922             :                             }
     923             :                         }
     924             : 
     925          92 :                         GByte *outPtr = sHelper.GetPtrForStringOrBinary(
     926             :                             iArrowField, iFeat, nBytes);
     927          92 :                         if (outPtr == nullptr)
     928             :                         {
     929           0 :                             errorErrno = ENOMEM;
     930           0 :                             goto error;
     931             :                         }
     932          92 :                         if (nBytes)
     933          92 :                             memcpy(outPtr, pszTxt, nBytes);
     934             :                     }
     935             :                     else
     936             :                     {
     937           0 :                         sHelper.SetEmptyStringOrBinary(psArray, iFeat);
     938           0 :                         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     939           0 :                                  sqlite3_errmsg(m_poDS->GetDB()));
     940             :                     }
     941          92 :                     break;
     942             :                 }
     943             : 
     944           0 :                 default:
     945           0 :                     break;
     946             :             }
     947             :         }
     948             : 
     949         218 :         ++iFeat;
     950             :     }
     951           2 : after_loop:
     952          84 :     sHelper.Shrink(iFeat);
     953          84 :     if (iFeat == 0)
     954           3 :         sHelper.ClearArray();
     955             : 
     956          84 :     return 0;
     957             : 
     958           0 : error:
     959           0 :     sHelper.ClearArray();
     960           0 :     return errorErrno;
     961             : }
     962             : 
     963             : /************************************************************************/
     964             : /*                            GetFIDColumn()                            */
     965             : /************************************************************************/
     966             : 
     967        3573 : const char *OGRGeoPackageLayer::GetFIDColumn() const
     968             : {
     969        3573 :     if (!m_pszFidColumn)
     970          13 :         return "";
     971             :     else
     972        3560 :         return m_pszFidColumn;
     973             : }
     974             : 
     975             : /************************************************************************/
     976             : /*                           TestCapability()                           */
     977             : /************************************************************************/
     978             : 
     979         104 : bool OGRGeoPackageLayer::TestCapability(const char *pszCap) const
     980             : {
     981         104 :     if (EQUAL(pszCap, OLCIgnoreFields))
     982           5 :         return TRUE;
     983          99 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
     984           4 :         return TRUE;
     985          95 :     else if (EQUAL(pszCap, OLCFastGetArrowStream))
     986          69 :         return TRUE;
     987          26 :     else if (EQUAL(pszCap, OLCZGeometries))
     988           3 :         return TRUE;
     989             :     else
     990          23 :         return FALSE;
     991             : }
     992             : 
     993             : /************************************************************************/
     994             : /*                          BuildFeatureDefn()                          */
     995             : /*                                                                      */
     996             : /*      Build feature definition from a set of column definitions       */
     997             : /*      set on a statement.  Sift out geometry and FID fields.          */
     998             : /************************************************************************/
     999             : 
    1000         841 : void OGRGeoPackageLayer::BuildFeatureDefn(const char *pszLayerName,
    1001             :                                           sqlite3_stmt *hStmt)
    1002             : 
    1003             : {
    1004         841 :     m_poFeatureDefn = new OGRSQLiteFeatureDefn(pszLayerName);
    1005         841 :     SetDescription(m_poFeatureDefn->GetName());
    1006         841 :     m_poFeatureDefn->SetGeomType(wkbNone);
    1007         841 :     m_poFeatureDefn->Reference();
    1008             : 
    1009         841 :     const int nRawColumns = sqlite3_column_count(hStmt);
    1010             : 
    1011         841 :     m_anFieldOrdinals.resize(nRawColumns);
    1012             : 
    1013             :     const bool bPromoteToInteger64 =
    1014         841 :         CPLTestBool(CPLGetConfigOption("OGR_PROMOTE_TO_INTEGER64", "FALSE"));
    1015             : 
    1016             : #ifdef SQLITE_HAS_COLUMN_METADATA
    1017             :     // Check that there are not several FID fields referenced.
    1018             :     // This is not a sufficient condition to ensure that we can get a true FID,
    1019             :     // but when this occurs, we are (almost) sure that this cannot be a FID.
    1020         841 :     int nFIDCandidates = 0;
    1021        3226 :     for (int iCol = 0; iCol < nRawColumns; iCol++)
    1022             :     {
    1023        2385 :         const char *pszTableName = sqlite3_column_table_name(hStmt, iCol);
    1024        2385 :         const char *pszOriginName = sqlite3_column_origin_name(hStmt, iCol);
    1025        2385 :         if (pszTableName != nullptr && pszOriginName != nullptr)
    1026             :         {
    1027        1730 :             OGRLayer *poLayer = m_poDS->GetLayerByName(pszTableName);
    1028        1730 :             if (poLayer != nullptr)
    1029             :             {
    1030         265 :                 if (EQUAL(pszOriginName, poLayer->GetFIDColumn()))
    1031             :                 {
    1032          49 :                     nFIDCandidates++;
    1033             :                 }
    1034             :             }
    1035             :         }
    1036             :     }
    1037             : #endif
    1038             : 
    1039         841 :     [[maybe_unused]] bool bGeometryColumnGuessed = false;
    1040        3226 :     for (int iCol = 0; iCol < nRawColumns; iCol++)
    1041             :     {
    1042        2385 :         OGRFieldDefn oField(SQLUnescape(sqlite3_column_name(hStmt, iCol)),
    1043        2385 :                             OFTString);
    1044             : 
    1045             :         // In some cases, particularly when there is a real name for
    1046             :         // the primary key/_rowid_ column we will end up getting the
    1047             :         // primary key column appearing twice.  Ignore any repeated names.
    1048        2385 :         if (m_poFeatureDefn->GetFieldIndex(oField.GetNameRef()) != -1)
    1049           3 :             continue;
    1050             : 
    1051        2596 :         if (m_pszFidColumn != nullptr &&
    1052         214 :             EQUAL(m_pszFidColumn, oField.GetNameRef()))
    1053           3 :             continue;
    1054             : 
    1055             :         // The rowid is for internal use, not a real column.
    1056        2379 :         if (EQUAL(oField.GetNameRef(), "_rowid_"))
    1057           0 :             continue;
    1058             : 
    1059             :         // this will avoid the old geom field to appear when running something
    1060             :         // like "select st_buffer(geom,5) as geom, * from my_layer"
    1061        2549 :         if (m_poFeatureDefn->GetGeomFieldCount() &&
    1062         170 :             EQUAL(oField.GetNameRef(),
    1063             :                   m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()))
    1064             :         {
    1065           3 :             continue;
    1066             :         }
    1067             : 
    1068             : #ifdef SQLITE_HAS_COLUMN_METADATA
    1069        2376 :         const char *pszTableName = sqlite3_column_table_name(hStmt, iCol);
    1070        2376 :         const char *pszOriginName = sqlite3_column_origin_name(hStmt, iCol);
    1071        2376 :         if (pszTableName != nullptr && pszOriginName != nullptr)
    1072             :         {
    1073        1724 :             OGRLayer *poLayer = m_poDS->GetLayerByName(pszTableName);
    1074        1724 :             if (poLayer != nullptr)
    1075             :             {
    1076         259 :                 if (EQUAL(pszOriginName, poLayer->GetGeometryColumn()))
    1077             :                 {
    1078          86 :                     if (bGeometryColumnGuessed ||
    1079          43 :                         m_poFeatureDefn->GetGeomFieldCount() == 0)
    1080             :                     {
    1081          42 :                         if (bGeometryColumnGuessed)
    1082           0 :                             m_poFeatureDefn->DeleteGeomFieldDefn(0);
    1083             :                         OGRGeomFieldDefn oGeomField(
    1084          42 :                             poLayer->GetLayerDefn()->GetGeomFieldDefn(0));
    1085          42 :                         oGeomField.SetName(oField.GetNameRef());
    1086          42 :                         m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    1087          42 :                         m_iGeomCol = iCol;
    1088             :                     }
    1089          43 :                     continue;
    1090             :                 }
    1091         216 :                 else if (EQUAL(pszOriginName, poLayer->GetFIDColumn()) &&
    1092         216 :                          m_pszFidColumn == nullptr && nFIDCandidates == 1)
    1093             :                 {
    1094          44 :                     m_pszFidColumn = CPLStrdup(oField.GetNameRef());
    1095          44 :                     m_iFIDCol = iCol;
    1096          44 :                     continue;
    1097             :                 }
    1098             :                 int nSrcIdx =
    1099         172 :                     poLayer->GetLayerDefn()->GetFieldIndex(oField.GetNameRef());
    1100         172 :                 if (nSrcIdx >= 0)
    1101             :                 {
    1102             :                     OGRFieldDefn *poSrcField =
    1103         169 :                         poLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
    1104         169 :                     oField.SetType(poSrcField->GetType());
    1105         169 :                     oField.SetSubType(poSrcField->GetSubType());
    1106         169 :                     oField.SetWidth(poSrcField->GetWidth());
    1107         169 :                     oField.SetPrecision(poSrcField->GetPrecision());
    1108         169 :                     oField.SetDomainName(poSrcField->GetDomainName());
    1109         169 :                     m_poFeatureDefn->AddFieldDefn(&oField);
    1110         169 :                     m_anFieldOrdinals[m_poFeatureDefn->GetFieldCount() - 1] =
    1111             :                         iCol;
    1112         169 :                     continue;
    1113             :                 }
    1114             :             }
    1115             :         }
    1116             : #endif
    1117             : 
    1118        2120 :         const int nColType = sqlite3_column_type(hStmt, iCol);
    1119        2120 :         if (m_poFeatureDefn->GetGeomFieldCount() == 0 &&
    1120        2599 :             m_pszFidColumn == nullptr && nColType == SQLITE_INTEGER &&
    1121         479 :             EQUAL(oField.GetNameRef(), "FID"))
    1122             :         {
    1123           2 :             m_pszFidColumn = CPLStrdup(oField.GetNameRef());
    1124           2 :             m_iFIDCol = iCol;
    1125           2 :             continue;
    1126             :         }
    1127             : 
    1128             :         // Heuristics to help for https://github.com/OSGeo/gdal/issues/8587
    1129        2118 :         if (nColType == SQLITE_NULL && m_iGeomCol < 0
    1130             : #ifdef SQLITE_HAS_COLUMN_METADATA
    1131         631 :             && !pszTableName && !pszOriginName
    1132             : #endif
    1133             :         )
    1134             :         {
    1135         288 :             bool bIsLikelyGeomColName = EQUAL(oField.GetNameRef(), "geom") ||
    1136         143 :                                         EQUAL(oField.GetNameRef(), "geometry");
    1137         145 :             bool bIsGeomFunction = false;
    1138         145 :             if (!bIsLikelyGeomColName)
    1139         143 :                 bIsGeomFunction = OGRSQLiteIsSpatialFunctionReturningGeometry(
    1140             :                     oField.GetNameRef());
    1141         145 :             if (bIsLikelyGeomColName || bIsGeomFunction)
    1142             :             {
    1143          10 :                 bGeometryColumnGuessed = bIsLikelyGeomColName;
    1144          20 :                 OGRGeomFieldDefn oGeomField(oField.GetNameRef(), wkbUnknown);
    1145          10 :                 m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    1146          10 :                 m_iGeomCol = iCol;
    1147          10 :                 continue;
    1148             :             }
    1149             :         }
    1150             : 
    1151        2108 :         const char *pszDeclType = sqlite3_column_decltype(hStmt, iCol);
    1152             : 
    1153             :         // Recognize a geometry column from trying to build the geometry
    1154        2167 :         if (nColType == SQLITE_BLOB &&
    1155          59 :             m_poFeatureDefn->GetGeomFieldCount() == 0)
    1156             :         {
    1157          58 :             const int nBytes = sqlite3_column_bytes(hStmt, iCol);
    1158          58 :             if (nBytes >= 8)
    1159             :             {
    1160             :                 // coverity[tainted_data_return]
    1161             :                 const GByte *pabyGpkg = reinterpret_cast<const GByte *>(
    1162          58 :                     sqlite3_column_blob(hStmt, iCol));
    1163             :                 GPkgHeader oHeader;
    1164          58 :                 OGRGeometry *poGeom = nullptr;
    1165          58 :                 int nSRID = 0;
    1166             : 
    1167          58 :                 if (GPkgHeaderFromWKB(pabyGpkg, nBytes, &oHeader) ==
    1168             :                     OGRERR_NONE)
    1169             :                 {
    1170           9 :                     poGeom = GPkgGeometryToOGR(pabyGpkg, nBytes, nullptr);
    1171           9 :                     nSRID = oHeader.iSrsId;
    1172             :                 }
    1173             :                 else
    1174             :                 {
    1175             :                     // Try also spatialite geometry blobs
    1176          49 :                     if (OGRSQLiteImportSpatiaLiteGeometry(
    1177          49 :                             pabyGpkg, nBytes, &poGeom, &nSRID) != OGRERR_NONE)
    1178             :                     {
    1179          48 :                         delete poGeom;
    1180          48 :                         poGeom = nullptr;
    1181             :                     }
    1182             :                 }
    1183             : 
    1184          58 :                 if (poGeom)
    1185             :                 {
    1186             :                     OGRGeomFieldDefn oGeomField(oField.GetNameRef(),
    1187          20 :                                                 wkbUnknown);
    1188             : 
    1189             :                     /* Read the SRS */
    1190          20 :                     auto poSRS = m_poDS->GetSpatialRef(nSRID, true);
    1191          10 :                     if (poSRS)
    1192             :                     {
    1193           7 :                         oGeomField.SetSpatialRef(poSRS.get());
    1194             :                     }
    1195             : 
    1196          10 :                     OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
    1197          10 :                     if (pszDeclType != nullptr)
    1198             :                     {
    1199             :                         OGRwkbGeometryType eDeclaredGeomType =
    1200           0 :                             GPkgGeometryTypeToWKB(pszDeclType, false, false);
    1201           0 :                         if (eDeclaredGeomType != wkbUnknown)
    1202             :                         {
    1203           0 :                             eGeomType = OGR_GT_SetModifier(
    1204             :                                 eDeclaredGeomType, OGR_GT_HasZ(eGeomType),
    1205             :                                 OGR_GT_HasM(eGeomType));
    1206             :                         }
    1207             :                     }
    1208          10 :                     oGeomField.SetType(eGeomType);
    1209             : 
    1210          10 :                     delete poGeom;
    1211          10 :                     poGeom = nullptr;
    1212             : 
    1213          10 :                     m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    1214          10 :                     m_iGeomCol = iCol;
    1215          10 :                     continue;
    1216             :                 }
    1217             :             }
    1218             :         }
    1219             : 
    1220        2098 :         switch (nColType)
    1221             :         {
    1222         481 :             case SQLITE_INTEGER:
    1223         481 :                 if (bPromoteToInteger64)
    1224           0 :                     oField.SetType(OFTInteger64);
    1225             :                 else
    1226             :                 {
    1227         481 :                     GIntBig nVal = sqlite3_column_int64(hStmt, iCol);
    1228         481 :                     if (CPL_INT64_FITS_ON_INT32(nVal))
    1229         476 :                         oField.SetType(OFTInteger);
    1230             :                     else
    1231           5 :                         oField.SetType(OFTInteger64);
    1232             :                 }
    1233         481 :                 break;
    1234             : 
    1235         382 :             case SQLITE_FLOAT:
    1236         382 :                 oField.SetType(OFTReal);
    1237         382 :                 break;
    1238             : 
    1239          49 :             case SQLITE_BLOB:
    1240          49 :                 oField.SetType(OFTBinary);
    1241          49 :                 break;
    1242             : 
    1243        2098 :             default:
    1244             :                 /* leave it as OFTString */;
    1245             :         }
    1246             : 
    1247        2098 :         if (pszDeclType != nullptr)
    1248             :         {
    1249             :             OGRFieldSubType eSubType;
    1250        1420 :             int nMaxWidth = 0;
    1251             :             const int nFieldType =
    1252        1420 :                 GPkgFieldToOGR(pszDeclType, eSubType, nMaxWidth);
    1253        1420 :             if (nFieldType <= OFTMaxType)
    1254             :             {
    1255        1420 :                 oField.SetType(static_cast<OGRFieldType>(nFieldType));
    1256        1420 :                 oField.SetSubType(eSubType);
    1257        1420 :                 oField.SetWidth(nMaxWidth);
    1258             :             }
    1259             :         }
    1260             : 
    1261        2098 :         m_poFeatureDefn->AddFieldDefn(&oField);
    1262        2098 :         m_anFieldOrdinals[m_poFeatureDefn->GetFieldCount() - 1] = iCol;
    1263             :     }
    1264         841 : }
    1265             : 
    1266             : /************************************************************************/
    1267             : /*                          SetIgnoredFields()                          */
    1268             : /************************************************************************/
    1269             : 
    1270          22 : OGRErr OGRGeoPackageLayer::SetIgnoredFields(CSLConstList papszFields)
    1271             : {
    1272          22 :     OGRErr eErr = OGRLayer::SetIgnoredFields(papszFields);
    1273          22 :     if (eErr == OGRERR_NONE)
    1274             :     {
    1275             :         // So that OGRGeoPackageTableLayer::BuildColumns() is called
    1276          22 :         ResetReading();
    1277             :     }
    1278          22 :     return eErr;
    1279             : }

Generated by: LCOV version 1.14