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

Generated by: LCOV version 1.14