LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagetablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3799 4201 90.4 %
Date: 2025-09-10 17:48:50 Functions: 134 134 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoPackage Translator
       4             :  * Purpose:  Implements OGRGeoPackageTableLayer 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 "ogrlayerarrow.h"
      17             : #include "ogrsqliteutility.h"
      18             : #include "cpl_md5.h"
      19             : #include "cpl_time.h"
      20             : #include "ogr_p.h"
      21             : #include "sqlite_rtree_bulk_load/wrapper.h"
      22             : #include "gdal_priv_templates.hpp"
      23             : 
      24             : #include <algorithm>
      25             : #include <cassert>
      26             : #include <cerrno>
      27             : #include <cinttypes>
      28             : #include <climits>
      29             : #include <cmath>
      30             : #include <limits>
      31             : #include <mutex>
      32             : 
      33             : #undef SQLITE_STATIC
      34             : #define SQLITE_STATIC static_cast<sqlite3_destructor_type>(nullptr)
      35             : #undef SQLITE_TRANSIENT
      36             : #define SQLITE_TRANSIENT reinterpret_cast<sqlite3_destructor_type>(-1)
      37             : 
      38             : static const char UNSUPPORTED_OP_READ_ONLY[] =
      39             :     "%s : unsupported operation on a read-only datasource.";
      40             : 
      41             : //----------------------------------------------------------------------
      42             : // SaveExtent()
      43             : //
      44             : // Write the current contents of the layer envelope down to the
      45             : // gpkg_contents metadata table.
      46             : //
      47        5044 : OGRErr OGRGeoPackageTableLayer::SaveExtent()
      48             : {
      49        5044 :     if (!m_poDS->GetUpdate() || !m_bExtentChanged || !m_poExtent)
      50        4521 :         return OGRERR_NONE;
      51             : 
      52         523 :     sqlite3 *poDb = m_poDS->GetDB();
      53             : 
      54         523 :     if (!poDb)
      55           0 :         return OGRERR_FAILURE;
      56             : 
      57             :     char *pszSQL =
      58         523 :         sqlite3_mprintf("UPDATE gpkg_contents SET "
      59             :                         "min_x = %.17g, min_y = %.17g, "
      60             :                         "max_x = %.17g, max_y = %.17g "
      61             :                         "WHERE lower(table_name) = lower('%q') AND "
      62             :                         "Lower(data_type) = 'features'",
      63         523 :                         m_poExtent->MinX, m_poExtent->MinY, m_poExtent->MaxX,
      64         523 :                         m_poExtent->MaxY, m_pszTableName);
      65             : 
      66         523 :     OGRErr err = SQLCommand(poDb, pszSQL);
      67         523 :     sqlite3_free(pszSQL);
      68         523 :     m_bExtentChanged = false;
      69             : 
      70         523 :     return err;
      71             : }
      72             : 
      73             : //----------------------------------------------------------------------
      74             : // SaveTimestamp()
      75             : //
      76             : // Update the last_change column of the gpkg_contents metadata table.
      77             : //
      78        5039 : OGRErr OGRGeoPackageTableLayer::SaveTimestamp()
      79             : {
      80        5039 :     if (!m_poDS->GetUpdate() || !m_bContentChanged)
      81        4325 :         return OGRERR_NONE;
      82             : 
      83         714 :     m_bContentChanged = false;
      84             : 
      85         714 :     OGRErr err = m_poDS->UpdateGpkgContentsLastChange(m_pszTableName);
      86             : 
      87             : #ifdef ENABLE_GPKG_OGR_CONTENTS
      88         714 :     if (m_bIsTable && err == OGRERR_NONE && m_poDS->m_bHasGPKGOGRContents &&
      89         707 :         !m_bOGRFeatureCountTriggersEnabled && m_nTotalFeatureCount >= 0)
      90             :     {
      91        1316 :         CPLString osFeatureCount;
      92         658 :         osFeatureCount.Printf(CPL_FRMT_GIB, m_nTotalFeatureCount);
      93         658 :         char *pszSQL = sqlite3_mprintf("UPDATE gpkg_ogr_contents SET "
      94             :                                        "feature_count = %s "
      95             :                                        "WHERE lower(table_name) = lower('%q')",
      96             :                                        osFeatureCount.c_str(), m_pszTableName);
      97         658 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
      98         658 :         sqlite3_free(pszSQL);
      99             :     }
     100             : #endif
     101             : 
     102         714 :     return err;
     103             : }
     104             : 
     105             : //----------------------------------------------------------------------
     106             : // UpdateExtent()
     107             : //
     108             : // Expand the layer envelope if necessary to reflect the bounds
     109             : // of new features being added to the layer.
     110             : //
     111      260351 : OGRErr OGRGeoPackageTableLayer::UpdateExtent(const OGREnvelope *poExtent)
     112             : {
     113      260351 :     if (!m_poExtent)
     114             :     {
     115         485 :         m_poExtent = std::make_unique<OGREnvelope>(*poExtent);
     116             :     }
     117      260351 :     m_poExtent->Merge(*poExtent);
     118      260351 :     m_bExtentChanged = true;
     119      260351 :     return OGRERR_NONE;
     120             : }
     121             : 
     122             : //----------------------------------------------------------------------
     123             : // BuildColumns()
     124             : //
     125             : // Save a list of columns (fid, geometry, attributes) suitable
     126             : // for use in a SELECT query that retrieves all fields.
     127             : //
     128       26161 : OGRErr OGRGeoPackageTableLayer::BuildColumns()
     129             : {
     130       26161 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
     131           0 :         return OGRERR_FAILURE;
     132             : 
     133       26161 :     if (!m_bFeatureDefnCompleted)
     134          25 :         GetLayerDefn();
     135             : 
     136       26161 :     m_anFieldOrdinals.resize(m_poFeatureDefn->GetFieldCount());
     137       26161 :     int iCurCol = 0;
     138             : 
     139             :     /* Always start with a primary key */
     140       26161 :     CPLString soColumns;
     141       26161 :     if (m_bIsTable || m_pszFidColumn != nullptr)
     142             :     {
     143       26147 :         soColumns += "m.";
     144       26147 :         soColumns += m_pszFidColumn
     145       52294 :                          ? "\"" + SQLEscapeName(m_pszFidColumn) + "\""
     146       26147 :                          : "_rowid_";
     147       26147 :         m_iFIDCol = iCurCol;
     148       26147 :         iCurCol++;
     149             :     }
     150             : 
     151             :     /* Add a geometry column if there is one (just one) */
     152       26161 :     if (m_poFeatureDefn->GetGeomFieldCount())
     153             :     {
     154       25957 :         const auto poFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
     155       25957 :         if (poFieldDefn->IsIgnored())
     156             :         {
     157          10 :             m_iGeomCol = -1;
     158             :         }
     159             :         else
     160             :         {
     161       25947 :             if (!soColumns.empty())
     162       25936 :                 soColumns += ", ";
     163       25947 :             soColumns += "m.\"";
     164       25947 :             soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
     165       25947 :             soColumns += "\"";
     166       25947 :             m_iGeomCol = iCurCol;
     167       25947 :             iCurCol++;
     168             :         }
     169             :     }
     170             : 
     171             :     /* Add all the attribute columns */
     172       37349 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     173             :     {
     174       11188 :         const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
     175       11188 :         if (poFieldDefn->IsIgnored())
     176             :         {
     177          28 :             m_anFieldOrdinals[i] = -1;
     178             :         }
     179             :         else
     180             :         {
     181       11160 :             if (!soColumns.empty())
     182       11159 :                 soColumns += ", ";
     183       11160 :             soColumns += "m.\"";
     184       11160 :             soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
     185       11160 :             soColumns += "\"";
     186       11160 :             m_anFieldOrdinals[i] = iCurCol;
     187       11160 :             iCurCol++;
     188             :         }
     189             :     }
     190             : 
     191       26161 :     if (soColumns.empty())
     192             :     {
     193             :         // Can happen if ignoring all fields on a view...
     194           2 :         soColumns = "NULL";
     195             :     }
     196       26161 :     m_soColumns = std::move(soColumns);
     197       26161 :     return OGRERR_NONE;
     198             : }
     199             : 
     200             : //----------------------------------------------------------------------
     201             : // IsGeomFieldSet()
     202             : //
     203             : // Utility method to determine if there is a non-Null geometry
     204             : // in an OGRGeometry.
     205             : //
     206      266527 : bool OGRGeoPackageTableLayer::IsGeomFieldSet(OGRFeature *poFeature)
     207             : {
     208      529280 :     return poFeature->GetDefnRef()->GetGeomFieldCount() &&
     209      529280 :            poFeature->GetGeomFieldRef(0);
     210             : }
     211             : 
     212      266549 : OGRErr OGRGeoPackageTableLayer::FeatureBindParameters(
     213             :     OGRFeature *poFeature, sqlite3_stmt *poStmt, int *pnColCount, bool bAddFID,
     214             :     bool bBindUnsetFields, int nUpdatedFieldsCount,
     215             :     const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
     216             :     const int * /*panUpdatedGeomFieldsIdx*/)
     217             : {
     218      266549 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     219             : 
     220      266549 :     int nColCount = 1;
     221      266549 :     if (bAddFID)
     222             :     {
     223       67378 :         int err = sqlite3_bind_int64(poStmt, nColCount++, poFeature->GetFID());
     224       67378 :         if (err != SQLITE_OK)
     225             :         {
     226           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     227             :                      "sqlite3_bind_int64() failed");
     228           0 :             return OGRERR_FAILURE;
     229             :         }
     230             :     }
     231             : 
     232             :     // Bind data values to the statement, here bind the blob for geometry.
     233             :     // We bind only if there's a geometry column (poFeatureDefn->GetGeomFieldCount() > 0)
     234             :     // and if we are:
     235             :     // - either in CreateFeature/SetFeature mode: nUpdatedGeomFieldsCount < 0
     236             :     // - or in UpdateFeature mode with nUpdatedGeomFieldsCount == 1, which
     237             :     //   implicitly involves that panUpdatedGeomFieldsIdx[0] == 0, so we don't
     238             :     //   need to test this condition.
     239      533090 :     if ((nUpdatedGeomFieldsCount < 0 || nUpdatedGeomFieldsCount == 1) &&
     240      266541 :         poFeatureDefn->GetGeomFieldCount())
     241             :     {
     242             :         // Non-NULL geometry.
     243      262765 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
     244      262765 :         if (poGeom)
     245             :         {
     246      261383 :             size_t szWkb = 0;
     247      522766 :             GByte *pabyWkb = GPkgGeometryFromOGR(poGeom, m_iSrs,
     248      261383 :                                                  &m_sBinaryPrecision, &szWkb);
     249      261383 :             if (!pabyWkb)
     250           0 :                 return OGRERR_FAILURE;
     251      261383 :             int err = sqlite3_bind_blob(poStmt, nColCount++, pabyWkb,
     252             :                                         static_cast<int>(szWkb), CPLFree);
     253      261383 :             if (err != SQLITE_OK)
     254             :             {
     255           0 :                 if (err == SQLITE_TOOBIG)
     256             :                 {
     257           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     258             :                              "sqlite3_bind_blob() failed: too big");
     259             :                 }
     260             :                 else
     261             :                 {
     262           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     263             :                              "sqlite3_bind_blob() failed");
     264             :                 }
     265           0 :                 return OGRERR_FAILURE;
     266             :             }
     267      261383 :             CreateGeometryExtensionIfNecessary(poGeom);
     268             :         }
     269             :         /* NULL geometry */
     270             :         else
     271             :         {
     272        1382 :             int err = sqlite3_bind_null(poStmt, nColCount++);
     273        1382 :             if (err != SQLITE_OK)
     274             :             {
     275           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     276             :                          "sqlite3_bind_null() failed");
     277           0 :                 return OGRERR_FAILURE;
     278             :             }
     279             :         }
     280             :     }
     281             : 
     282             :     /* Bind the attributes using appropriate SQLite data types */
     283      266549 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
     284             : 
     285      266549 :     size_t nInsertionBufferPos = 0;
     286      266549 :     if (m_osInsertionBuffer.empty())
     287       28994 :         m_osInsertionBuffer.resize(OGR_SIZEOF_ISO8601_DATETIME_BUFFER *
     288             :                                    nFieldCount);
     289             : 
     290     4734690 :     for (int idx = 0;
     291     4734690 :          idx < (nUpdatedFieldsCount < 0 ? nFieldCount : nUpdatedFieldsCount);
     292             :          idx++)
     293             :     {
     294     4468140 :         const int iField =
     295     4468140 :             nUpdatedFieldsCount < 0 ? idx : panUpdatedFieldsIdx[idx];
     296     4468140 :         assert(iField >= 0);
     297     4468140 :         const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(iField);
     298     4468140 :         if (iField == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
     299          27 :             continue;
     300     4468120 :         if (!poFeature->IsFieldSetUnsafe(iField))
     301             :         {
     302        1240 :             if (bBindUnsetFields)
     303             :             {
     304        1226 :                 int err = sqlite3_bind_null(poStmt, nColCount++);
     305        1226 :                 if (err != SQLITE_OK)
     306             :                 {
     307           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     308             :                              "sqlite3_bind_null() failed");
     309           0 :                     return OGRERR_FAILURE;
     310             :                 }
     311             :             }
     312        1240 :             continue;
     313             :         }
     314             : 
     315             :         const OGRFieldDefn *poFieldDefn =
     316     4466880 :             poFeatureDefn->GetFieldDefnUnsafe(iField);
     317     4466880 :         int err = SQLITE_OK;
     318             : 
     319     4466880 :         if (!poFeature->IsFieldNullUnsafe(iField))
     320             :         {
     321     4466840 :             const auto eType = poFieldDefn->GetType();
     322     4466840 :             switch (eType)
     323             :             {
     324        8617 :                 case OFTInteger:
     325             :                 {
     326        8617 :                     err = sqlite3_bind_int(
     327             :                         poStmt, nColCount++,
     328             :                         poFeature->GetFieldAsIntegerUnsafe(iField));
     329        8617 :                     break;
     330             :                 }
     331        5766 :                 case OFTInteger64:
     332             :                 {
     333        5766 :                     err = sqlite3_bind_int64(
     334             :                         poStmt, nColCount++,
     335             :                         poFeature->GetFieldAsInteger64Unsafe(iField));
     336        5766 :                     break;
     337             :                 }
     338       22961 :                 case OFTReal:
     339             :                 {
     340       22961 :                     err = sqlite3_bind_double(
     341             :                         poStmt, nColCount++,
     342             :                         poFeature->GetFieldAsDoubleUnsafe(iField));
     343       22961 :                     break;
     344             :                 }
     345        7311 :                 case OFTBinary:
     346             :                 {
     347        7311 :                     int szBlob = 0;
     348             :                     GByte *pabyBlob =
     349        7311 :                         poFeature->GetFieldAsBinary(iField, &szBlob);
     350        7311 :                     err = sqlite3_bind_blob(poStmt, nColCount++, pabyBlob,
     351             :                                             szBlob, SQLITE_STATIC);
     352        7311 :                     break;
     353             :                 }
     354     4422190 :                 default:
     355             :                 {
     356     4422190 :                     const char *pszVal = "";
     357     4422190 :                     CPL_IGNORE_RET_VAL(pszVal);  // Make CSA happy
     358     4422190 :                     int nValLengthBytes = -1;
     359     4422190 :                     sqlite3_destructor_type destructorType = SQLITE_TRANSIENT;
     360     4422190 :                     if (eType == OFTDate)
     361             :                     {
     362        5141 :                         destructorType = SQLITE_STATIC;
     363             :                         const auto psFieldRaw =
     364        5141 :                             poFeature->GetRawFieldRef(iField);
     365             :                         char *pszValEdit =
     366        5141 :                             &m_osInsertionBuffer[nInsertionBufferPos];
     367        5141 :                         pszVal = pszValEdit;
     368        5141 :                         if (psFieldRaw->Date.Year < 0 ||
     369        5141 :                             psFieldRaw->Date.Year >= 10000)
     370             :                         {
     371           0 :                             CPLError(
     372             :                                 CE_Failure, CPLE_AppDefined,
     373             :                                 "OGRGetISO8601DateTime(): year %d unsupported ",
     374           0 :                                 psFieldRaw->Date.Year);
     375           0 :                             nValLengthBytes = 0;
     376             :                         }
     377             :                         else
     378             :                         {
     379        5141 :                             int nYear = psFieldRaw->Date.Year;
     380        5141 :                             pszValEdit[3] = (nYear % 10) + '0';
     381        5141 :                             nYear /= 10;
     382        5141 :                             pszValEdit[2] = (nYear % 10) + '0';
     383        5141 :                             nYear /= 10;
     384        5141 :                             pszValEdit[1] = (nYear % 10) + '0';
     385        5141 :                             nYear /= 10;
     386        5141 :                             pszValEdit[0] =
     387        5141 :                                 static_cast<char>(nYear /*% 10*/ + '0');
     388        5141 :                             pszValEdit[4] = '-';
     389        5141 :                             pszValEdit[5] =
     390        5141 :                                 ((psFieldRaw->Date.Month / 10) % 10) + '0';
     391        5141 :                             pszValEdit[6] = (psFieldRaw->Date.Month % 10) + '0';
     392        5141 :                             pszValEdit[7] = '-';
     393        5141 :                             pszValEdit[8] =
     394        5141 :                                 ((psFieldRaw->Date.Day / 10) % 10) + '0';
     395        5141 :                             pszValEdit[9] = (psFieldRaw->Date.Day % 10) + '0';
     396        5141 :                             nValLengthBytes = 10;
     397        5141 :                             nInsertionBufferPos += 10;
     398             :                         }
     399             :                     }
     400     4417050 :                     else if (eType == OFTDateTime)
     401             :                     {
     402        5169 :                         destructorType = SQLITE_STATIC;
     403             :                         const auto psFieldRaw =
     404        5169 :                             poFeature->GetRawFieldRef(iField);
     405             :                         char *pszValEdit =
     406        5169 :                             &m_osInsertionBuffer[nInsertionBufferPos];
     407        5169 :                         pszVal = pszValEdit;
     408        5169 :                         if (m_poDS->m_bDateTimeWithTZ ||
     409           6 :                             psFieldRaw->Date.TZFlag == 100)
     410             :                         {
     411        5165 :                             nValLengthBytes = OGRGetISO8601DateTime(
     412        5165 :                                 psFieldRaw, m_sDateTimeFormat, pszValEdit);
     413             :                         }
     414             :                         else
     415             :                         {
     416           4 :                             OGRField sField(*psFieldRaw);
     417           4 :                             if (sField.Date.TZFlag == 0 ||
     418           2 :                                 sField.Date.TZFlag == 1)
     419             :                             {
     420           2 :                                 sField.Date.TZFlag = 100;
     421             :                             }
     422             :                             else
     423             :                             {
     424             :                                 struct tm brokendowntime;
     425           2 :                                 brokendowntime.tm_year =
     426           2 :                                     sField.Date.Year - 1900;
     427           2 :                                 brokendowntime.tm_mon = sField.Date.Month - 1;
     428           2 :                                 brokendowntime.tm_mday = sField.Date.Day;
     429           2 :                                 brokendowntime.tm_hour = sField.Date.Hour;
     430           2 :                                 brokendowntime.tm_min = sField.Date.Minute;
     431           2 :                                 brokendowntime.tm_sec = 0;
     432             :                                 GIntBig nDT =
     433           2 :                                     CPLYMDHMSToUnixTime(&brokendowntime);
     434           2 :                                 const int TZOffset =
     435           2 :                                     std::abs(sField.Date.TZFlag - 100) * 15;
     436           2 :                                 nDT -= TZOffset * 60;
     437           2 :                                 CPLUnixTimeToYMDHMS(nDT, &brokendowntime);
     438           2 :                                 sField.Date.Year = static_cast<GInt16>(
     439           2 :                                     brokendowntime.tm_year + 1900);
     440           2 :                                 sField.Date.Month = static_cast<GByte>(
     441           2 :                                     brokendowntime.tm_mon + 1);
     442           2 :                                 sField.Date.Day =
     443           2 :                                     static_cast<GByte>(brokendowntime.tm_mday);
     444           2 :                                 sField.Date.Hour =
     445           2 :                                     static_cast<GByte>(brokendowntime.tm_hour);
     446           2 :                                 sField.Date.Minute =
     447           2 :                                     static_cast<GByte>(brokendowntime.tm_min);
     448           2 :                                 sField.Date.TZFlag = 100;
     449             :                             }
     450             : 
     451           4 :                             nValLengthBytes = OGRGetISO8601DateTime(
     452           4 :                                 &sField, m_sDateTimeFormat, pszValEdit);
     453             :                         }
     454        5169 :                         nInsertionBufferPos += nValLengthBytes;
     455             :                     }
     456     4411880 :                     else if (eType == OFTString)
     457             :                     {
     458     4406880 :                         pszVal = poFeature->GetFieldAsStringUnsafe(iField);
     459     4406880 :                         if (poFieldDefn->GetWidth() > 0)
     460             :                         {
     461         669 :                             if (!CPLIsUTF8(pszVal, -1))
     462             :                             {
     463           4 :                                 CPLError(CE_Warning, CPLE_AppDefined,
     464             :                                          "Value of field '%s' is not a valid "
     465             :                                          "UTF-8 string.%s",
     466           2 :                                          poFeatureDefn->GetFieldDefn(iField)
     467             :                                              ->GetNameRef(),
     468           2 :                                          m_bTruncateFields
     469             :                                              ? " Value will be laundered."
     470             :                                              : "");
     471           2 :                                 if (m_bTruncateFields)
     472             :                                 {
     473           1 :                                     pszVal = CPLForceToASCII(pszVal, -1, '_');
     474           1 :                                     destructorType = CPLFree;
     475             :                                 }
     476             :                             }
     477             : 
     478         669 :                             if (CPLStrlenUTF8Ex(pszVal) >
     479         669 :                                 static_cast<size_t>(poFieldDefn->GetWidth()))
     480             :                             {
     481           4 :                                 CPLError(CE_Warning, CPLE_AppDefined,
     482             :                                          "Value of field '%s' has %" PRIu64
     483             :                                          " characters, "
     484             :                                          "whereas maximum allowed is %d.%s",
     485           2 :                                          poFeatureDefn->GetFieldDefn(iField)
     486             :                                              ->GetNameRef(),
     487             :                                          static_cast<uint64_t>(
     488           2 :                                              CPLStrlenUTF8Ex(pszVal)),
     489             :                                          poFieldDefn->GetWidth(),
     490           2 :                                          m_bTruncateFields
     491             :                                              ? " Value will be truncated."
     492             :                                              : "");
     493           2 :                                 if (m_bTruncateFields)
     494             :                                 {
     495           1 :                                     size_t countUTF8Chars = 0;
     496           1 :                                     nValLengthBytes = 0;
     497           3 :                                     while (pszVal[nValLengthBytes])
     498             :                                     {
     499           3 :                                         if ((pszVal[nValLengthBytes] & 0xc0) !=
     500             :                                             0x80)
     501             :                                         {
     502             :                                             // Stop at the start of the
     503             :                                             // character just beyond the maximum
     504             :                                             // accepted
     505           3 :                                             if (countUTF8Chars ==
     506           3 :                                                 static_cast<size_t>(
     507           3 :                                                     poFieldDefn->GetWidth()))
     508             :                                             {
     509           1 :                                                 break;
     510             :                                             }
     511           2 :                                             countUTF8Chars++;
     512             :                                         }
     513           2 :                                         nValLengthBytes++;
     514             :                                     }
     515             :                                 }
     516             :                             }
     517             :                         }
     518             :                         else
     519             :                         {
     520     4406210 :                             destructorType = SQLITE_STATIC;
     521             :                         }
     522             :                     }
     523             :                     else
     524             :                     {
     525        5000 :                         pszVal = poFeature->GetFieldAsString(iField);
     526             :                     }
     527             : 
     528     4422190 :                     err = sqlite3_bind_text(poStmt, nColCount++, pszVal,
     529             :                                             nValLengthBytes, destructorType);
     530     4422190 :                     break;
     531             :                 }
     532             :             }
     533             :         }
     534             :         else
     535             :         {
     536          36 :             err = sqlite3_bind_null(poStmt, nColCount++);
     537             :         }
     538     4466880 :         if (err != SQLITE_OK)
     539             :         {
     540           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     541             :                      "sqlite3_bind_() for column %s failed: %s",
     542             :                      poFieldDefn->GetNameRef(),
     543           0 :                      sqlite3_errmsg(m_poDS->GetDB()));
     544           0 :             return OGRERR_FAILURE;
     545             :         }
     546             :     }
     547             : 
     548      266549 :     if (pnColCount != nullptr)
     549          65 :         *pnColCount = nColCount;
     550      266549 :     return OGRERR_NONE;
     551             : }
     552             : 
     553             : //----------------------------------------------------------------------
     554             : // FeatureBindUpdateParameters()
     555             : //
     556             : // Selectively bind the values of an OGRFeature to a prepared
     557             : // statement, prior to execution. Carefully binds exactly the
     558             : // same parameters that have been set up by FeatureGenerateUpdateSQL()
     559             : // as bindable.
     560             : //
     561             : OGRErr
     562          55 : OGRGeoPackageTableLayer::FeatureBindUpdateParameters(OGRFeature *poFeature,
     563             :                                                      sqlite3_stmt *poStmt)
     564             : {
     565             : 
     566          55 :     int nColCount = 0;
     567          55 :     const OGRErr err = FeatureBindParameters(
     568             :         poFeature, poStmt, &nColCount, false, false, -1, nullptr, -1, nullptr);
     569          55 :     if (err != OGRERR_NONE)
     570           0 :         return err;
     571             : 
     572             :     // Bind the FID to the "WHERE" clause.
     573             :     const int sqlite_err =
     574          55 :         sqlite3_bind_int64(poStmt, nColCount, poFeature->GetFID());
     575          55 :     if (sqlite_err != SQLITE_OK)
     576             :     {
     577           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     578             :                  "failed to bind FID '" CPL_FRMT_GIB "' to statement: %s",
     579           0 :                  poFeature->GetFID(), sqlite3_errmsg(m_poDS->GetDB()));
     580           0 :         return OGRERR_FAILURE;
     581             :     }
     582             : 
     583          55 :     return OGRERR_NONE;
     584             : }
     585             : 
     586             : //----------------------------------------------------------------------
     587             : // FeatureBindInsertParameters()
     588             : //
     589             : // Selectively bind the values of an OGRFeature to a prepared
     590             : // statement, prior to execution. Carefully binds exactly the
     591             : // same parameters that have been set up by FeatureGenerateInsertSQL()
     592             : // as bindable.
     593             : //
     594      266484 : OGRErr OGRGeoPackageTableLayer::FeatureBindInsertParameters(
     595             :     OGRFeature *poFeature, sqlite3_stmt *poStmt, bool bAddFID,
     596             :     bool bBindUnsetFields)
     597             : {
     598      266484 :     return FeatureBindParameters(poFeature, poStmt, nullptr, bAddFID,
     599      266484 :                                  bBindUnsetFields, -1, nullptr, -1, nullptr);
     600             : }
     601             : 
     602             : //----------------------------------------------------------------------
     603             : // FeatureGenerateInsertSQL()
     604             : //
     605             : // Build a SQL INSERT statement that references all the columns in
     606             : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
     607             : // statement. All statements start off with geometry (if it exists)
     608             : // then reference each column in the order it appears in the OGRFeatureDefn.
     609             : // FeatureBindParameters operates on the expectation of this
     610             : // column ordering.
     611             : //
     612         692 : CPLString OGRGeoPackageTableLayer::FeatureGenerateInsertSQL(
     613             :     OGRFeature *poFeature, bool bAddFID, bool bBindUnsetFields, bool bUpsert,
     614             :     const std::string &osUpsertUniqueColumnName)
     615             : {
     616         692 :     bool bNeedComma = false;
     617         692 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     618             : 
     619         692 :     if (poFeatureDefn->GetFieldCount() ==
     620         692 :             ((m_iFIDAsRegularColumnIndex >= 0) ? 1 : 0) &&
     621         692 :         poFeatureDefn->GetGeomFieldCount() == 0 && !bAddFID)
     622             :         return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
     623           8 :                           SQLEscapeName(m_pszTableName).c_str());
     624             : 
     625             :     /* Set up our SQL string basics */
     626        1376 :     CPLString osSQLFront("INSERT");
     627         688 :     if (bUpsert && osUpsertUniqueColumnName.empty())
     628           8 :         osSQLFront += " OR REPLACE";
     629             :     osSQLFront +=
     630         688 :         CPLSPrintf(" INTO \"%s\" ( ", SQLEscapeName(m_pszTableName).c_str());
     631             : 
     632        1376 :     CPLString osSQLBack;
     633         688 :     osSQLBack = ") VALUES (";
     634             : 
     635        1376 :     CPLString osSQLColumn;
     636             : 
     637         688 :     if (bAddFID)
     638             :     {
     639          98 :         osSQLColumn.Printf("\"%s\"", SQLEscapeName(GetFIDColumn()).c_str());
     640          98 :         osSQLFront += osSQLColumn;
     641          98 :         osSQLBack += "?";
     642          98 :         bNeedComma = true;
     643             :     }
     644             : 
     645         688 :     if (poFeatureDefn->GetGeomFieldCount())
     646             :     {
     647         637 :         if (bNeedComma)
     648             :         {
     649          81 :             osSQLFront += ", ";
     650          81 :             osSQLBack += ", ";
     651             :         }
     652             : 
     653             :         osSQLColumn.Printf(
     654             :             "\"%s\"",
     655        1274 :             SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
     656         637 :                 .c_str());
     657         637 :         osSQLFront += osSQLColumn;
     658         637 :         osSQLBack += "?";
     659         637 :         bNeedComma = true;
     660             :     }
     661             : 
     662             :     /* Add attribute column names (except FID) to the SQL */
     663        2935 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     664             :     {
     665        2247 :         const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(i);
     666        2247 :         if (i == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
     667          11 :             continue;
     668        2236 :         if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
     669           2 :             continue;
     670             : 
     671        2234 :         if (!bNeedComma)
     672             :         {
     673          34 :             bNeedComma = true;
     674             :         }
     675             :         else
     676             :         {
     677        2200 :             osSQLFront += ", ";
     678        2200 :             osSQLBack += ", ";
     679             :         }
     680             : 
     681             :         osSQLColumn.Printf(
     682             :             "\"%s\"",
     683        4468 :             SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
     684        2234 :                 .c_str());
     685        2234 :         osSQLFront += osSQLColumn;
     686        2234 :         osSQLBack += "?";
     687             :     }
     688             : 
     689         688 :     osSQLBack += ")";
     690             : 
     691         688 :     if (!bNeedComma)
     692             :         return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
     693           0 :                           SQLEscapeName(m_pszTableName).c_str());
     694             : 
     695         688 :     if (bUpsert && !osUpsertUniqueColumnName.empty())
     696             :     {
     697           6 :         osSQLBack += " ON CONFLICT ";
     698             : #if SQLITE_VERSION_NUMBER < 3035000L
     699             :         osSQLBack += "(\"";
     700             :         osSQLBack += SQLEscapeName(osUpsertUniqueColumnName.c_str());
     701             :         osSQLBack += "\") ";
     702             : #endif
     703           6 :         osSQLBack += "DO UPDATE SET ";
     704           6 :         bNeedComma = false;
     705           6 :         if (poFeatureDefn->GetGeomFieldCount())
     706             :         {
     707             :             osSQLBack += CPLSPrintf(
     708             :                 "\"%s\" = excluded.\"%s\"",
     709           8 :                 SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
     710             :                     .c_str(),
     711           8 :                 SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
     712           8 :                     .c_str());
     713           4 :             bNeedComma = true;
     714             :         }
     715          18 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     716             :         {
     717          12 :             if (i == m_iFIDAsRegularColumnIndex)
     718           0 :                 continue;
     719          12 :             if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
     720           0 :                 continue;
     721             : 
     722          12 :             if (!bNeedComma)
     723             :             {
     724           2 :                 bNeedComma = true;
     725             :             }
     726             :             else
     727             :             {
     728          10 :                 osSQLBack += ", ";
     729             :             }
     730             : 
     731             :             osSQLBack += CPLSPrintf(
     732             :                 "\"%s\" = excluded.\"%s\"",
     733          24 :                 SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
     734             :                     .c_str(),
     735          24 :                 SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
     736          24 :                     .c_str());
     737             :         }
     738             : #if SQLITE_VERSION_NUMBER >= 3035000L
     739           6 :         osSQLBack += " RETURNING \"";
     740           6 :         osSQLBack += SQLEscapeName(GetFIDColumn()).c_str();
     741           6 :         osSQLBack += "\"";
     742             : #endif
     743             :     }
     744             : 
     745        1376 :     return osSQLFront + osSQLBack;
     746             : }
     747             : 
     748             : //----------------------------------------------------------------------
     749             : // FeatureGenerateUpdateSQL()
     750             : //
     751             : // Build a SQL UPDATE statement that references all the columns in
     752             : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
     753             : // statement. All statements start off with geometry (if it exists)
     754             : // then reference each column in the order it appears in the OGRFeatureDefn.
     755             : // FeatureBindParameters operates on the expectation of this
     756             : // column ordering.
     757             : 
     758             : //
     759          45 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
     760             :     const OGRFeature *poFeature) const
     761             : {
     762          45 :     bool bNeedComma = false;
     763          45 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     764             : 
     765             :     /* Set up our SQL string basics */
     766          90 :     std::string osUpdate("UPDATE \"");
     767          45 :     osUpdate += SQLEscapeName(m_pszTableName);
     768          45 :     osUpdate += "\" SET ";
     769             : 
     770          45 :     if (poFeatureDefn->GetGeomFieldCount() > 0)
     771             :     {
     772          38 :         osUpdate += '"';
     773             :         osUpdate +=
     774          38 :             SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
     775          38 :         osUpdate += "\"=?";
     776          38 :         bNeedComma = true;
     777             :     }
     778             : 
     779             :     /* Add attribute column names (except FID) to the SQL */
     780          45 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
     781         110 :     for (int i = 0; i < nFieldCount; i++)
     782             :     {
     783          65 :         const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(i);
     784          65 :         if (i == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
     785           8 :             continue;
     786          57 :         if (!poFeature->IsFieldSet(i))
     787          11 :             continue;
     788          46 :         if (!bNeedComma)
     789           6 :             bNeedComma = true;
     790             :         else
     791          40 :             osUpdate += ", ";
     792             : 
     793          46 :         osUpdate += '"';
     794          46 :         osUpdate += SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     795          46 :         osUpdate += "\"=?";
     796             :     }
     797          45 :     if (!bNeedComma)
     798           1 :         return CPLString();
     799             : 
     800          44 :     osUpdate += " WHERE \"";
     801          44 :     osUpdate += SQLEscapeName(m_pszFidColumn);
     802          44 :     osUpdate += "\" = ?";
     803             : 
     804          44 :     return osUpdate;
     805             : }
     806             : 
     807             : /************************************************************************/
     808             : /*                            GetLayerDefn()                            */
     809             : /************************************************************************/
     810             : 
     811       72866 : const OGRFeatureDefn *OGRGeoPackageTableLayer::GetLayerDefn() const
     812             : {
     813       72866 :     if (!m_bFeatureDefnCompleted)
     814             :     {
     815         897 :         m_bFeatureDefnCompleted = true;
     816         897 :         const_cast<OGRGeoPackageTableLayer *>(this)->ReadTableDefinition();
     817         897 :         m_poFeatureDefn->Seal(/* bSealFields = */ true);
     818             :     }
     819       72866 :     return m_poFeatureDefn;
     820             : }
     821             : 
     822             : /************************************************************************/
     823             : /*                      GetFIDColumn()                                  */
     824             : /************************************************************************/
     825             : 
     826        2886 : const char *OGRGeoPackageTableLayer::GetFIDColumn() const
     827             : {
     828        2886 :     if (!m_bFeatureDefnCompleted)
     829          23 :         GetLayerDefn();
     830        2886 :     return OGRGeoPackageLayer::GetFIDColumn();
     831             : }
     832             : 
     833             : /************************************************************************/
     834             : /*                            GetGeomType()                             */
     835             : /************************************************************************/
     836             : 
     837      269949 : OGRwkbGeometryType OGRGeoPackageTableLayer::GetGeomType() const
     838             : {
     839      269949 :     return m_poFeatureDefn->GetGeomType();
     840             : }
     841             : 
     842             : /************************************************************************/
     843             : /*                         GetGeometryColumn()                          */
     844             : /************************************************************************/
     845             : 
     846        6436 : const char *OGRGeoPackageTableLayer::GetGeometryColumn() const
     847             : 
     848             : {
     849        6436 :     if (m_poFeatureDefn->GetGeomFieldCount() > 0)
     850        6425 :         return m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
     851             :     else
     852          11 :         return "";
     853             : }
     854             : 
     855             : //----------------------------------------------------------------------
     856             : // ReadTableDefinition()
     857             : //
     858             : // Initialization routine. Read all the metadata about a table,
     859             : // starting from just the table name. Reads information from GPKG
     860             : // metadata tables and from SQLite table metadata. Uses it to
     861             : // populate OGRSpatialReference information and OGRFeatureDefn objects,
     862             : // among others.
     863             : //
     864         897 : OGRErr OGRGeoPackageTableLayer::ReadTableDefinition()
     865             : {
     866         897 :     m_poDS->IncrementReadTableDefCounter();
     867             : 
     868         897 :     bool bReadExtent = false;
     869         897 :     sqlite3 *poDb = m_poDS->GetDB();
     870         897 :     OGREnvelope oExtent;
     871        1794 :     CPLString osGeomColumnName;
     872        1794 :     CPLString osGeomColsType;
     873         897 :     bool bHasZ = false;
     874         897 :     bool bHasM = false;
     875             : 
     876             : #ifdef ENABLE_GPKG_OGR_CONTENTS
     877         897 :     if (m_poDS->m_bHasGPKGOGRContents)
     878             :     {
     879             :         CPLString osTrigger1Name(
     880        1792 :             CPLSPrintf("trigger_insert_feature_count_%s", m_pszTableName));
     881             :         CPLString osTrigger2Name(
     882        1792 :             CPLSPrintf("trigger_delete_feature_count_%s", m_pszTableName));
     883             :         const std::map<CPLString, CPLString> &oMap =
     884         896 :             m_poDS->GetNameTypeMapFromSQliteMaster();
     885        1750 :         if (cpl::contains(oMap, osTrigger1Name.toupper()) &&
     886         854 :             cpl::contains(oMap, osTrigger2Name.toupper()))
     887             :         {
     888         854 :             m_bOGRFeatureCountTriggersEnabled = true;
     889             :         }
     890          42 :         else if (m_bIsTable)
     891             :         {
     892          34 :             CPLDebug("GPKG",
     893             :                      "Insert/delete feature_count triggers "
     894             :                      "missing on %s",
     895             :                      m_pszTableName);
     896             :         }
     897             :     }
     898             : #endif
     899             : 
     900             : #ifdef ENABLE_GPKG_OGR_CONTENTS
     901         897 :     if (m_poDS->m_bHasGPKGOGRContents)
     902             :     {
     903         896 :         char *pszSQL = sqlite3_mprintf("SELECT feature_count "
     904             :                                        "FROM gpkg_ogr_contents "
     905             :                                        "WHERE table_name = '%q'"
     906             : #ifdef WORKAROUND_SQLITE3_BUGS
     907             :                                        " OR 0"
     908             : #endif
     909             :                                        " LIMIT 2",
     910             :                                        m_pszTableName);
     911        1792 :         auto oResultFeatureCount = SQLQuery(poDb, pszSQL);
     912         896 :         sqlite3_free(pszSQL);
     913         896 :         if (oResultFeatureCount && oResultFeatureCount->RowCount() == 0)
     914             :         {
     915          32 :             pszSQL = sqlite3_mprintf("SELECT feature_count "
     916             :                                      "FROM gpkg_ogr_contents "
     917             :                                      "WHERE lower(table_name) = lower('%q')"
     918             : #ifdef WORKAROUND_SQLITE3_BUGS
     919             :                                      " OR 0"
     920             : #endif
     921             :                                      " LIMIT 2",
     922             :                                      m_pszTableName);
     923          32 :             oResultFeatureCount = SQLQuery(poDb, pszSQL);
     924          32 :             sqlite3_free(pszSQL);
     925             :         }
     926             : 
     927         896 :         if (oResultFeatureCount && oResultFeatureCount->RowCount() == 1)
     928             :         {
     929         864 :             const char *pszFeatureCount = oResultFeatureCount->GetValue(0, 0);
     930         864 :             if (pszFeatureCount)
     931             :             {
     932         849 :                 m_nTotalFeatureCount =
     933         849 :                     std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
     934             :             }
     935             :         }
     936             :     }
     937             : #endif
     938             : 
     939             :     bool bHasPreexistingSingleGeomColumn =
     940         897 :         m_poFeatureDefn->GetGeomFieldCount() == 1;
     941         897 :     bool bHasMultipleGeomColsInGpkgGeometryColumns = false;
     942             : 
     943         897 :     if (m_bIsInGpkgContents)
     944             :     {
     945             :         /* Check that the table name is registered in gpkg_contents */
     946             :         const std::map<CPLString, GPKGContentsDesc> &oMapContents =
     947         879 :             m_poDS->GetContents();
     948             :         const auto oIterContents =
     949         879 :             oMapContents.find(CPLString(m_pszTableName).toupper());
     950         879 :         if (oIterContents == oMapContents.end())
     951             :         {
     952           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     953             :                      "layer '%s' is not registered in gpkg_contents",
     954             :                      m_pszTableName);
     955           0 :             return OGRERR_FAILURE;
     956             :         }
     957             : 
     958         879 :         const GPKGContentsDesc &oContents = oIterContents->second;
     959             : 
     960         879 :         const char *pszIdentifier = oContents.osIdentifier.c_str();
     961         879 :         if (pszIdentifier[0] != 0 && strcmp(pszIdentifier, m_pszTableName) != 0)
     962           6 :             OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
     963         879 :         const char *pszDescription = oContents.osDescription.c_str();
     964         879 :         if (pszDescription[0])
     965           9 :             OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
     966             : 
     967         879 :         if (m_bIsSpatial)
     968             :         {
     969             :             /* All the extrema have to be non-NULL for this to make sense */
     970        1429 :             if (!oContents.osMinX.empty() && !oContents.osMinY.empty() &&
     971        1429 :                 !oContents.osMaxX.empty() && !oContents.osMaxY.empty())
     972             :             {
     973         604 :                 oExtent.MinX = CPLAtof(oContents.osMinX);
     974         604 :                 oExtent.MinY = CPLAtof(oContents.osMinY);
     975         604 :                 oExtent.MaxX = CPLAtof(oContents.osMaxX);
     976         604 :                 oExtent.MaxY = CPLAtof(oContents.osMaxY);
     977        1208 :                 bReadExtent = oExtent.MinX <= oExtent.MaxX &&
     978         604 :                               oExtent.MinY <= oExtent.MaxY;
     979             :             }
     980             : 
     981             :             /* Check that the table name is registered in gpkg_geometry_columns
     982             :              */
     983         825 :             char *pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
     984             :                                            "geometry_type_name, srs_id, z, m "
     985             :                                            "FROM gpkg_geometry_columns "
     986             :                                            "WHERE table_name = '%q'"
     987             : #ifdef WORKAROUND_SQLITE3_BUGS
     988             :                                            " OR 0"
     989             : #endif
     990             :                                            " LIMIT 2000",
     991             :                                            m_pszTableName);
     992             : 
     993        1650 :             auto oResultGeomCols = SQLQuery(poDb, pszSQL);
     994         825 :             sqlite3_free(pszSQL);
     995         825 :             if (oResultGeomCols && oResultGeomCols->RowCount() == 0)
     996             :             {
     997           0 :                 pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
     998             :                                          "geometry_type_name, srs_id, z, m "
     999             :                                          "FROM gpkg_geometry_columns "
    1000             :                                          "WHERE lower(table_name) = lower('%q')"
    1001             : #ifdef WORKAROUND_SQLITE3_BUGS
    1002             :                                          " OR 0"
    1003             : #endif
    1004             :                                          " LIMIT 2000",
    1005             :                                          m_pszTableName);
    1006             : 
    1007           0 :                 oResultGeomCols = SQLQuery(poDb, pszSQL);
    1008           0 :                 sqlite3_free(pszSQL);
    1009             :             }
    1010             : 
    1011             :             /* gpkg_geometry_columns query has to work */
    1012         825 :             if (!(oResultGeomCols && oResultGeomCols->RowCount() > 0))
    1013             :             {
    1014           0 :                 CPLError(
    1015             :                     CE_Warning, CPLE_AppDefined,
    1016             :                     "layer '%s' is not registered in gpkg_geometry_columns",
    1017             :                     m_pszTableName);
    1018             :             }
    1019             :             else
    1020             :             {
    1021         825 :                 int iRow = -1;
    1022         825 :                 bHasMultipleGeomColsInGpkgGeometryColumns =
    1023         825 :                     oResultGeomCols->RowCount() > 1;
    1024         826 :                 for (int i = 0; i < oResultGeomCols->RowCount(); ++i)
    1025             :                 {
    1026             :                     const char *pszGeomColName =
    1027         826 :                         oResultGeomCols->GetValue(1, i);
    1028         826 :                     if (!pszGeomColName)
    1029           0 :                         continue;
    1030        1652 :                     if (!bHasPreexistingSingleGeomColumn ||
    1031         826 :                         strcmp(pszGeomColName,
    1032         826 :                                m_poFeatureDefn->GetGeomFieldDefn(0)
    1033             :                                    ->GetNameRef()) == 0)
    1034             :                     {
    1035         825 :                         iRow = i;
    1036         825 :                         break;
    1037             :                     }
    1038             :                 }
    1039             : 
    1040         825 :                 if (iRow >= 0)
    1041             :                 {
    1042             :                     const char *pszGeomColName =
    1043         825 :                         oResultGeomCols->GetValue(1, iRow);
    1044         825 :                     if (pszGeomColName != nullptr)
    1045         825 :                         osGeomColumnName = pszGeomColName;
    1046             :                     const char *pszGeomColsType =
    1047         825 :                         oResultGeomCols->GetValue(2, iRow);
    1048         825 :                     if (pszGeomColsType != nullptr)
    1049         825 :                         osGeomColsType = pszGeomColsType;
    1050         825 :                     m_iSrs = oResultGeomCols->GetValueAsInteger(3, iRow);
    1051         825 :                     m_nZFlag = oResultGeomCols->GetValueAsInteger(4, iRow);
    1052         825 :                     m_nMFlag = oResultGeomCols->GetValueAsInteger(5, iRow);
    1053         825 :                     if (!(EQUAL(osGeomColsType, "GEOMETRY") && m_nZFlag == 2))
    1054             :                     {
    1055         819 :                         bHasZ = CPL_TO_BOOL(m_nZFlag);
    1056         819 :                         bHasM = CPL_TO_BOOL(m_nMFlag);
    1057             :                     }
    1058             :                 }
    1059             :                 else
    1060             :                 {
    1061           0 :                     CPLError(
    1062             :                         CE_Warning, CPLE_AppDefined,
    1063             :                         "Cannot find record for layer '%s' and geometry column "
    1064             :                         "'%s' in gpkg_geometry_columns",
    1065             :                         m_pszTableName,
    1066             :                         bHasPreexistingSingleGeomColumn
    1067           0 :                             ? m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()
    1068             :                             : "unknown");
    1069             :                 }
    1070             :             }
    1071             :         }
    1072             :     }
    1073             : 
    1074             :     // set names (in upper case) of fields with unique constraint
    1075        1794 :     std::set<std::string> uniqueFieldsUC;
    1076         897 :     if (m_bIsTable)
    1077             :     {
    1078             :         // If resolving the layer definition of a substantial number of tables,
    1079             :         // fetch in a single time the content of the sqlite_master to increase
    1080             :         // performance
    1081             :         // Threshold somewhat arbitrary. If changing it, change
    1082             :         // ogr_gpkg.py::test_ogr_gpkg_unique_many_layers as well
    1083         889 :         constexpr int THRESHOLD_GET_SQLITE_MASTER = 10;
    1084         889 :         if (m_poDS->GetReadTableDefCounter() >= THRESHOLD_GET_SQLITE_MASTER)
    1085             :         {
    1086           2 :             uniqueFieldsUC = SQLGetUniqueFieldUCConstraints(
    1087           2 :                 poDb, m_pszTableName, m_poDS->GetSqliteMasterContent());
    1088             :         }
    1089             :         else
    1090             :         {
    1091             :             uniqueFieldsUC =
    1092         887 :                 SQLGetUniqueFieldUCConstraints(poDb, m_pszTableName);
    1093             :         }
    1094             :     }
    1095             : 
    1096             :     /* Use the "PRAGMA TABLE_INFO()" call to get table definition */
    1097             :     /*  #|name|type|notnull|default|pk */
    1098             :     /*  0|id|integer|0||1 */
    1099             :     /*  1|name|varchar|0||0 */
    1100         897 :     char *pszSQL = sqlite3_mprintf("pragma table_xinfo('%q')", m_pszTableName);
    1101        1794 :     auto oResultTable = SQLQuery(poDb, pszSQL);
    1102         897 :     sqlite3_free(pszSQL);
    1103             : 
    1104         897 :     if (!oResultTable || oResultTable->RowCount() == 0)
    1105             :     {
    1106           0 :         if (oResultTable)
    1107           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
    1108             :                      m_pszTableName);
    1109           0 :         return OGRERR_FAILURE;
    1110             :     }
    1111             : 
    1112             :     /* Populate feature definition from table description */
    1113             : 
    1114             :     // First pass to determine if we have a single PKID column
    1115         897 :     int nCountPKIDColumns = 0;
    1116        4182 :     for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
    1117             :     {
    1118        3285 :         int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
    1119        3285 :         if (nPKIDIndex > 0)
    1120         883 :             nCountPKIDColumns++;
    1121             :     }
    1122         897 :     if (nCountPKIDColumns > 1)
    1123             :     {
    1124           1 :         CPLDebug("GPKG",
    1125             :                  "For table %s, multiple columns make "
    1126             :                  "the primary key. Ignoring them",
    1127             :                  m_pszTableName);
    1128             :     }
    1129             : 
    1130        4182 :     for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
    1131             :     {
    1132        3285 :         const char *pszName = oResultTable->GetValue(1, iRecord);
    1133        6570 :         std::string osType = oResultTable->GetValue(2, iRecord);
    1134        3285 :         int bNotNull = oResultTable->GetValueAsInteger(3, iRecord);
    1135        3285 :         const char *pszDefault = oResultTable->GetValue(4, iRecord);
    1136        3285 :         int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
    1137        3285 :         int nHiddenValue = oResultTable->GetValueAsInteger(6, iRecord);
    1138             : 
    1139        3285 :         OGRFieldSubType eSubType = OFSTNone;
    1140        3285 :         int nMaxWidth = 0;
    1141        3285 :         int nType = OFTMaxType + 1;
    1142             : 
    1143             :         // SQLite 3.31 has a " GENERATED ALWAYS" suffix in the type column,
    1144             :         // but more recent versions no longer have it.
    1145        3285 :         bool bIsGenerated = false;
    1146        3285 :         constexpr const char *GENERATED_ALWAYS_SUFFIX = " GENERATED ALWAYS";
    1147        3285 :         if (osType.size() > strlen(GENERATED_ALWAYS_SUFFIX) &&
    1148        3285 :             CPLString(osType).toupper().compare(
    1149           0 :                 osType.size() - strlen(GENERATED_ALWAYS_SUFFIX),
    1150             :                 strlen(GENERATED_ALWAYS_SUFFIX), GENERATED_ALWAYS_SUFFIX) == 0)
    1151             :         {
    1152           0 :             bIsGenerated = true;
    1153           0 :             osType.resize(osType.size() - strlen(GENERATED_ALWAYS_SUFFIX));
    1154             :         }
    1155        3285 :         constexpr int GENERATED_VIRTUAL = 2;
    1156        3285 :         constexpr int GENERATED_STORED = 3;
    1157        3285 :         if (nHiddenValue == GENERATED_VIRTUAL ||
    1158             :             nHiddenValue == GENERATED_STORED)
    1159             :         {
    1160           2 :             bIsGenerated = true;
    1161             :         }
    1162             : 
    1163        3285 :         if (!osType.empty() || m_bIsTable)
    1164             :         {
    1165        3282 :             nType = GPkgFieldToOGR(osType.c_str(), eSubType, nMaxWidth);
    1166             :         }
    1167             :         else
    1168             :         {
    1169             :             // For a view, if the geometry column is computed, we don't
    1170             :             // get a type, so trust the one from gpkg_geometry_columns
    1171           3 :             if (EQUAL(osGeomColumnName, pszName))
    1172             :             {
    1173           1 :                 osType = osGeomColsType;
    1174             :             }
    1175             :         }
    1176             : 
    1177             :         /* Not a standard field type... */
    1178        6564 :         if (!osType.empty() && !EQUAL(pszName, "OGC_FID") &&
    1179         829 :             ((nType > OFTMaxType && !osGeomColsType.empty()) ||
    1180        2452 :              EQUAL(osGeomColumnName, pszName)))
    1181             :         {
    1182             :             /* Maybe it is a geometry type? */
    1183             :             OGRwkbGeometryType oGeomType;
    1184         827 :             if (nType > OFTMaxType)
    1185         827 :                 oGeomType = GPkgGeometryTypeToWKB(osType.c_str(), bHasZ, bHasM);
    1186             :             else
    1187           0 :                 oGeomType = wkbUnknown;
    1188         827 :             if (oGeomType != wkbNone)
    1189             :             {
    1190        1653 :                 if ((bHasPreexistingSingleGeomColumn &&
    1191         826 :                      (!bHasMultipleGeomColsInGpkgGeometryColumns ||
    1192           3 :                       strcmp(pszName, m_poFeatureDefn->GetGeomFieldDefn(0)
    1193        1654 :                                           ->GetNameRef()) == 0)) ||
    1194           2 :                     m_poFeatureDefn->GetGeomFieldCount() == 0)
    1195             :                 {
    1196             :                     OGRwkbGeometryType oGeomTypeGeomCols =
    1197         825 :                         GPkgGeometryTypeToWKB(osGeomColsType.c_str(), bHasZ,
    1198             :                                               bHasM);
    1199             :                     /* Enforce consistency between table and metadata */
    1200         825 :                     if (wkbFlatten(oGeomType) == wkbUnknown)
    1201         485 :                         oGeomType = oGeomTypeGeomCols;
    1202         825 :                     if (oGeomType != oGeomTypeGeomCols)
    1203             :                     {
    1204           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1205             :                                  "geometry column type for layer '%s' in "
    1206             :                                  "'%s.%s' (%s) is not "
    1207             :                                  "consistent with type in "
    1208             :                                  "gpkg_geometry_columns (%s)",
    1209             :                                  GetName(), m_pszTableName, pszName,
    1210             :                                  osType.c_str(), osGeomColsType.c_str());
    1211             :                     }
    1212             : 
    1213         825 :                     if (!bHasPreexistingSingleGeomColumn)
    1214             :                     {
    1215           0 :                         OGRGeomFieldDefn oGeomField(pszName, oGeomType);
    1216           0 :                         m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    1217             :                     }
    1218         825 :                     bHasPreexistingSingleGeomColumn = false;
    1219         825 :                     if (bNotNull)
    1220           3 :                         m_poFeatureDefn->GetGeomFieldDefn(0)->SetNullable(
    1221             :                             FALSE);
    1222             : 
    1223             :                     /* Read the SRS */
    1224        1650 :                     auto poSRS = m_poDS->GetSpatialRef(m_iSrs);
    1225         825 :                     if (poSRS)
    1226             :                     {
    1227         700 :                         m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
    1228         350 :                             poSRS.get());
    1229             :                     }
    1230             :                 }
    1231           2 :                 else if (!STARTS_WITH(
    1232             :                              GetName(),
    1233             :                              (std::string(m_pszTableName) + " (").c_str()))
    1234             :                 {
    1235           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1236             :                              "table '%s' has multiple geometry fields. "
    1237             :                              "Ignoring field '%s' for this layer",
    1238             :                              m_pszTableName, pszName);
    1239             :                 }
    1240             :             }
    1241             :             else
    1242             :             {
    1243           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1244             :                          "geometry column '%s' of type '%s' ignored", pszName,
    1245             :                          osType.c_str());
    1246             :             }
    1247             :         }
    1248             :         else
    1249             :         {
    1250        2458 :             if (nType > OFTMaxType)
    1251             :             {
    1252           5 :                 CPLDebug("GPKG",
    1253             :                          "For table %s, unrecognized type name %s for "
    1254             :                          "column %s. Using string type",
    1255             :                          m_pszTableName, osType.c_str(), pszName);
    1256           5 :                 nType = OFTString;
    1257             :             }
    1258             : 
    1259             :             /* Is this the FID column? */
    1260        2458 :             if (nPKIDIndex > 0 && nCountPKIDColumns == 1 &&
    1261         881 :                 (nType == OFTInteger || nType == OFTInteger64))
    1262             :             {
    1263         881 :                 m_pszFidColumn = CPLStrdup(pszName);
    1264             :             }
    1265             :             else
    1266             :             {
    1267        3154 :                 OGRFieldDefn oField(pszName, static_cast<OGRFieldType>(nType));
    1268        1577 :                 oField.SetSubType(eSubType);
    1269        1577 :                 oField.SetWidth(nMaxWidth);
    1270        1577 :                 if (bNotNull)
    1271          10 :                     oField.SetNullable(FALSE);
    1272             : 
    1273        1577 :                 if (cpl::contains(uniqueFieldsUC, CPLString(pszName).toupper()))
    1274             :                 {
    1275          45 :                     oField.SetUnique(TRUE);
    1276             :                 }
    1277             : 
    1278        1577 :                 if (pszDefault != nullptr)
    1279             :                 {
    1280          14 :                     int nYear = 0;
    1281          14 :                     int nMonth = 0;
    1282          14 :                     int nDay = 0;
    1283          14 :                     int nHour = 0;
    1284          14 :                     int nMinute = 0;
    1285          14 :                     float fSecond = 0.0f;
    1286          14 :                     if (oField.GetType() == OFTString &&
    1287           3 :                         !EQUAL(pszDefault, "NULL") &&
    1288           3 :                         !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
    1289          17 :                         pszDefault[0] != '(' && pszDefault[0] != '\'' &&
    1290           0 :                         CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
    1291             :                     {
    1292           0 :                         CPLString osDefault("'");
    1293             :                         char *pszTmp =
    1294           0 :                             CPLEscapeString(pszDefault, -1, CPLES_SQL);
    1295           0 :                         osDefault += pszTmp;
    1296           0 :                         CPLFree(pszTmp);
    1297           0 :                         osDefault += "'";
    1298           0 :                         oField.SetDefault(osDefault);
    1299             :                     }
    1300          22 :                     else if (nType == OFTDateTime &&
    1301           8 :                              sscanf(pszDefault, "'%d-%d-%dT%d:%d:%fZ'", &nYear,
    1302             :                                     &nMonth, &nDay, &nHour, &nMinute,
    1303             :                                     &fSecond) == 6)
    1304             :                     {
    1305           2 :                         if (strchr(pszDefault, '.') == nullptr)
    1306           1 :                             oField.SetDefault(
    1307             :                                 CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
    1308             :                                            nYear, nMonth, nDay, nHour, nMinute,
    1309           1 :                                            static_cast<int>(fSecond + 0.5)));
    1310             :                         else
    1311           1 :                             oField.SetDefault(CPLSPrintf(
    1312             :                                 "'%04d/%02d/%02d %02d:%02d:%06.3f'", nYear,
    1313             :                                 nMonth, nDay, nHour, nMinute, fSecond));
    1314             :                     }
    1315          23 :                     else if ((oField.GetType() == OFTDate ||
    1316          11 :                               oField.GetType() == OFTDateTime) &&
    1317           7 :                              !EQUAL(pszDefault, "NULL") &&
    1318           7 :                              !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
    1319           6 :                              pszDefault[0] != '(' && pszDefault[0] != '\'' &&
    1320          28 :                              !(pszDefault[0] >= '0' && pszDefault[0] <= '9') &&
    1321           4 :                              CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
    1322             :                     {
    1323           8 :                         CPLString osDefault("(");
    1324           4 :                         osDefault += pszDefault;
    1325           4 :                         osDefault += ")";
    1326           4 :                         if (EQUAL(osDefault,
    1327             :                                   "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))"))
    1328           4 :                             oField.SetDefault("CURRENT_TIMESTAMP");
    1329             :                         else
    1330           0 :                             oField.SetDefault(osDefault);
    1331             :                     }
    1332             :                     else
    1333             :                     {
    1334           8 :                         oField.SetDefault(pszDefault);
    1335             :                     }
    1336             :                 }
    1337        1577 :                 oField.SetGenerated(bIsGenerated);
    1338        1577 :                 m_poFeatureDefn->AddFieldDefn(&oField);
    1339             :             }
    1340             :         }
    1341             :     }
    1342             : 
    1343             :     /* Wait, we didn't find a FID? Some operations will not be possible */
    1344         897 :     if (m_bIsTable && m_pszFidColumn == nullptr)
    1345             :     {
    1346           8 :         CPLDebug("GPKG", "no integer primary key defined for table '%s'",
    1347             :                  m_pszTableName);
    1348             :     }
    1349             : 
    1350         897 :     if (bReadExtent)
    1351             :     {
    1352         604 :         m_poExtent = std::make_unique<OGREnvelope>(oExtent);
    1353             :     }
    1354             : 
    1355             :     // Look for sub-types such as JSON
    1356         897 :     if (m_poDS->HasDataColumnsTable())
    1357             :     {
    1358          38 :         pszSQL = sqlite3_mprintf(
    1359             :             "SELECT column_name, name, mime_type, "
    1360             :             "constraint_name, description FROM gpkg_data_columns "
    1361             :             "WHERE table_name = '%q'",
    1362             :             m_pszTableName);
    1363          38 :         oResultTable = SQLQuery(poDb, pszSQL);
    1364          38 :         sqlite3_free(pszSQL);
    1365          38 :         if (oResultTable)
    1366             :         {
    1367         118 :             for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
    1368             :             {
    1369          80 :                 const char *pszColumn = oResultTable->GetValue(0, iRecord);
    1370          80 :                 if (pszColumn == nullptr)
    1371           0 :                     continue;
    1372          80 :                 const char *pszName = oResultTable->GetValue(1, iRecord);
    1373             : 
    1374             :                 // We use the "name" attribute from gpkg_data_columns as the
    1375             :                 // field alternative name, so long as it isn't just a copy
    1376             :                 // of the column name
    1377          80 :                 const char *pszAlias = nullptr;
    1378          80 :                 if (pszName && !EQUAL(pszName, pszColumn))
    1379           9 :                     pszAlias = pszName;
    1380             : 
    1381          80 :                 if (pszAlias)
    1382             :                 {
    1383           9 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1384           9 :                     if (iIdx >= 0)
    1385             :                     {
    1386           9 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetAlternativeName(
    1387             :                             pszAlias);
    1388             :                     }
    1389             :                 }
    1390             : 
    1391          80 :                 if (const char *pszDescription =
    1392          80 :                         oResultTable->GetValue(4, iRecord))
    1393             :                 {
    1394           6 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1395           6 :                     if (iIdx >= 0)
    1396             :                     {
    1397           6 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetComment(
    1398             :                             pszDescription);
    1399             :                     }
    1400             :                 }
    1401             : 
    1402          80 :                 const char *pszMimeType = oResultTable->GetValue(2, iRecord);
    1403             :                 const char *pszConstraintName =
    1404          80 :                     oResultTable->GetValue(3, iRecord);
    1405          80 :                 if (pszMimeType && EQUAL(pszMimeType, "application/json"))
    1406             :                 {
    1407           5 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1408          10 :                     if (iIdx >= 0 &&
    1409           5 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->GetType() ==
    1410             :                             OFTString)
    1411             :                     {
    1412           5 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetSubType(
    1413             :                             OFSTJSON);
    1414           5 :                     }
    1415             :                 }
    1416          75 :                 else if (pszConstraintName)
    1417             :                 {
    1418          63 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1419          63 :                     if (iIdx >= 0)
    1420             :                     {
    1421          63 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetDomainName(
    1422             :                             pszConstraintName);
    1423             :                     }
    1424             :                 }
    1425             :             }
    1426             :         }
    1427             :     }
    1428             : 
    1429             :     // Look for geometry column coordinate precision in gpkg_metadata
    1430         897 :     if (m_poDS->HasMetadataTables() && m_poFeatureDefn->GetGeomFieldCount() > 0)
    1431             :     {
    1432         331 :         pszSQL = sqlite3_mprintf(
    1433             :             "SELECT md.metadata, mdr.column_name "
    1434             :             "FROM gpkg_metadata md "
    1435             :             "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id) "
    1436             :             "WHERE lower(mdr.table_name) = lower('%q') "
    1437             :             "AND md.md_standard_uri = 'http://gdal.org' "
    1438             :             "AND md.mime_type = 'text/xml' "
    1439             :             "AND mdr.reference_scope = 'column' "
    1440             :             "AND md.metadata LIKE '<CoordinatePrecision%%' "
    1441             :             "ORDER BY md.id LIMIT 1000",  // to avoid denial of service
    1442             :             m_pszTableName);
    1443             : 
    1444         662 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    1445         331 :         sqlite3_free(pszSQL);
    1446             : 
    1447         347 :         for (int i = 0; oResult && i < oResult->RowCount(); i++)
    1448             :         {
    1449          16 :             const char *pszMetadata = oResult->GetValue(0, i);
    1450          16 :             const char *pszColumn = oResult->GetValue(1, i);
    1451          16 :             if (pszMetadata && pszColumn)
    1452             :             {
    1453             :                 const int iGeomCol =
    1454          16 :                     m_poFeatureDefn->GetGeomFieldIndex(pszColumn);
    1455          16 :                 if (iGeomCol >= 0)
    1456             :                 {
    1457             :                     auto psXMLNode =
    1458          32 :                         CPLXMLTreeCloser(CPLParseXMLString(pszMetadata));
    1459          16 :                     if (psXMLNode)
    1460             :                     {
    1461          32 :                         OGRGeomCoordinatePrecision sCoordPrec;
    1462          16 :                         if (const char *pszVal = CPLGetXMLValue(
    1463          16 :                                 psXMLNode.get(), "xy_resolution", nullptr))
    1464             :                         {
    1465          16 :                             sCoordPrec.dfXYResolution = CPLAtof(pszVal);
    1466             :                         }
    1467          16 :                         if (const char *pszVal = CPLGetXMLValue(
    1468          16 :                                 psXMLNode.get(), "z_resolution", nullptr))
    1469             :                         {
    1470          12 :                             sCoordPrec.dfZResolution = CPLAtof(pszVal);
    1471             :                         }
    1472          16 :                         if (const char *pszVal = CPLGetXMLValue(
    1473          16 :                                 psXMLNode.get(), "m_resolution", nullptr))
    1474             :                         {
    1475          12 :                             sCoordPrec.dfMResolution = CPLAtof(pszVal);
    1476             :                         }
    1477          16 :                         m_poFeatureDefn->GetGeomFieldDefn(iGeomCol)
    1478          16 :                             ->SetCoordinatePrecision(sCoordPrec);
    1479          16 :                         if (CPLTestBool(CPLGetXMLValue(
    1480          16 :                                 psXMLNode.get(), "discard_coord_lsb", "false")))
    1481             :                         {
    1482           4 :                             m_sBinaryPrecision.SetFrom(sCoordPrec);
    1483           4 :                             m_bUndoDiscardCoordLSBOnReading =
    1484           4 :                                 CPLTestBool(CPLGetXMLValue(
    1485           4 :                                     psXMLNode.get(),
    1486             :                                     "undo_discard_coord_lsb_on_reading",
    1487             :                                     "false"));
    1488             :                         }
    1489             :                     }
    1490             :                 }
    1491             :             }
    1492             :         }
    1493             :     }
    1494             : 
    1495             :     /* Update the columns string */
    1496         897 :     BuildColumns();
    1497             : 
    1498         897 :     CheckUnknownExtensions();
    1499             : 
    1500         897 :     InitView();
    1501             : 
    1502         897 :     return OGRERR_NONE;
    1503             : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                      OGRGeoPackageTableLayer()                       */
    1507             : /************************************************************************/
    1508             : 
    1509        3971 : OGRGeoPackageTableLayer::OGRGeoPackageTableLayer(GDALGeoPackageDataset *poDS,
    1510        3971 :                                                  const char *pszTableName)
    1511        3971 :     : OGRGeoPackageLayer(poDS), m_pszTableName(CPLStrdup(pszTableName))
    1512             : {
    1513        3971 :     memset(m_abHasGeometryExtension, 0, sizeof(m_abHasGeometryExtension));
    1514             : 
    1515        3971 :     m_poFeatureDefn = new OGRFeatureDefn(m_pszTableName);
    1516        3971 :     SetDescription(m_poFeatureDefn->GetName());
    1517        3971 :     m_poFeatureDefn->SetGeomType(wkbNone);
    1518        3971 :     m_poFeatureDefn->Reference();
    1519        3971 : }
    1520             : 
    1521             : /************************************************************************/
    1522             : /*                      ~OGRGeoPackageTableLayer()                      */
    1523             : /************************************************************************/
    1524             : 
    1525        7942 : OGRGeoPackageTableLayer::~OGRGeoPackageTableLayer()
    1526             : {
    1527        3971 :     OGRGeoPackageTableLayer::SyncToDisk();
    1528             : 
    1529             :     /* Clean up resources in memory */
    1530        3971 :     if (m_pszTableName)
    1531        3971 :         CPLFree(m_pszTableName);
    1532             : 
    1533        3971 :     if (m_poUpdateStatement)
    1534          22 :         sqlite3_finalize(m_poUpdateStatement);
    1535             : 
    1536        3971 :     if (m_poInsertStatement)
    1537         530 :         sqlite3_finalize(m_poInsertStatement);
    1538             : 
    1539        3971 :     if (m_poGetFeatureStatement)
    1540          28 :         sqlite3_finalize(m_poGetFeatureStatement);
    1541             : 
    1542        3971 :     CancelAsyncNextArrowArray();
    1543        7942 : }
    1544             : 
    1545             : /************************************************************************/
    1546             : /*                 CancelAsyncNextArrowArray()                          */
    1547             : /************************************************************************/
    1548             : 
    1549      331861 : void OGRGeoPackageTableLayer::CancelAsyncNextArrowArray()
    1550             : {
    1551      331861 :     if (m_poFillArrowArray)
    1552             :     {
    1553          98 :         std::lock_guard oLock(m_poFillArrowArray->oMutex);
    1554          49 :         m_poFillArrowArray->nCountRows = -1;
    1555          49 :         m_poFillArrowArray->oCV.notify_one();
    1556             :     }
    1557             : 
    1558      331861 :     if (m_oThreadNextArrowArray.joinable())
    1559             :     {
    1560           2 :         m_oThreadNextArrowArray.join();
    1561             :     }
    1562             : 
    1563      331861 :     m_poFillArrowArray.reset();
    1564             : 
    1565      331875 :     while (!m_oQueueArrowArrayPrefetchTasks.empty())
    1566             :     {
    1567          28 :         auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
    1568          14 :         m_oQueueArrowArrayPrefetchTasks.pop();
    1569             : 
    1570             :         {
    1571          28 :             std::lock_guard oLock(task->m_oMutex);
    1572          14 :             task->m_bStop = true;
    1573          14 :             task->m_oCV.notify_one();
    1574             :         }
    1575          14 :         if (task->m_oThread.joinable())
    1576          14 :             task->m_oThread.join();
    1577             : 
    1578          14 :         if (task->m_psArrowArray)
    1579             :         {
    1580          14 :             if (task->m_psArrowArray->release)
    1581          14 :                 task->m_psArrowArray->release(task->m_psArrowArray.get());
    1582             :         }
    1583             :     }
    1584      331861 : }
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                        InitView()                                    */
    1588             : /************************************************************************/
    1589             : 
    1590         897 : void OGRGeoPackageTableLayer::InitView()
    1591             : {
    1592             : #ifdef SQLITE_HAS_COLUMN_METADATA
    1593         897 :     if (!m_bIsTable)
    1594             :     {
    1595             :         /* Detect if the view columns have the FID and geom columns of a */
    1596             :         /* table that has itself a spatial index */
    1597           8 :         sqlite3_stmt *hStmt = nullptr;
    1598           8 :         char *pszSQL = sqlite3_mprintf("SELECT * FROM \"%w\"", m_pszTableName);
    1599           8 :         CPL_IGNORE_RET_VAL(
    1600           8 :             sqlite3_prepare_v2(m_poDS->GetDB(), pszSQL, -1, &hStmt, nullptr));
    1601           8 :         sqlite3_free(pszSQL);
    1602           8 :         if (hStmt)
    1603             :         {
    1604           8 :             if (sqlite3_step(hStmt) == SQLITE_ROW)
    1605             :             {
    1606           8 :                 OGRGeoPackageTableLayer *poLayerGeom = nullptr;
    1607           8 :                 const int nRawColumns = sqlite3_column_count(hStmt);
    1608          33 :                 for (int iCol = 0; iCol < nRawColumns; iCol++)
    1609             :                 {
    1610             :                     CPLString osColName(
    1611          50 :                         SQLUnescape(sqlite3_column_name(hStmt, iCol)));
    1612             :                     const char *pszTableName =
    1613          25 :                         sqlite3_column_table_name(hStmt, iCol);
    1614             :                     const char *pszOriginName =
    1615          25 :                         sqlite3_column_origin_name(hStmt, iCol);
    1616          26 :                     if (EQUAL(osColName, "OGC_FID") &&
    1617           1 :                         (pszOriginName == nullptr ||
    1618           1 :                          osColName != pszOriginName))
    1619             :                     {
    1620             :                         // in the case we have a OGC_FID column, and that
    1621             :                         // is not the name of the original column, then
    1622             :                         // interpret this as an explicit intent to be a
    1623             :                         // PKID.
    1624             :                         // We cannot just take the FID of a source table as
    1625             :                         // a FID because of potential joins that would result
    1626             :                         // in multiple records with same source FID.
    1627           1 :                         CPLFree(m_pszFidColumn);
    1628           1 :                         m_pszFidColumn = CPLStrdup(osColName);
    1629           1 :                         m_poFeatureDefn->DeleteFieldDefn(
    1630           1 :                             m_poFeatureDefn->GetFieldIndex(osColName));
    1631             :                     }
    1632          32 :                     else if (iCol == 0 &&
    1633           8 :                              sqlite3_column_type(hStmt, iCol) == SQLITE_INTEGER)
    1634             :                     {
    1635             :                         // Assume the first column of integer type is the FID
    1636             :                         // column per the latest requirements of the GPKG spec
    1637           6 :                         CPLFree(m_pszFidColumn);
    1638           6 :                         m_pszFidColumn = CPLStrdup(osColName);
    1639           6 :                         m_poFeatureDefn->DeleteFieldDefn(
    1640           6 :                             m_poFeatureDefn->GetFieldIndex(osColName));
    1641             :                     }
    1642          18 :                     else if (pszTableName != nullptr &&
    1643             :                              pszOriginName != nullptr)
    1644             :                     {
    1645             :                         OGRGeoPackageTableLayer *poLayer =
    1646           0 :                             dynamic_cast<OGRGeoPackageTableLayer *>(
    1647          16 :                                 m_poDS->GetLayerByName(pszTableName));
    1648          16 :                         if (poLayer != nullptr &&
    1649          32 :                             osColName == GetGeometryColumn() &&
    1650           6 :                             strcmp(pszOriginName,
    1651             :                                    poLayer->GetGeometryColumn()) == 0)
    1652             :                         {
    1653           6 :                             poLayerGeom = poLayer;
    1654             :                         }
    1655             :                     }
    1656             :                 }
    1657             : 
    1658           8 :                 if (poLayerGeom != nullptr && poLayerGeom->HasSpatialIndex())
    1659             :                 {
    1660           9 :                     for (int iCol = 0; iCol < nRawColumns; iCol++)
    1661             :                     {
    1662             :                         const std::string osColName(
    1663           9 :                             SQLUnescape(sqlite3_column_name(hStmt, iCol)));
    1664             :                         const char *pszTableName =
    1665           9 :                             sqlite3_column_table_name(hStmt, iCol);
    1666             :                         const char *pszOriginName =
    1667           9 :                             sqlite3_column_origin_name(hStmt, iCol);
    1668           9 :                         if (pszTableName != nullptr && pszOriginName != nullptr)
    1669             :                         {
    1670             :                             OGRGeoPackageTableLayer *poLayer =
    1671           0 :                                 dynamic_cast<OGRGeoPackageTableLayer *>(
    1672           8 :                                     m_poDS->GetLayerByName(pszTableName));
    1673          16 :                             if (poLayer != nullptr && poLayer == poLayerGeom &&
    1674           8 :                                 strcmp(pszOriginName,
    1675             :                                        poLayer->GetFIDColumn()) == 0)
    1676             :                             {
    1677           6 :                                 m_bHasSpatialIndex = true;
    1678           6 :                                 m_osRTreeName = poLayerGeom->m_osRTreeName;
    1679           6 :                                 m_osFIDForRTree = osColName;
    1680           6 :                                 break;
    1681             :                             }
    1682             :                         }
    1683             :                     }
    1684             :                 }
    1685             :             }
    1686           8 :             sqlite3_finalize(hStmt);
    1687             :         }
    1688             : 
    1689             :         /* Update the columns string */
    1690           8 :         BuildColumns();
    1691             :     }
    1692             : #endif
    1693         897 : }
    1694             : 
    1695             : /************************************************************************/
    1696             : /*                      CheckUpdatableTable()                           */
    1697             : /************************************************************************/
    1698             : 
    1699        5251 : bool OGRGeoPackageTableLayer::CheckUpdatableTable(const char *pszOperation)
    1700             : {
    1701        5251 :     if (!m_poDS->GetUpdate())
    1702             :     {
    1703           4 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1704             :                  pszOperation);
    1705           4 :         return false;
    1706             :     }
    1707             :     /* -------------------------------------------------------------------- */
    1708             :     /*      Check that is a table and not a view                            */
    1709             :     /* -------------------------------------------------------------------- */
    1710        5247 :     if (!m_bIsTable)
    1711             :     {
    1712           6 :         CPLError(CE_Failure, CPLE_AppDefined, "Layer %s is not a table",
    1713             :                  m_pszTableName);
    1714           6 :         return false;
    1715             :     }
    1716        5241 :     return true;
    1717             : }
    1718             : 
    1719             : /************************************************************************/
    1720             : /*                      CreateField()                                   */
    1721             : /************************************************************************/
    1722             : 
    1723        4247 : OGRErr OGRGeoPackageTableLayer::CreateField(const OGRFieldDefn *poField,
    1724             :                                             int /* bApproxOK */)
    1725             : {
    1726        4247 :     if (!m_bFeatureDefnCompleted)
    1727           0 :         GetLayerDefn();
    1728        4247 :     if (!CheckUpdatableTable("CreateField"))
    1729           1 :         return OGRERR_FAILURE;
    1730             : 
    1731        8492 :     OGRFieldDefn oFieldDefn(poField);
    1732        4246 :     int nMaxWidth = 0;
    1733        4246 :     if (m_bPreservePrecision && poField->GetType() == OFTString)
    1734        2816 :         nMaxWidth = poField->GetWidth();
    1735             :     else
    1736        1430 :         oFieldDefn.SetWidth(0);
    1737        4246 :     oFieldDefn.SetPrecision(0);
    1738             : 
    1739        4246 :     if (m_bLaunder)
    1740           1 :         oFieldDefn.SetName(
    1741           2 :             GDALGeoPackageDataset::LaunderName(oFieldDefn.GetNameRef())
    1742             :                 .c_str());
    1743             : 
    1744        4246 :     if (m_poFeatureDefn->GetFieldIndex(oFieldDefn.GetNameRef()) >= 0)
    1745             :     {
    1746           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1747             :                  "Cannot create field %s. "
    1748             :                  "A field with the same name already exists.",
    1749             :                  oFieldDefn.GetNameRef());
    1750           3 :         return OGRERR_FAILURE;
    1751             :     }
    1752             : 
    1753        4243 :     if (m_poFeatureDefn->GetGeomFieldIndex(oFieldDefn.GetNameRef()) >= 0)
    1754             :     {
    1755           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1756             :                  "Cannot create field %s. "
    1757             :                  "It has the same name as the geometry field.",
    1758             :                  oFieldDefn.GetNameRef());
    1759           3 :         return OGRERR_FAILURE;
    1760             :     }
    1761             : 
    1762       12720 :     if (m_pszFidColumn != nullptr &&
    1763        4247 :         EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn) &&
    1764          12 :         poField->GetType() != OFTInteger &&
    1765        8486 :         poField->GetType() != OFTInteger64 &&
    1766             :         // typically a GeoPackage exported with QGIS as a shapefile and
    1767             :         // re-imported See https://github.com/qgis/QGIS/pull/43118
    1768           6 :         !(poField->GetType() == OFTReal && poField->GetWidth() == 20 &&
    1769           1 :           poField->GetPrecision() == 0))
    1770             :     {
    1771           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1772             :                  oFieldDefn.GetNameRef());
    1773           3 :         return OGRERR_FAILURE;
    1774             :     }
    1775             : 
    1776             :     const int nMaxColumns =
    1777        4237 :         sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_COLUMN, -1);
    1778             :     // + 1 for the FID column
    1779        4237 :     if (m_poFeatureDefn->GetFieldCount() +
    1780        4237 :             m_poFeatureDefn->GetGeomFieldCount() + 1 >=
    1781             :         nMaxColumns)
    1782             :     {
    1783           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1784             :                  "Cannot add field %s. Limit of %d columns reached",
    1785             :                  oFieldDefn.GetNameRef(), nMaxColumns);
    1786           1 :         return OGRERR_FAILURE;
    1787             :     }
    1788             : 
    1789        4236 :     if (!m_bDeferredCreation)
    1790             :     {
    1791          15 :         CPLString osCommand;
    1792             : 
    1793             :         // ADD COLUMN has several restrictions
    1794             :         // See https://www.sqlite.org/lang_altertable.html#altertabaddcol
    1795             : 
    1796             :         osCommand.Printf("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s",
    1797          30 :                          SQLEscapeName(m_pszTableName).c_str(),
    1798          30 :                          SQLEscapeName(oFieldDefn.GetNameRef()).c_str(),
    1799             :                          GPkgFieldFromOGR(poField->GetType(),
    1800          45 :                                           poField->GetSubType(), nMaxWidth));
    1801          15 :         if (!poField->IsNullable())
    1802           1 :             osCommand += " NOT NULL";
    1803          15 :         if (poField->IsUnique())
    1804             :         {
    1805             :             // this will fail when SQLCommand() is run, as it is not allowed
    1806             :             // by SQLite. This is a bit of an artificial restriction.
    1807             :             // We could override it by rewriting the table.
    1808           1 :             osCommand += " UNIQUE";
    1809             :         }
    1810          18 :         if (poField->GetDefault() != nullptr &&
    1811           3 :             !poField->IsDefaultDriverSpecific())
    1812             :         {
    1813           3 :             osCommand += " DEFAULT ";
    1814           3 :             int nYear = 0;
    1815           3 :             int nMonth = 0;
    1816           3 :             int nDay = 0;
    1817           3 :             int nHour = 0;
    1818           3 :             int nMinute = 0;
    1819           3 :             float fSecond = 0.0f;
    1820           5 :             if (poField->GetType() == OFTDateTime &&
    1821           2 :                 sscanf(poField->GetDefault(), "'%d/%d/%d %d:%d:%f'", &nYear,
    1822             :                        &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6)
    1823             :             {
    1824           2 :                 if (strchr(poField->GetDefault(), '.') == nullptr)
    1825             :                     osCommand += CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%02dZ'",
    1826             :                                             nYear, nMonth, nDay, nHour, nMinute,
    1827           1 :                                             static_cast<int>(fSecond + 0.5));
    1828             :                 else
    1829             :                     osCommand +=
    1830             :                         CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%06.3fZ'", nYear,
    1831           1 :                                    nMonth, nDay, nHour, nMinute, fSecond);
    1832             :             }
    1833             :             else
    1834             :             {
    1835             :                 // This could fail if it is CURRENT_TIMESTAMP, etc.
    1836           1 :                 osCommand += poField->GetDefault();
    1837             :             }
    1838             :         }
    1839          12 :         else if (!poField->IsNullable())
    1840             :         {
    1841             :             // SQLite mandates a DEFAULT value when adding a NOT NULL column in
    1842             :             // an ALTER TABLE ADD COLUMN.
    1843           1 :             osCommand += " DEFAULT ''";
    1844             :         }
    1845             : 
    1846          15 :         OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
    1847             : 
    1848          15 :         if (err != OGRERR_NONE)
    1849           1 :             return err;
    1850             : 
    1851          14 :         if (!DoSpecialProcessingForColumnCreation(poField))
    1852             :         {
    1853           0 :             return OGRERR_FAILURE;
    1854             :         }
    1855             :     }
    1856             : 
    1857        4235 :     whileUnsealing(m_poFeatureDefn)->AddFieldDefn(&oFieldDefn);
    1858             : 
    1859        4235 :     if (m_poDS->IsInTransaction())
    1860             :     {
    1861             :         m_apoFieldDefnChanges.emplace_back(
    1862         412 :             std::make_unique<OGRFieldDefn>(oFieldDefn),
    1863         412 :             m_poFeatureDefn->GetFieldCount() - 1, FieldChangeType::ADD_FIELD,
    1864        1236 :             m_poDS->GetCurrentSavepoint());
    1865             :     }
    1866             : 
    1867        8470 :     if (m_pszFidColumn != nullptr &&
    1868        4235 :         EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn))
    1869             :     {
    1870           4 :         m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount() - 1;
    1871             :     }
    1872             : 
    1873        4235 :     if (!m_bDeferredCreation)
    1874             :     {
    1875          14 :         ResetReading();
    1876             :     }
    1877             : 
    1878        4235 :     return OGRERR_NONE;
    1879             : }
    1880             : 
    1881             : /************************************************************************/
    1882             : /*                DoSpecialProcessingForColumnCreation()                */
    1883             : /************************************************************************/
    1884             : 
    1885        4243 : bool OGRGeoPackageTableLayer::DoSpecialProcessingForColumnCreation(
    1886             :     const OGRFieldDefn *poField)
    1887             : {
    1888        4243 :     const std::string &osConstraintName(poField->GetDomainName());
    1889        8486 :     const std::string osName(poField->GetAlternativeNameRef());
    1890        4243 :     const std::string &osDescription(poField->GetComment());
    1891             : 
    1892        8486 :     std::string osMimeType;
    1893        4243 :     if (poField->GetType() == OFTString && poField->GetSubType() == OFSTJSON)
    1894             :     {
    1895          13 :         osMimeType = "application/json";
    1896             :     }
    1897             : 
    1898        8467 :     if (osConstraintName.empty() && osName.empty() && osDescription.empty() &&
    1899        4224 :         osMimeType.empty())
    1900             :     {
    1901             :         // no record required
    1902        4211 :         return true;
    1903             :     }
    1904             : 
    1905          32 :     if (!m_poDS->CreateColumnsTableAndColumnConstraintsTablesIfNecessary())
    1906           0 :         return false;
    1907             : 
    1908             :     /* Now let's register our column. */
    1909          64 :     std::string osNameSqlValue;
    1910          32 :     if (osName.empty())
    1911             :     {
    1912          23 :         osNameSqlValue = "NULL";
    1913             :     }
    1914             :     else
    1915             :     {
    1916           9 :         char *pszName = sqlite3_mprintf("'%q'", osName.c_str());
    1917           9 :         osNameSqlValue = std::string(pszName);
    1918           9 :         sqlite3_free(pszName);
    1919             :     }
    1920             : 
    1921          64 :     std::string osDescriptionSqlValue;
    1922          32 :     if (osDescription.empty())
    1923             :     {
    1924          28 :         osDescriptionSqlValue = "NULL";
    1925             :     }
    1926             :     else
    1927             :     {
    1928           4 :         char *pszDescription = sqlite3_mprintf("'%q'", osDescription.c_str());
    1929           4 :         osDescriptionSqlValue = std::string(pszDescription);
    1930           4 :         sqlite3_free(pszDescription);
    1931             :     }
    1932             : 
    1933          64 :     std::string osMimeTypeSqlValue;
    1934          32 :     if (osMimeType.empty())
    1935             :     {
    1936          19 :         osMimeTypeSqlValue = "NULL";
    1937             :     }
    1938             :     else
    1939             :     {
    1940          13 :         char *pszMimeType = sqlite3_mprintf("'%q'", osMimeType.c_str());
    1941          13 :         osMimeTypeSqlValue = std::string(pszMimeType);
    1942          13 :         sqlite3_free(pszMimeType);
    1943             :     }
    1944             : 
    1945          32 :     std::string osConstraintNameValue;
    1946          32 :     if (osConstraintName.empty())
    1947             :     {
    1948          23 :         osConstraintNameValue = "NULL";
    1949             :     }
    1950             :     else
    1951             :     {
    1952             :         char *pszConstraintName =
    1953           9 :             sqlite3_mprintf("'%q'", osConstraintName.c_str());
    1954           9 :         osConstraintNameValue = std::string(pszConstraintName);
    1955           9 :         sqlite3_free(pszConstraintName);
    1956             :     }
    1957             : 
    1958          32 :     char *pszSQL = sqlite3_mprintf(
    1959             :         "INSERT INTO gpkg_data_columns (table_name, column_name, name, "
    1960             :         "title, description, mime_type, constraint_name) VALUES ("
    1961             :         "'%q', '%q', %s, NULL, %s, %s, %s)",
    1962             :         m_pszTableName, poField->GetNameRef(), osNameSqlValue.c_str(),
    1963             :         osDescriptionSqlValue.c_str(), osMimeTypeSqlValue.c_str(),
    1964             :         osConstraintNameValue.c_str());
    1965             : 
    1966          32 :     bool ok = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
    1967          32 :     sqlite3_free(pszSQL);
    1968          32 :     return ok;
    1969             : }
    1970             : 
    1971             : /************************************************************************/
    1972             : /*                           CreateGeomField()                          */
    1973             : /************************************************************************/
    1974             : 
    1975             : OGRErr
    1976           6 : OGRGeoPackageTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
    1977             :                                          int /* bApproxOK */)
    1978             : {
    1979           6 :     if (!m_bFeatureDefnCompleted)
    1980           1 :         GetLayerDefn();
    1981           6 :     if (!CheckUpdatableTable("CreateGeomField"))
    1982           1 :         return OGRERR_FAILURE;
    1983             : 
    1984           5 :     if (m_poFeatureDefn->GetGeomFieldCount() == 1)
    1985             :     {
    1986           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1987             :                  "Cannot create more than one geometry field in GeoPackage");
    1988           2 :         return OGRERR_FAILURE;
    1989             :     }
    1990             : 
    1991           3 :     OGRwkbGeometryType eType = poGeomFieldIn->GetType();
    1992           3 :     if (eType == wkbNone)
    1993             :     {
    1994           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1995             :                  "Cannot create geometry field of type wkbNone");
    1996           0 :         return OGRERR_FAILURE;
    1997             :     }
    1998             : 
    1999           6 :     OGRGeomFieldDefn oGeomField(poGeomFieldIn);
    2000           3 :     auto poSRSOri = poGeomFieldIn->GetSpatialRef();
    2001           3 :     if (poSRSOri)
    2002             :     {
    2003           0 :         auto poSRS = poSRSOri->Clone();
    2004           0 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2005           0 :         oGeomField.SetSpatialRef(poSRS);
    2006           0 :         poSRS->Release();
    2007             :     }
    2008           3 :     if (EQUAL(oGeomField.GetNameRef(), ""))
    2009             :     {
    2010           1 :         oGeomField.SetName("geom");
    2011             :     }
    2012             : 
    2013           3 :     const OGRSpatialReference *poSRS = oGeomField.GetSpatialRef();
    2014           3 :     m_iSrs = m_poDS->GetSrsId(poSRS);
    2015             : 
    2016             :     /* -------------------------------------------------------------------- */
    2017             :     /*      Create the new field.                                           */
    2018             :     /* -------------------------------------------------------------------- */
    2019           3 :     if (!m_bDeferredCreation)
    2020             :     {
    2021           4 :         char *pszSQL = sqlite3_mprintf(
    2022             :             "ALTER TABLE \"%w\" ADD COLUMN \"%w\" %s%s"
    2023             :             ";"
    2024             :             "UPDATE gpkg_contents SET data_type = 'features' "
    2025             :             "WHERE lower(table_name) = lower('%q')",
    2026             :             m_pszTableName, oGeomField.GetNameRef(),
    2027           2 :             m_poDS->GetGeometryTypeString(oGeomField.GetType()),
    2028           2 :             !oGeomField.IsNullable() ? " NOT NULL DEFAULT ''" : "",
    2029             :             m_pszTableName);
    2030           2 :         CPLString osSQL(pszSQL);
    2031           2 :         sqlite3_free(pszSQL);
    2032             : 
    2033           2 :         OGRErr err = SQLCommand(m_poDS->GetDB(), osSQL);
    2034           2 :         if (err != OGRERR_NONE)
    2035           0 :             return err;
    2036             :     }
    2037             : 
    2038           3 :     if (m_poDS->IsInTransaction())
    2039             :     {
    2040             :         m_apoGeomFieldDefnChanges.emplace_back(
    2041           0 :             std::make_unique<OGRGeomFieldDefn>(oGeomField),
    2042           0 :             m_poFeatureDefn->GetGeomFieldCount(), FieldChangeType::ADD_FIELD);
    2043             :     }
    2044             : 
    2045           3 :     whileUnsealing(m_poFeatureDefn)->AddGeomFieldDefn(&oGeomField);
    2046             : 
    2047           3 :     if (!m_bDeferredCreation)
    2048             :     {
    2049           2 :         OGRErr err = RegisterGeometryColumn();
    2050           2 :         if (err != OGRERR_NONE)
    2051           0 :             return err;
    2052             : 
    2053           2 :         ResetReading();
    2054             :     }
    2055             : 
    2056           3 :     return OGRERR_NONE;
    2057             : }
    2058             : 
    2059             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    2060             : 
    2061             : /************************************************************************/
    2062             : /*                      DisableFeatureCount()                           */
    2063             : /************************************************************************/
    2064             : 
    2065         240 : void OGRGeoPackageTableLayer::DisableFeatureCount()
    2066             : {
    2067         240 :     m_nTotalFeatureCount = -1;
    2068         240 : }
    2069             : 
    2070             : /************************************************************************/
    2071             : /*                     CreateFeatureCountTriggers()                     */
    2072             : /************************************************************************/
    2073             : 
    2074        5049 : void OGRGeoPackageTableLayer::CreateFeatureCountTriggers(
    2075             :     const char *pszTableName)
    2076             : {
    2077        5049 :     if (m_bAddOGRFeatureCountTriggers)
    2078             :     {
    2079         876 :         if (pszTableName == nullptr)
    2080         867 :             pszTableName = m_pszTableName;
    2081             : 
    2082         876 :         m_bOGRFeatureCountTriggersEnabled = true;
    2083         876 :         m_bAddOGRFeatureCountTriggers = false;
    2084         876 :         m_bFeatureCountTriggersDeletedInTransaction = false;
    2085             : 
    2086         876 :         CPLDebug("GPKG", "Creating insert/delete feature_count triggers");
    2087         876 :         char *pszSQL = sqlite3_mprintf(
    2088             :             "CREATE TRIGGER \"trigger_insert_feature_count_%w\" "
    2089             :             "AFTER INSERT ON \"%w\" "
    2090             :             "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
    2091             :             "feature_count + 1 WHERE lower(table_name) = lower('%q'); END;",
    2092             :             pszTableName, pszTableName, pszTableName);
    2093         876 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2094         876 :         sqlite3_free(pszSQL);
    2095             : 
    2096         876 :         pszSQL = sqlite3_mprintf(
    2097             :             "CREATE TRIGGER \"trigger_delete_feature_count_%w\" "
    2098             :             "AFTER DELETE ON \"%w\" "
    2099             :             "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
    2100             :             "feature_count - 1 WHERE lower(table_name) = lower('%q'); END;",
    2101             :             pszTableName, pszTableName, pszTableName);
    2102         876 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2103         876 :         sqlite3_free(pszSQL);
    2104             :     }
    2105        5049 : }
    2106             : 
    2107             : /************************************************************************/
    2108             : /*                   DisableFeatureCountTriggers()                      */
    2109             : /************************************************************************/
    2110             : 
    2111          65 : void OGRGeoPackageTableLayer::DisableFeatureCountTriggers(
    2112             :     bool bNullifyFeatureCount)
    2113             : {
    2114          65 :     if (m_bOGRFeatureCountTriggersEnabled)
    2115             :     {
    2116          65 :         m_bOGRFeatureCountTriggersEnabled = false;
    2117          65 :         m_bAddOGRFeatureCountTriggers = true;
    2118          65 :         m_bFeatureCountTriggersDeletedInTransaction = m_poDS->IsInTransaction();
    2119             : 
    2120          65 :         CPLDebug("GPKG", "Deleting insert/delete feature_count triggers");
    2121             : 
    2122          65 :         char *pszSQL = sqlite3_mprintf(
    2123             :             "DROP TRIGGER \"trigger_insert_feature_count_%w\"", m_pszTableName);
    2124          65 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2125          65 :         sqlite3_free(pszSQL);
    2126             : 
    2127          65 :         pszSQL = sqlite3_mprintf(
    2128             :             "DROP TRIGGER \"trigger_delete_feature_count_%w\"", m_pszTableName);
    2129          65 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2130          65 :         sqlite3_free(pszSQL);
    2131             : 
    2132          65 :         if (m_poDS->m_bHasGPKGOGRContents && bNullifyFeatureCount)
    2133             :         {
    2134          56 :             pszSQL = sqlite3_mprintf(
    2135             :                 "UPDATE gpkg_ogr_contents SET feature_count = NULL WHERE "
    2136             :                 "lower(table_name )= lower('%q')",
    2137             :                 m_pszTableName);
    2138          56 :             SQLCommand(m_poDS->GetDB(), pszSQL);
    2139          56 :             sqlite3_free(pszSQL);
    2140             :         }
    2141             :     }
    2142          65 : }
    2143             : 
    2144             : #endif  // #ifdef ENABLE_GPKG_OGR_CONTENTS
    2145             : 
    2146             : /************************************************************************/
    2147             : /*                      CheckGeometryType()                             */
    2148             : /************************************************************************/
    2149             : 
    2150             : /** Check that the feature geometry type is consistent with the layer geometry
    2151             :  * type.
    2152             :  *
    2153             :  * And potentially update the Z and M flags of gpkg_geometry_columns to
    2154             :  * reflect the dimensionality of feature geometries.
    2155             :  */
    2156      266556 : void OGRGeoPackageTableLayer::CheckGeometryType(const OGRFeature *poFeature)
    2157             : {
    2158      266556 :     const OGRwkbGeometryType eLayerGeomType = GetGeomType();
    2159      266556 :     const OGRwkbGeometryType eFlattenLayerGeomType = wkbFlatten(eLayerGeomType);
    2160      266556 :     const OGRGeometry *poGeom = poFeature->GetGeometryRef();
    2161      266556 :     if (eFlattenLayerGeomType != wkbNone && eFlattenLayerGeomType != wkbUnknown)
    2162             :     {
    2163       15250 :         if (poGeom != nullptr)
    2164             :         {
    2165             :             OGRwkbGeometryType eGeomType =
    2166       15097 :                 wkbFlatten(poGeom->getGeometryType());
    2167       15113 :             if (!OGR_GT_IsSubClassOf(eGeomType, eFlattenLayerGeomType) &&
    2168          16 :                 !cpl::contains(m_eSetBadGeomTypeWarned, eGeomType))
    2169             :             {
    2170          15 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2171             :                          "A geometry of type %s is inserted into layer %s "
    2172             :                          "of geometry type %s, which is not normally allowed "
    2173             :                          "by the GeoPackage specification, but the driver will "
    2174             :                          "however do it. "
    2175             :                          "To create a conformant GeoPackage, if using ogr2ogr, "
    2176             :                          "the -nlt option can be used to override the layer "
    2177             :                          "geometry type. "
    2178             :                          "This warning will no longer be emitted for this "
    2179             :                          "combination of layer and feature geometry type.",
    2180             :                          OGRToOGCGeomType(eGeomType), GetName(),
    2181             :                          OGRToOGCGeomType(eFlattenLayerGeomType));
    2182          15 :                 m_eSetBadGeomTypeWarned.insert(eGeomType);
    2183             :             }
    2184             :         }
    2185             :     }
    2186             : 
    2187             :     // Make sure to update the z and m columns of gpkg_geometry_columns to 2
    2188             :     // if we have geometries with Z and M components
    2189      266556 :     if (m_nZFlag == 0 || m_nMFlag == 0)
    2190             :     {
    2191      266552 :         if (poGeom != nullptr)
    2192             :         {
    2193      261384 :             bool bUpdateGpkgGeometryColumnsTable = false;
    2194      261384 :             const OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
    2195      261384 :             if (m_nZFlag == 0 && wkbHasZ(eGeomType))
    2196             :             {
    2197          11 :                 if (eLayerGeomType != wkbUnknown && !wkbHasZ(eLayerGeomType))
    2198             :                 {
    2199           2 :                     CPLError(
    2200             :                         CE_Warning, CPLE_AppDefined,
    2201             :                         "Layer '%s' has been declared with non-Z geometry type "
    2202             :                         "%s, but it does contain geometries with Z. Setting "
    2203             :                         "the Z=2 hint into gpkg_geometry_columns",
    2204             :                         GetName(),
    2205             :                         OGRToOGCGeomType(eLayerGeomType, true, true, true));
    2206             :                 }
    2207          11 :                 m_nZFlag = 2;
    2208          11 :                 bUpdateGpkgGeometryColumnsTable = true;
    2209             :             }
    2210      261384 :             if (m_nMFlag == 0 && wkbHasM(eGeomType))
    2211             :             {
    2212           8 :                 if (eLayerGeomType != wkbUnknown && !wkbHasM(eLayerGeomType))
    2213             :                 {
    2214           1 :                     CPLError(
    2215             :                         CE_Warning, CPLE_AppDefined,
    2216             :                         "Layer '%s' has been declared with non-M geometry type "
    2217             :                         "%s, but it does contain geometries with M. Setting "
    2218             :                         "the M=2 hint into gpkg_geometry_columns",
    2219             :                         GetName(),
    2220             :                         OGRToOGCGeomType(eLayerGeomType, true, true, true));
    2221             :                 }
    2222           8 :                 m_nMFlag = 2;
    2223           8 :                 bUpdateGpkgGeometryColumnsTable = true;
    2224             :             }
    2225      261384 :             if (bUpdateGpkgGeometryColumnsTable)
    2226             :             {
    2227             :                 /* Update gpkg_geometry_columns */
    2228          14 :                 char *pszSQL = sqlite3_mprintf(
    2229             :                     "UPDATE gpkg_geometry_columns SET z = %d, m = %d WHERE "
    2230             :                     "table_name = '%q' AND column_name = '%q'",
    2231             :                     m_nZFlag, m_nMFlag, GetName(), GetGeometryColumn());
    2232          14 :                 CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
    2233          14 :                 sqlite3_free(pszSQL);
    2234             :             }
    2235             :         }
    2236             :     }
    2237      266556 : }
    2238             : 
    2239             : /************************************************************************/
    2240             : /*                   CheckFIDAndFIDColumnConsistency()                  */
    2241             : /************************************************************************/
    2242             : 
    2243          16 : static bool CheckFIDAndFIDColumnConsistency(const OGRFeature *poFeature,
    2244             :                                             int iFIDAsRegularColumnIndex)
    2245             : {
    2246          16 :     bool ok = true;
    2247          16 :     if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
    2248             :     {
    2249             :         // nothing to do
    2250             :     }
    2251          14 :     else if (poFeature->GetDefnRef()
    2252          14 :                  ->GetFieldDefn(iFIDAsRegularColumnIndex)
    2253          14 :                  ->GetType() == OFTReal)
    2254             :     {
    2255             :         const double dfFID =
    2256           4 :             poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex);
    2257           4 :         if (GDALIsValueInRange<int64_t>(dfFID))
    2258             :         {
    2259           4 :             const auto nFID = static_cast<GIntBig>(dfFID);
    2260           4 :             if (nFID != poFeature->GetFID())
    2261             :             {
    2262           1 :                 ok = false;
    2263           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2264             :                          "Inconsistent values of FID (" CPL_FRMT_GIB
    2265             :                          ") and field of same name (%g)",
    2266             :                          poFeature->GetFID(),
    2267             :                          poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex));
    2268             :             }
    2269             :         }
    2270             :     }
    2271          20 :     else if (poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
    2272          10 :              poFeature->GetFID())
    2273             :     {
    2274           3 :         ok = false;
    2275           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    2276             :                  "Inconsistent values of FID (" CPL_FRMT_GIB
    2277             :                  ") and field of same name (" CPL_FRMT_GIB ")",
    2278             :                  poFeature->GetFID(),
    2279             :                  poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
    2280             :     }
    2281          16 :     return ok;
    2282             : }
    2283             : 
    2284             : /************************************************************************/
    2285             : /*                      ICreateFeature()                                 */
    2286             : /************************************************************************/
    2287             : 
    2288             : // rtreeValueDown() / rtreeValueUp() come from SQLite3 source code
    2289             : // SQLite3 RTree stores min/max values as float. So do the same for our
    2290             : // GPKGRTreeEntry
    2291             : 
    2292             : /*
    2293             : ** Rounding constants for float->double conversion.
    2294             : */
    2295             : #define RNDTOWARDS (1.0 - 1.0 / 8388608.0) /* Round towards zero */
    2296             : #define RNDAWAY (1.0 + 1.0 / 8388608.0)    /* Round away from zero */
    2297             : 
    2298             : /*
    2299             : ** Convert an sqlite3_value into an RtreeValue (presumably a float)
    2300             : ** while taking care to round toward negative or positive, respectively.
    2301             : */
    2302      490690 : static float rtreeValueDown(double d)
    2303             : {
    2304      490690 :     float f = static_cast<float>(d);
    2305      490690 :     if (f > d)
    2306             :     {
    2307       12835 :         f = static_cast<float>(d * (d < 0 ? RNDAWAY : RNDTOWARDS));
    2308             :     }
    2309      490690 :     return f;
    2310             : }
    2311             : 
    2312      490690 : static float rtreeValueUp(double d)
    2313             : {
    2314      490690 :     float f = static_cast<float>(d);
    2315      490690 :     if (f < d)
    2316             :     {
    2317       12943 :         f = static_cast<float>(d * (d < 0 ? RNDTOWARDS : RNDAWAY));
    2318             :     }
    2319      490690 :     return f;
    2320             : }
    2321             : 
    2322      266488 : OGRErr OGRGeoPackageTableLayer::CreateOrUpsertFeature(OGRFeature *poFeature,
    2323             :                                                       bool bUpsert)
    2324             : {
    2325      266488 :     if (!m_bFeatureDefnCompleted)
    2326           0 :         GetLayerDefn();
    2327      266488 :     if (!m_poDS->GetUpdate())
    2328             :     {
    2329           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2330             :                  "CreateFeature");
    2331           0 :         return OGRERR_FAILURE;
    2332             :     }
    2333             : 
    2334      266488 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    2335           0 :         return OGRERR_FAILURE;
    2336             : 
    2337      266488 :     CancelAsyncNextArrowArray();
    2338             : 
    2339      532976 :     std::string osUpsertUniqueColumnName;
    2340      266488 :     if (bUpsert && poFeature->GetFID() == OGRNullFID)
    2341             :     {
    2342          14 :         int nUniqueColumns = 0;
    2343          14 :         const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    2344          42 :         for (int i = 0; i < nFieldCount; ++i)
    2345             :         {
    2346          28 :             const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    2347          28 :             if (poFieldDefn->IsUnique())
    2348             :             {
    2349          14 :                 if (osUpsertUniqueColumnName.empty())
    2350          14 :                     osUpsertUniqueColumnName = poFieldDefn->GetNameRef();
    2351          14 :                 nUniqueColumns++;
    2352             :             }
    2353             :         }
    2354          14 :         if (nUniqueColumns == 0)
    2355             :         {
    2356             :             // This is just a regular INSERT
    2357           0 :             bUpsert = false;
    2358             :         }
    2359             :     }
    2360             : 
    2361      266488 :     if (bUpsert)
    2362             :     {
    2363          26 :         if (m_bThreadRTreeStarted)
    2364           0 :             CancelAsyncRTree();
    2365          26 :         if (!RunDeferredSpatialIndexUpdate())
    2366           0 :             return OGRERR_FAILURE;
    2367          26 :         if (!m_bUpdate1TriggerDisabled && HasSpatialIndex())
    2368          18 :             WorkaroundUpdate1TriggerIssue();
    2369             :     }
    2370             : 
    2371             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    2372      266488 :     if (bUpsert)
    2373             :     {
    2374          26 :         if (m_nTotalFeatureCount >= 0)
    2375             :         {
    2376             :             // There's no reliable way of knowing if a new row has been inserted
    2377             :             // or just updated, so serialize known value and then
    2378             :             // invalidate feature count.
    2379          10 :             if (m_poDS->m_bHasGPKGOGRContents)
    2380             :             {
    2381             :                 const char *pszCount =
    2382          10 :                     CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
    2383          10 :                 char *pszSQL = sqlite3_mprintf(
    2384             :                     "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
    2385             :                     "lower(table_name )= lower('%q')",
    2386             :                     pszCount, m_pszTableName);
    2387          10 :                 SQLCommand(m_poDS->GetDB(), pszSQL);
    2388          10 :                 sqlite3_free(pszSQL);
    2389             :             }
    2390          10 :             m_nTotalFeatureCount = -1;
    2391             : 
    2392          10 :             if (!m_bOGRFeatureCountTriggersEnabled)
    2393           1 :                 CreateFeatureCountTriggers();
    2394             :         }
    2395             :     }
    2396             :     else
    2397             :     {
    2398             :         // To maximize performance of insertion, disable feature count triggers
    2399      266462 :         if (m_bOGRFeatureCountTriggersEnabled)
    2400             :         {
    2401          39 :             DisableFeatureCountTriggers();
    2402             :         }
    2403             :     }
    2404             : #endif
    2405             : 
    2406      266488 :     CheckGeometryType(poFeature);
    2407             : 
    2408             :     /* Substitute default values for null Date/DateTime fields as the standard
    2409             :      */
    2410             :     /* format of SQLite is not the one mandated by GeoPackage */
    2411      266488 :     poFeature->FillUnsetWithDefault(FALSE, nullptr);
    2412      266488 :     bool bHasDefaultValue = false;
    2413      266488 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    2414     4734520 :     for (int iField = 0; iField < nFieldCount; iField++)
    2415             :     {
    2416     4468030 :         if (poFeature->IsFieldSetUnsafe(iField))
    2417     4466790 :             continue;
    2418             :         const char *pszDefault =
    2419        1237 :             m_poFeatureDefn->GetFieldDefnUnsafe(iField)->GetDefault();
    2420        1237 :         if (pszDefault != nullptr)
    2421             :         {
    2422           1 :             bHasDefaultValue = true;
    2423             :         }
    2424             :     }
    2425             : 
    2426             :     /* In case the FID column has also been created as a regular field */
    2427      266488 :     if (m_iFIDAsRegularColumnIndex >= 0)
    2428             :     {
    2429          13 :         if (poFeature->GetFID() == OGRNullFID)
    2430             :         {
    2431           6 :             if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
    2432             :             {
    2433          10 :                 if (m_poFeatureDefn->GetFieldDefn(m_iFIDAsRegularColumnIndex)
    2434           5 :                         ->GetType() == OFTReal)
    2435             :                 {
    2436           2 :                     bool ok = false;
    2437             :                     const double dfFID =
    2438           2 :                         poFeature->GetFieldAsDouble(m_iFIDAsRegularColumnIndex);
    2439           4 :                     if (dfFID >= static_cast<double>(
    2440           4 :                                      std::numeric_limits<int64_t>::min()) &&
    2441           2 :                         dfFID <= static_cast<double>(
    2442           2 :                                      std::numeric_limits<int64_t>::max()))
    2443             :                     {
    2444           2 :                         const auto nFID = static_cast<GIntBig>(dfFID);
    2445           2 :                         if (static_cast<double>(nFID) == dfFID)
    2446             :                         {
    2447           1 :                             poFeature->SetFID(nFID);
    2448           1 :                             ok = true;
    2449             :                         }
    2450             :                     }
    2451           2 :                     if (!ok)
    2452             :                     {
    2453           1 :                         CPLError(
    2454             :                             CE_Failure, CPLE_AppDefined,
    2455             :                             "Value of FID %g cannot be parsed to an Integer64",
    2456             :                             dfFID);
    2457           1 :                         return OGRERR_FAILURE;
    2458             :                     }
    2459             :                 }
    2460             :                 else
    2461             :                 {
    2462           3 :                     poFeature->SetFID(poFeature->GetFieldAsInteger64(
    2463           3 :                         m_iFIDAsRegularColumnIndex));
    2464             :                 }
    2465             :             }
    2466             :         }
    2467           7 :         else if (!CheckFIDAndFIDColumnConsistency(poFeature,
    2468             :                                                   m_iFIDAsRegularColumnIndex))
    2469             :         {
    2470           3 :             return OGRERR_FAILURE;
    2471             :         }
    2472             :     }
    2473             : 
    2474             :     /* If there's a unset field with a default value, then we must create */
    2475             :     /* a specific INSERT statement to avoid unset fields to be bound to NULL */
    2476      532278 :     if (m_poInsertStatement &&
    2477      265794 :         (bHasDefaultValue ||
    2478      265794 :          m_bInsertStatementWithFID != (poFeature->GetFID() != OGRNullFID) ||
    2479      531584 :          m_bInsertStatementWithUpsert != bUpsert ||
    2480      265792 :          m_osInsertStatementUpsertUniqueColumnName != osUpsertUniqueColumnName))
    2481             :     {
    2482           2 :         sqlite3_finalize(m_poInsertStatement);
    2483           2 :         m_poInsertStatement = nullptr;
    2484             :     }
    2485             : 
    2486      266484 :     if (!m_poInsertStatement)
    2487             :     {
    2488             :         /* Construct a SQL INSERT statement from the OGRFeature */
    2489             :         /* Only work with fields that are set */
    2490             :         /* Do not stick values into SQL, use placeholder and bind values later
    2491             :          */
    2492         692 :         m_bInsertStatementWithFID = poFeature->GetFID() != OGRNullFID;
    2493         692 :         m_bInsertStatementWithUpsert = bUpsert;
    2494         692 :         m_osInsertStatementUpsertUniqueColumnName = osUpsertUniqueColumnName;
    2495             :         CPLString osCommand = FeatureGenerateInsertSQL(
    2496         692 :             poFeature, m_bInsertStatementWithFID, !bHasDefaultValue, bUpsert,
    2497         692 :             osUpsertUniqueColumnName);
    2498             : 
    2499             :         /* Prepare the SQL into a statement */
    2500         692 :         sqlite3 *poDb = m_poDS->GetDB();
    2501         692 :         int err = SQLPrepareWithError(poDb, osCommand, -1, &m_poInsertStatement,
    2502             :                                       nullptr);
    2503         692 :         if (err != SQLITE_OK)
    2504             :         {
    2505           0 :             return OGRERR_FAILURE;
    2506             :         }
    2507             :     }
    2508             : 
    2509             :     /* Bind values onto the statement now */
    2510      532968 :     OGRErr errOgr = FeatureBindInsertParameters(poFeature, m_poInsertStatement,
    2511      266484 :                                                 m_bInsertStatementWithFID,
    2512      266484 :                                                 !bHasDefaultValue);
    2513      266484 :     if (errOgr != OGRERR_NONE)
    2514             :     {
    2515           0 :         sqlite3_reset(m_poInsertStatement);
    2516           0 :         sqlite3_clear_bindings(m_poInsertStatement);
    2517           0 :         sqlite3_finalize(m_poInsertStatement);
    2518           0 :         m_poInsertStatement = nullptr;
    2519           0 :         return errOgr;
    2520             :     }
    2521             : 
    2522             :     /* From here execute the statement and check errors */
    2523      266484 :     const int err = sqlite3_step(m_poInsertStatement);
    2524      266484 :     if (!(err == SQLITE_OK || err == SQLITE_DONE
    2525             : #if SQLITE_VERSION_NUMBER >= 3035000L
    2526             :           || err == SQLITE_ROW
    2527             : #endif
    2528             :           ))
    2529             :     {
    2530           8 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to execute insert : %s",
    2531           8 :                  sqlite3_errmsg(m_poDS->GetDB())
    2532           8 :                      ? sqlite3_errmsg(m_poDS->GetDB())
    2533             :                      : "");
    2534           8 :         sqlite3_reset(m_poInsertStatement);
    2535           8 :         sqlite3_clear_bindings(m_poInsertStatement);
    2536           8 :         sqlite3_finalize(m_poInsertStatement);
    2537           8 :         m_poInsertStatement = nullptr;
    2538           8 :         return OGRERR_FAILURE;
    2539             :     }
    2540             : 
    2541             :     /* Read the latest FID value */
    2542          26 :     const GIntBig nFID = (bUpsert && !osUpsertUniqueColumnName.empty())
    2543      266476 :                              ?
    2544             : #if SQLITE_VERSION_NUMBER >= 3035000L
    2545          14 :                              sqlite3_column_int64(m_poInsertStatement, 0)
    2546             : #else
    2547             :                              OGRNullFID
    2548             : #endif
    2549      266462 :                              : sqlite3_last_insert_rowid(m_poDS->GetDB());
    2550             : 
    2551      266476 :     sqlite3_reset(m_poInsertStatement);
    2552      266476 :     sqlite3_clear_bindings(m_poInsertStatement);
    2553             : 
    2554      266476 :     if (bHasDefaultValue)
    2555             :     {
    2556           1 :         sqlite3_finalize(m_poInsertStatement);
    2557           1 :         m_poInsertStatement = nullptr;
    2558             :     }
    2559             : 
    2560      266476 :     if (nFID != OGRNullFID)
    2561             :     {
    2562      266476 :         poFeature->SetFID(nFID);
    2563      266476 :         if (m_iFIDAsRegularColumnIndex >= 0)
    2564           9 :             poFeature->SetField(m_iFIDAsRegularColumnIndex, nFID);
    2565             :     }
    2566             :     else
    2567             :     {
    2568           0 :         poFeature->SetFID(OGRNullFID);
    2569             :     }
    2570             : 
    2571             :     /* Update the layer extents with this new object */
    2572      266476 :     if (IsGeomFieldSet(poFeature))
    2573             :     {
    2574      261341 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
    2575      261341 :         if (!poGeom->IsEmpty())
    2576             :         {
    2577      260314 :             OGREnvelope oEnv;
    2578      260314 :             poGeom->getEnvelope(&oEnv);
    2579      260314 :             UpdateExtent(&oEnv);
    2580             : 
    2581      260300 :             if (!bUpsert && !m_bDeferredSpatialIndexCreation &&
    2582      520614 :                 HasSpatialIndex() && m_poDS->IsInTransaction())
    2583             :             {
    2584         326 :                 m_nCountInsertInTransaction++;
    2585         326 :                 if (m_nCountInsertInTransactionThreshold < 0)
    2586             :                 {
    2587          11 :                     m_nCountInsertInTransactionThreshold =
    2588          11 :                         atoi(CPLGetConfigOption(
    2589             :                             "OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "100"));
    2590             :                 }
    2591         326 :                 if (m_nCountInsertInTransaction ==
    2592         326 :                     m_nCountInsertInTransactionThreshold)
    2593             :                 {
    2594           7 :                     StartDeferredSpatialIndexUpdate();
    2595             :                 }
    2596         319 :                 else if (!m_aoRTreeTriggersSQL.empty())
    2597             :                 {
    2598         187 :                     if (m_aoRTreeEntries.size() == 1000 * 1000)
    2599             :                     {
    2600           0 :                         if (!FlushPendingSpatialIndexUpdate())
    2601           0 :                             return OGRERR_FAILURE;
    2602             :                     }
    2603             :                     GPKGRTreeEntry sEntry;
    2604         187 :                     sEntry.nId = nFID;
    2605         187 :                     sEntry.fMinX = rtreeValueDown(oEnv.MinX);
    2606         187 :                     sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
    2607         187 :                     sEntry.fMinY = rtreeValueDown(oEnv.MinY);
    2608         187 :                     sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
    2609         187 :                     m_aoRTreeEntries.push_back(sEntry);
    2610             :                 }
    2611             :             }
    2612      259988 :             else if (!bUpsert && m_bAllowedRTreeThread &&
    2613      248945 :                      !m_bErrorDuringRTreeThread)
    2614             :             {
    2615             :                 GPKGRTreeEntry sEntry;
    2616             : #ifdef DEBUG_VERBOSE
    2617             :                 if (m_aoRTreeEntries.empty())
    2618             :                     CPLDebug("GPKG",
    2619             :                              "Starting to fill m_aoRTreeEntries at "
    2620             :                              "FID " CPL_FRMT_GIB,
    2621             :                              nFID);
    2622             : #endif
    2623      245158 :                 sEntry.nId = nFID;
    2624      245158 :                 sEntry.fMinX = rtreeValueDown(oEnv.MinX);
    2625      245158 :                 sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
    2626      245158 :                 sEntry.fMinY = rtreeValueDown(oEnv.MinY);
    2627      245158 :                 sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
    2628             :                 try
    2629             :                 {
    2630      245158 :                     m_aoRTreeEntries.push_back(sEntry);
    2631      245158 :                     if (m_aoRTreeEntries.size() == m_nRTreeBatchSize)
    2632             :                     {
    2633         877 :                         m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
    2634         877 :                         m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
    2635             :                     }
    2636      482211 :                     if (!m_bThreadRTreeStarted &&
    2637      237053 :                         m_oQueueRTreeEntries.size() ==
    2638      237053 :                             m_nRTreeBatchesBeforeStart)
    2639             :                     {
    2640          48 :                         StartAsyncRTree();
    2641             :                     }
    2642             :                 }
    2643           0 :                 catch (const std::bad_alloc &)
    2644             :                 {
    2645           0 :                     CPLDebug("GPKG",
    2646             :                              "Memory allocation error regarding RTree "
    2647             :                              "structures. Falling back to slower method");
    2648           0 :                     if (m_bThreadRTreeStarted)
    2649           0 :                         CancelAsyncRTree();
    2650             :                     else
    2651           0 :                         m_bAllowedRTreeThread = false;
    2652             :                 }
    2653             :             }
    2654             :         }
    2655             :     }
    2656             : 
    2657             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    2658      266476 :     if (m_nTotalFeatureCount >= 0)
    2659             :     {
    2660      266410 :         if (m_nTotalFeatureCount < std::numeric_limits<int64_t>::max())
    2661             :         {
    2662      266409 :             m_nTotalFeatureCount++;
    2663             :         }
    2664             :         else
    2665             :         {
    2666           1 :             if (m_poDS->m_bHasGPKGOGRContents)
    2667             :             {
    2668           1 :                 char *pszSQL = sqlite3_mprintf(
    2669             :                     "UPDATE gpkg_ogr_contents SET feature_count = null "
    2670             :                     "WHERE lower(table_name) = lower('%q')",
    2671             :                     m_pszTableName);
    2672           1 :                 CPL_IGNORE_RET_VAL(sqlite3_exec(m_poDS->hDB, pszSQL, nullptr,
    2673             :                                                 nullptr, nullptr));
    2674           1 :                 sqlite3_free(pszSQL);
    2675             :             }
    2676           1 :             m_nTotalFeatureCount = -1;
    2677             :         }
    2678             :     }
    2679             : #endif
    2680             : 
    2681      266476 :     m_bContentChanged = true;
    2682             : 
    2683             :     /* All done! */
    2684      266476 :     return OGRERR_NONE;
    2685             : }
    2686             : 
    2687      266462 : OGRErr OGRGeoPackageTableLayer::ICreateFeature(OGRFeature *poFeature)
    2688             : {
    2689      266462 :     return CreateOrUpsertFeature(poFeature, /* bUpsert=*/false);
    2690             : }
    2691             : 
    2692             : /************************************************************************/
    2693             : /*                  SetDeferredSpatialIndexCreation()                   */
    2694             : /************************************************************************/
    2695             : 
    2696         729 : void OGRGeoPackageTableLayer::SetDeferredSpatialIndexCreation(bool bFlag)
    2697             : {
    2698         729 :     m_bDeferredSpatialIndexCreation = bFlag;
    2699         729 :     if (bFlag)
    2700             :     {
    2701             :         // This method is invoked before the layer is added to the dataset,
    2702             :         // so GetLayerCount() will return 0 for the first layer added.
    2703         729 :         m_bAllowedRTreeThread =
    2704        1376 :             m_poDS->GetLayerCount() == 0 && sqlite3_threadsafe() != 0 &&
    2705        2023 :             CPLGetNumCPUs() >= 2 &&
    2706         647 :             CPLTestBool(
    2707             :                 CPLGetConfigOption("OGR_GPKG_ALLOW_THREADED_RTREE", "YES"));
    2708             : 
    2709             :         // For unit tests
    2710         729 :         if (CPLTestBool(CPLGetConfigOption(
    2711             :                 "OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "NO")))
    2712             :         {
    2713          60 :             m_nRTreeBatchSize = 10;
    2714          60 :             m_nRTreeBatchesBeforeStart = 1;
    2715             :         }
    2716             :     }
    2717         729 : }
    2718             : 
    2719             : /************************************************************************/
    2720             : /*                          StartAsyncRTree()                           */
    2721             : /************************************************************************/
    2722             : 
    2723             : // We create a temporary database with only the RTree, and we insert
    2724             : // records into it in a dedicated thread, in parallel of the main thread
    2725             : // that inserts rows in the user table. When the layer is finalized, we
    2726             : // just use bulk copy statements of the form
    2727             : // INSERT INTO rtree_xxxx_rowid/node/parent SELECT * FROM
    2728             : // temp_rtree.my_rtree_rowid/node/parent to copy the RTree auxiliary tables into
    2729             : // the main database, which is a very fast operation.
    2730             : 
    2731          48 : void OGRGeoPackageTableLayer::StartAsyncRTree()
    2732             : {
    2733          48 :     m_osAsyncDBName = m_poDS->GetDescription();
    2734          48 :     m_osAsyncDBName += ".tmp_rtree_";
    2735          48 :     bool bCanUseTableName = false;
    2736          48 :     if (strlen(m_pszTableName) <= 32)
    2737             :     {
    2738          36 :         bCanUseTableName = true;
    2739          36 :         constexpr char DIGIT_ZERO = '0';
    2740         144 :         for (int i = 0; m_pszTableName[i] != '\0'; ++i)
    2741             :         {
    2742         120 :             const char ch = m_pszTableName[i];
    2743         132 :             if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
    2744          12 :                   (ch >= DIGIT_ZERO && ch <= '9') || ch == '.' || ch == '_'))
    2745             :             {
    2746          12 :                 bCanUseTableName = false;
    2747          12 :                 break;
    2748             :             }
    2749             :         }
    2750             :     }
    2751          48 :     if (bCanUseTableName)
    2752          24 :         m_osAsyncDBName += m_pszTableName;
    2753             :     else
    2754             :     {
    2755          24 :         m_osAsyncDBName += CPLMD5String(m_pszTableName);
    2756             :     }
    2757          48 :     m_osAsyncDBName += ".db";
    2758             : 
    2759          48 :     m_osAsyncDBAttachName = "temp_rtree_";
    2760          48 :     m_osAsyncDBAttachName += CPLMD5String(m_pszTableName);
    2761             : 
    2762          48 :     VSIUnlink(m_osAsyncDBName.c_str());
    2763          48 :     CPLDebug("GPKG", "Creating background RTree DB %s",
    2764             :              m_osAsyncDBName.c_str());
    2765          72 :     if (sqlite3_open_v2(m_osAsyncDBName.c_str(), &m_hAsyncDBHandle,
    2766             :                         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
    2767         120 :                         m_poDS->GetVFS() ? m_poDS->GetVFS()->zName : nullptr) !=
    2768             :         SQLITE_OK)
    2769             :     {
    2770           0 :         CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_open_v2() of %s failed",
    2771             :                  m_osAsyncDBName.c_str());
    2772           0 :         sqlite3_close(m_hAsyncDBHandle);
    2773           0 :         m_hAsyncDBHandle = nullptr;
    2774             :     }
    2775          48 :     if (m_hAsyncDBHandle != nullptr)
    2776             :     {
    2777             :         /* Make sure our auxiliary DB has the same page size as the main one.
    2778             :          * Because the number of RTree cells depends on the SQLite page size.
    2779             :          * However the sqlite implementation limits to 51 cells maximum per page,
    2780             :          * which is reached starting with a page size of 2048 bytes.
    2781             :          * As the default SQLite page size is 4096 currently, having potentially
    2782             :          * different page sizes >= 4096 between the main and auxiliary DBs would
    2783             :          * not be a practical issue, but better be consistent.
    2784             :          */
    2785             :         const int nPageSize =
    2786          48 :             SQLGetInteger(m_poDS->GetDB(), "PRAGMA page_size", nullptr);
    2787             : 
    2788          48 :         if (SQLCommand(m_hAsyncDBHandle,
    2789             :                        CPLSPrintf("PRAGMA page_size = %d;\n"
    2790             :                                   "PRAGMA journal_mode = OFF;\n"
    2791             :                                   "PRAGMA synchronous = OFF;",
    2792          48 :                                   nPageSize)) == OGRERR_NONE)
    2793             :         {
    2794          48 :             char *pszSQL = sqlite3_mprintf("ATTACH DATABASE '%q' AS '%q'",
    2795             :                                            m_osAsyncDBName.c_str(),
    2796             :                                            m_osAsyncDBAttachName.c_str());
    2797          48 :             OGRErr eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    2798          48 :             sqlite3_free(pszSQL);
    2799             : 
    2800          48 :             if (eErr == OGRERR_NONE)
    2801             :             {
    2802          48 :                 m_hRTree = gdal_sqlite_rtree_bl_new(nPageSize);
    2803             :                 try
    2804             :                 {
    2805             :                     m_oThreadRTree =
    2806          96 :                         std::thread([this]() { AsyncRTreeThreadFunction(); });
    2807          48 :                     m_bThreadRTreeStarted = true;
    2808             :                 }
    2809           0 :                 catch (const std::exception &e)
    2810             :                 {
    2811           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2812           0 :                              "RTree thread cannot be created: %s", e.what());
    2813             :                 }
    2814             :             }
    2815             :         }
    2816             : 
    2817          48 :         if (!m_bThreadRTreeStarted)
    2818             :         {
    2819           0 :             if (m_hRTree)
    2820             :             {
    2821           0 :                 gdal_sqlite_rtree_bl_free(m_hRTree);
    2822           0 :                 m_hRTree = nullptr;
    2823             :             }
    2824           0 :             m_oQueueRTreeEntries.clear();
    2825           0 :             m_bErrorDuringRTreeThread = true;
    2826           0 :             sqlite3_close(m_hAsyncDBHandle);
    2827           0 :             m_hAsyncDBHandle = nullptr;
    2828           0 :             VSIUnlink(m_osAsyncDBName.c_str());
    2829             :         }
    2830             :     }
    2831             :     else
    2832             :     {
    2833           0 :         m_oQueueRTreeEntries.clear();
    2834           0 :         m_bErrorDuringRTreeThread = true;
    2835             :     }
    2836          48 : }
    2837             : 
    2838             : /************************************************************************/
    2839             : /*                        RemoveAsyncRTreeTempDB()                      */
    2840             : /************************************************************************/
    2841             : 
    2842          48 : void OGRGeoPackageTableLayer::RemoveAsyncRTreeTempDB()
    2843             : {
    2844          48 :     if (!m_osAsyncDBAttachName.empty())
    2845             :     {
    2846          96 :         SQLCommand(
    2847          48 :             m_poDS->GetDB(),
    2848             :             CPLSPrintf("DETACH DATABASE \"%s\"",
    2849          96 :                        SQLEscapeName(m_osAsyncDBAttachName.c_str()).c_str()));
    2850          48 :         m_osAsyncDBAttachName.clear();
    2851          48 :         VSIUnlink(m_osAsyncDBName.c_str());
    2852          48 :         m_osAsyncDBName.clear();
    2853             :     }
    2854          48 : }
    2855             : 
    2856             : /************************************************************************/
    2857             : /*                          CancelAsyncRTree()                          */
    2858             : /************************************************************************/
    2859             : 
    2860          36 : void OGRGeoPackageTableLayer::CancelAsyncRTree()
    2861             : {
    2862          36 :     CPLDebug("GPKG", "Cancel background RTree creation");
    2863          36 :     m_oQueueRTreeEntries.push({});
    2864          36 :     m_oThreadRTree.join();
    2865          36 :     m_bThreadRTreeStarted = false;
    2866          36 :     if (m_hAsyncDBHandle)
    2867             :     {
    2868          36 :         sqlite3_close(m_hAsyncDBHandle);
    2869          36 :         m_hAsyncDBHandle = nullptr;
    2870             :     }
    2871          36 :     gdal_sqlite_rtree_bl_free(m_hRTree);
    2872          36 :     m_hRTree = nullptr;
    2873          36 :     m_bErrorDuringRTreeThread = true;
    2874          36 :     RemoveAsyncRTreeTempDB();
    2875          36 : }
    2876             : 
    2877             : /************************************************************************/
    2878             : /*                     FinishOrDisableThreadedRTree()                   */
    2879             : /************************************************************************/
    2880             : 
    2881          78 : void OGRGeoPackageTableLayer::FinishOrDisableThreadedRTree()
    2882             : {
    2883          78 :     if (m_bThreadRTreeStarted)
    2884             :     {
    2885          12 :         CreateSpatialIndexIfNecessary();
    2886             :     }
    2887          78 :     m_bAllowedRTreeThread = false;
    2888          78 : }
    2889             : 
    2890             : /************************************************************************/
    2891             : /*                       FlushInMemoryRTree()                           */
    2892             : /************************************************************************/
    2893             : 
    2894          12 : bool OGRGeoPackageTableLayer::FlushInMemoryRTree(sqlite3 *hRTreeDB,
    2895             :                                                  const char *pszRTreeName)
    2896             : {
    2897          12 :     if (hRTreeDB == m_hAsyncDBHandle)
    2898           8 :         SQLCommand(hRTreeDB, "BEGIN");
    2899             : 
    2900          12 :     char *pszErrMsg = nullptr;
    2901          12 :     bool bRet = gdal_sqlite_rtree_bl_serialize(m_hRTree, hRTreeDB, pszRTreeName,
    2902             :                                                "id", "minx", "miny", "maxx",
    2903             :                                                "maxy", &pszErrMsg);
    2904          12 :     if (hRTreeDB == m_hAsyncDBHandle)
    2905             :     {
    2906           8 :         if (bRet)
    2907           8 :             bRet = SQLCommand(hRTreeDB, "COMMIT") == OGRERR_NONE;
    2908             :         else
    2909           0 :             SQLCommand(hRTreeDB, "ROLLBACK");
    2910             :     }
    2911             : 
    2912          12 :     gdal_sqlite_rtree_bl_free(m_hRTree);
    2913          12 :     m_hRTree = nullptr;
    2914             : 
    2915          12 :     if (!bRet)
    2916             :     {
    2917           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2918             :                  "sqlite_rtree_bl_serialize() failed with %s",
    2919           0 :                  pszErrMsg ? pszErrMsg : "(null)");
    2920             : 
    2921           0 :         m_bErrorDuringRTreeThread = true;
    2922             : 
    2923           0 :         if (m_hAsyncDBHandle)
    2924             :         {
    2925           0 :             sqlite3_close(m_hAsyncDBHandle);
    2926           0 :             m_hAsyncDBHandle = nullptr;
    2927             :         }
    2928             : 
    2929           0 :         m_oQueueRTreeEntries.clear();
    2930             :     }
    2931          12 :     sqlite3_free(pszErrMsg);
    2932             : 
    2933          12 :     return bRet;
    2934             : }
    2935             : 
    2936             : /************************************************************************/
    2937             : /*                     GetMaxRAMUsageAllowedForRTree()                  */
    2938             : /************************************************************************/
    2939             : 
    2940         776 : static size_t GetMaxRAMUsageAllowedForRTree()
    2941             : {
    2942         776 :     const uint64_t nUsableRAM = CPLGetUsablePhysicalRAM();
    2943         776 :     uint64_t nMaxRAMUsageAllowed =
    2944         776 :         (nUsableRAM ? nUsableRAM / 10 : 100 * 1024 * 1024);
    2945             :     const char *pszMaxRAMUsageAllowed =
    2946         776 :         CPLGetConfigOption("OGR_GPKG_MAX_RAM_USAGE_RTREE", nullptr);
    2947         776 :     if (pszMaxRAMUsageAllowed)
    2948             :     {
    2949             :         nMaxRAMUsageAllowed = static_cast<uint64_t>(
    2950          20 :             std::strtoull(pszMaxRAMUsageAllowed, nullptr, 10));
    2951             :     }
    2952         776 :     if (nMaxRAMUsageAllowed > std::numeric_limits<size_t>::max() - 1U)
    2953             :     {
    2954           0 :         nMaxRAMUsageAllowed = std::numeric_limits<size_t>::max() - 1U;
    2955             :     }
    2956         776 :     return static_cast<size_t>(nMaxRAMUsageAllowed);
    2957             : }
    2958             : 
    2959             : /************************************************************************/
    2960             : /*                      AsyncRTreeThreadFunction()                      */
    2961             : /************************************************************************/
    2962             : 
    2963          48 : void OGRGeoPackageTableLayer::AsyncRTreeThreadFunction()
    2964             : {
    2965          48 :     CPLAssert(m_hRTree);
    2966             : 
    2967          48 :     const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
    2968          48 :     sqlite3_stmt *hStmt = nullptr;
    2969          48 :     GIntBig nCount = 0;
    2970             :     while (true)
    2971             :     {
    2972         884 :         const auto aoEntries = m_oQueueRTreeEntries.get_and_pop_front();
    2973         884 :         if (aoEntries.empty())
    2974          44 :             break;
    2975             : 
    2976         840 :         constexpr int NOTIFICATION_INTERVAL = 500 * 1000;
    2977             : 
    2978         840 :         auto oIter = aoEntries.begin();
    2979         840 :         if (m_hRTree)
    2980             :         {
    2981        4808 :             for (; oIter != aoEntries.end(); ++oIter)
    2982             :             {
    2983        4372 :                 const auto &entry = *oIter;
    2984        4372 :                 if (gdal_sqlite_rtree_bl_ram_usage(m_hRTree) >
    2985        8736 :                         nMaxRAMUsageAllowed ||
    2986        4364 :                     !gdal_sqlite_rtree_bl_insert(m_hRTree, entry.nId,
    2987        4364 :                                                  entry.fMinX, entry.fMinY,
    2988        4364 :                                                  entry.fMaxX, entry.fMaxY))
    2989             :                 {
    2990           8 :                     CPLDebug("GPKG", "Too large in-memory RTree. "
    2991             :                                      "Flushing it and using memory friendly "
    2992             :                                      "algorithm for the rest");
    2993           8 :                     if (!FlushInMemoryRTree(m_hAsyncDBHandle, "my_rtree"))
    2994           0 :                         return;
    2995           8 :                     break;
    2996             :                 }
    2997        4364 :                 ++nCount;
    2998        4364 :                 if ((nCount % NOTIFICATION_INTERVAL) == 0)
    2999             :                 {
    3000           0 :                     CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree",
    3001             :                              nCount);
    3002             :                 }
    3003             :             }
    3004         444 :             if (oIter == aoEntries.end())
    3005         436 :                 continue;
    3006             :         }
    3007             : 
    3008         404 :         if (hStmt == nullptr)
    3009             :         {
    3010             :             const char *pszInsertSQL =
    3011           8 :                 CPLGetConfigOption(
    3012             :                     "OGR_GPKG_SIMULATE_INSERT_INTO_MY_RTREE_PREPARATION_ERROR",
    3013             :                     nullptr)
    3014           8 :                     ? "INSERT INTO my_rtree_SIMULATE_ERROR VALUES (?,?,?,?,?)"
    3015           8 :                     : "INSERT INTO my_rtree VALUES (?,?,?,?,?)";
    3016           8 :             if (SQLPrepareWithError(m_hAsyncDBHandle, pszInsertSQL, -1, &hStmt,
    3017           8 :                                     nullptr) != SQLITE_OK)
    3018             :             {
    3019           4 :                 m_bErrorDuringRTreeThread = true;
    3020             : 
    3021           4 :                 sqlite3_close(m_hAsyncDBHandle);
    3022           4 :                 m_hAsyncDBHandle = nullptr;
    3023             : 
    3024           4 :                 m_oQueueRTreeEntries.clear();
    3025           4 :                 return;
    3026             :             }
    3027             : 
    3028           4 :             SQLCommand(m_hAsyncDBHandle, "BEGIN");
    3029             :         }
    3030             : 
    3031             : #ifdef DEBUG_VERBOSE
    3032             :         CPLDebug("GPKG",
    3033             :                  "AsyncRTreeThreadFunction(): "
    3034             :                  "Processing batch of %d features, "
    3035             :                  "starting at FID " CPL_FRMT_GIB " and ending "
    3036             :                  "at FID " CPL_FRMT_GIB,
    3037             :                  static_cast<int>(aoEntries.size()), aoEntries.front().nId,
    3038             :                  aoEntries.back().nId);
    3039             : #endif
    3040        4398 :         for (; oIter != aoEntries.end(); ++oIter)
    3041             :         {
    3042        3998 :             const auto &entry = *oIter;
    3043        3998 :             sqlite3_reset(hStmt);
    3044             : 
    3045        3998 :             sqlite3_bind_int64(hStmt, 1, entry.nId);
    3046        3998 :             sqlite3_bind_double(hStmt, 2, entry.fMinX);
    3047        3998 :             sqlite3_bind_double(hStmt, 3, entry.fMaxX);
    3048        3998 :             sqlite3_bind_double(hStmt, 4, entry.fMinY);
    3049        3998 :             sqlite3_bind_double(hStmt, 5, entry.fMaxY);
    3050        3998 :             int sqlite_err = sqlite3_step(hStmt);
    3051        3998 :             if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
    3052             :             {
    3053           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3054             :                          "failed to execute insertion in RTree : %s",
    3055             :                          sqlite3_errmsg(m_hAsyncDBHandle));
    3056           0 :                 m_bErrorDuringRTreeThread = true;
    3057           0 :                 break;
    3058             :             }
    3059        3998 :             ++nCount;
    3060        3998 :             if ((nCount % NOTIFICATION_INTERVAL) == 0)
    3061             :             {
    3062           0 :                 CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree", nCount);
    3063           0 :                 if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
    3064             :                 {
    3065           0 :                     m_bErrorDuringRTreeThread = true;
    3066           0 :                     break;
    3067             :                 }
    3068           0 :                 SQLCommand(m_hAsyncDBHandle, "BEGIN");
    3069             :             }
    3070             :         }
    3071         836 :     }
    3072          44 :     if (!m_hRTree)
    3073             :     {
    3074           4 :         if (m_bErrorDuringRTreeThread)
    3075             :         {
    3076           0 :             SQLCommand(m_hAsyncDBHandle, "ROLLBACK");
    3077             :         }
    3078           4 :         else if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
    3079             :         {
    3080           0 :             m_bErrorDuringRTreeThread = true;
    3081             :         }
    3082             : 
    3083           4 :         sqlite3_finalize(hStmt);
    3084             : 
    3085           4 :         if (m_bErrorDuringRTreeThread)
    3086             :         {
    3087           0 :             sqlite3_close(m_hAsyncDBHandle);
    3088           0 :             m_hAsyncDBHandle = nullptr;
    3089             : 
    3090           0 :             VSIUnlink(m_osAsyncDBName.c_str());
    3091             : 
    3092           0 :             m_oQueueRTreeEntries.clear();
    3093             :         }
    3094             :     }
    3095          44 :     CPLDebug("GPKG",
    3096             :              "AsyncRTreeThreadFunction(): " CPL_FRMT_GIB
    3097             :              " rows inserted into RTree",
    3098             :              nCount);
    3099             : }
    3100             : 
    3101             : /************************************************************************/
    3102             : /*                          ISetFeature()                                */
    3103             : /************************************************************************/
    3104             : 
    3105          58 : OGRErr OGRGeoPackageTableLayer::ISetFeature(OGRFeature *poFeature)
    3106             : {
    3107          58 :     if (!m_bFeatureDefnCompleted)
    3108           0 :         GetLayerDefn();
    3109          58 :     if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
    3110             :     {
    3111           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3112             :                  "SetFeature");
    3113           0 :         return OGRERR_FAILURE;
    3114             :     }
    3115             : 
    3116             :     /* No FID? */
    3117          58 :     if (poFeature->GetFID() == OGRNullFID)
    3118             :     {
    3119           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3120             :                  "FID required on features given to SetFeature().");
    3121           0 :         return OGRERR_FAILURE;
    3122             :     }
    3123             : 
    3124             :     /* In case the FID column has also been created as a regular field */
    3125          67 :     if (m_iFIDAsRegularColumnIndex >= 0 &&
    3126           9 :         !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
    3127             :     {
    3128           1 :         return OGRERR_FAILURE;
    3129             :     }
    3130             : 
    3131          57 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3132           0 :         return OGRERR_FAILURE;
    3133             : 
    3134          57 :     CancelAsyncNextArrowArray();
    3135             : 
    3136          57 :     if (m_bThreadRTreeStarted)
    3137          12 :         CancelAsyncRTree();
    3138          57 :     if (!RunDeferredSpatialIndexUpdate())
    3139           0 :         return OGRERR_FAILURE;
    3140             : 
    3141             :     const sqlite3_int64 nTotalChangesBefore =
    3142             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3143          57 :         sqlite3_total_changes64(m_poDS->GetDB());
    3144             : #else
    3145             :         sqlite3_total_changes(m_poDS->GetDB());
    3146             : #endif
    3147             : 
    3148          57 :     CheckGeometryType(poFeature);
    3149             : 
    3150          57 :     if (!m_osUpdateStatementSQL.empty())
    3151             :     {
    3152           1 :         m_osUpdateStatementSQL.clear();
    3153           1 :         if (m_poUpdateStatement)
    3154           1 :             sqlite3_finalize(m_poUpdateStatement);
    3155           1 :         m_poUpdateStatement = nullptr;
    3156             :     }
    3157          57 :     if (!m_poUpdateStatement)
    3158             :     {
    3159             :         /* Construct a SQL UPDATE statement from the OGRFeature */
    3160             :         /* Only work with fields that are set */
    3161             :         /* Do not stick values into SQL, use placeholder and bind values later
    3162             :          */
    3163          45 :         const std::string osCommand = FeatureGenerateUpdateSQL(poFeature);
    3164          45 :         if (osCommand.empty())
    3165           1 :             return OGRERR_NONE;
    3166             : 
    3167             :         /* Prepare the SQL into a statement */
    3168          44 :         int err = SQLPrepareWithError(m_poDS->GetDB(), osCommand.c_str(),
    3169          44 :                                       static_cast<int>(osCommand.size()),
    3170             :                                       &m_poUpdateStatement, nullptr);
    3171          44 :         if (err != SQLITE_OK)
    3172             :         {
    3173           1 :             return OGRERR_FAILURE;
    3174             :         }
    3175             :     }
    3176             : 
    3177             :     /* Bind values onto the statement now */
    3178          55 :     OGRErr errOgr = FeatureBindUpdateParameters(poFeature, m_poUpdateStatement);
    3179          55 :     if (errOgr != OGRERR_NONE)
    3180             :     {
    3181           0 :         sqlite3_reset(m_poUpdateStatement);
    3182           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3183           0 :         return errOgr;
    3184             :     }
    3185             : 
    3186             :     /* From here execute the statement and check errors */
    3187          55 :     int err = sqlite3_step(m_poUpdateStatement);
    3188          55 :     if (!(err == SQLITE_OK || err == SQLITE_DONE))
    3189             :     {
    3190           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
    3191           0 :                  sqlite3_errmsg(m_poDS->GetDB()));
    3192           0 :         sqlite3_reset(m_poUpdateStatement);
    3193           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3194           0 :         return OGRERR_FAILURE;
    3195             :     }
    3196             : 
    3197          55 :     sqlite3_reset(m_poUpdateStatement);
    3198          55 :     sqlite3_clear_bindings(m_poUpdateStatement);
    3199             : 
    3200             :     const sqlite3_int64 nTotalChangesAfter =
    3201             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3202          55 :         sqlite3_total_changes64(m_poDS->GetDB());
    3203             : #else
    3204             :         sqlite3_total_changes(m_poDS->GetDB());
    3205             : #endif
    3206             : 
    3207             :     /* Only update the envelope if we changed something */
    3208          55 :     OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
    3209          55 :                       ? OGRERR_NONE
    3210             :                       : OGRERR_NON_EXISTING_FEATURE;
    3211          55 :     if (eErr == OGRERR_NONE)
    3212             :     {
    3213             :         /* Update the layer extents with this new object */
    3214          49 :         if (IsGeomFieldSet(poFeature))
    3215             :         {
    3216          36 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
    3217          36 :             if (!poGeom->IsEmpty())
    3218             :             {
    3219          36 :                 OGREnvelope oEnv;
    3220          36 :                 poGeom->getEnvelope(&oEnv);
    3221          36 :                 UpdateExtent(&oEnv);
    3222             :             }
    3223             :         }
    3224             : 
    3225          49 :         m_bContentChanged = true;
    3226             :     }
    3227             : 
    3228             :     /* All done! */
    3229          55 :     return eErr;
    3230             : }
    3231             : 
    3232             : /************************************************************************/
    3233             : /*                           IUpsertFeature()                           */
    3234             : /************************************************************************/
    3235             : 
    3236          26 : OGRErr OGRGeoPackageTableLayer::IUpsertFeature(OGRFeature *poFeature)
    3237             : 
    3238             : {
    3239          26 :     return CreateOrUpsertFeature(poFeature, /* bUpsert = */ true);
    3240             : }
    3241             : 
    3242             : //----------------------------------------------------------------------
    3243             : // FeatureGenerateUpdateSQL()
    3244             : //
    3245             : // Build a SQL UPDATE statement that references all the columns in
    3246             : // the OGRFeatureDefn that the user asked to be updated, then prepare it for
    3247             : // repeated use in a prepared statement. All statements start off with geometry
    3248             : // (if it exists, and if it is asked to be updated), then reference each column
    3249             : // in the order it appears in the OGRFeatureDefn.
    3250             : // FeatureBindParameters operates on the expectation of this
    3251             : // column ordering.
    3252             : 
    3253             : //
    3254          11 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
    3255             :     const OGRFeature *poFeature, int nUpdatedFieldsCount,
    3256             :     const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
    3257             :     const int * /*panUpdatedGeomFieldsIdx*/) const
    3258             : {
    3259          11 :     bool bNeedComma = false;
    3260          11 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
    3261             : 
    3262             :     /* Set up our SQL string basics */
    3263          22 :     std::string osUpdate("UPDATE \"");
    3264          11 :     osUpdate += SQLEscapeName(m_pszTableName);
    3265          11 :     osUpdate += "\" SET ";
    3266             : 
    3267          11 :     if (nUpdatedGeomFieldsCount == 1 && poFeatureDefn->GetGeomFieldCount() > 0)
    3268             :     {
    3269           2 :         osUpdate += '"';
    3270             :         osUpdate +=
    3271           2 :             SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
    3272           2 :         osUpdate += "\"=?";
    3273           2 :         bNeedComma = true;
    3274             :     }
    3275             : 
    3276             :     /* Add attribute column names (except FID) to the SQL */
    3277          20 :     for (int i = 0; i < nUpdatedFieldsCount; i++)
    3278             :     {
    3279           9 :         const int iField = panUpdatedFieldsIdx[i];
    3280          18 :         if (iField == m_iFIDAsRegularColumnIndex ||
    3281           9 :             poFeatureDefn->GetFieldDefn(iField)->IsGenerated())
    3282           0 :             continue;
    3283           9 :         if (!poFeature->IsFieldSet(iField))
    3284           1 :             continue;
    3285           8 :         if (!bNeedComma)
    3286           8 :             bNeedComma = true;
    3287             :         else
    3288           0 :             osUpdate += ", ";
    3289             : 
    3290           8 :         osUpdate += '"';
    3291             :         osUpdate +=
    3292           8 :             SQLEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
    3293           8 :         osUpdate += "\"=?";
    3294             :     }
    3295          11 :     if (!bNeedComma)
    3296           1 :         return CPLString();
    3297             : 
    3298          10 :     osUpdate += " WHERE \"";
    3299          10 :     osUpdate += SQLEscapeName(m_pszFidColumn);
    3300          10 :     osUpdate += "\" = ?";
    3301             : 
    3302          10 :     return osUpdate;
    3303             : }
    3304             : 
    3305             : /************************************************************************/
    3306             : /*                           UpdateFeature()                            */
    3307             : /************************************************************************/
    3308             : 
    3309          11 : OGRErr OGRGeoPackageTableLayer::IUpdateFeature(
    3310             :     OGRFeature *poFeature, int nUpdatedFieldsCount,
    3311             :     const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
    3312             :     const int *panUpdatedGeomFieldsIdx, bool /* bUpdateStyleString*/)
    3313             : 
    3314             : {
    3315          11 :     if (!m_bFeatureDefnCompleted)
    3316           0 :         GetLayerDefn();
    3317          11 :     if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
    3318             :     {
    3319           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3320             :                  "UpdateFeature");
    3321           0 :         return OGRERR_FAILURE;
    3322             :     }
    3323             : 
    3324             :     /* No FID? */
    3325          11 :     if (poFeature->GetFID() == OGRNullFID)
    3326             :     {
    3327           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3328             :                  "FID required on features given to SetFeature().");
    3329           0 :         return OGRERR_FAILURE;
    3330             :     }
    3331             : 
    3332             :     /* In case the FID column has also been created as a regular field */
    3333          11 :     if (m_iFIDAsRegularColumnIndex >= 0 &&
    3334           0 :         !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
    3335             :     {
    3336           0 :         return OGRERR_FAILURE;
    3337             :     }
    3338             : 
    3339          11 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3340           0 :         return OGRERR_FAILURE;
    3341             : 
    3342          11 :     CancelAsyncNextArrowArray();
    3343             : 
    3344          11 :     if (m_bThreadRTreeStarted)
    3345           0 :         CancelAsyncRTree();
    3346          11 :     if (!RunDeferredSpatialIndexUpdate())
    3347           0 :         return OGRERR_FAILURE;
    3348             : 
    3349          11 :     CheckGeometryType(poFeature);
    3350             : 
    3351             :     /* Construct a SQL UPDATE statement from the OGRFeature */
    3352             :     /* Only work with fields that are set */
    3353             :     /* Do not stick values into SQL, use placeholder and bind values later
    3354             :      */
    3355             :     std::string osUpdateStatementSQL = FeatureGenerateUpdateSQL(
    3356             :         poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
    3357          22 :         nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
    3358          11 :     if (osUpdateStatementSQL.empty())
    3359           1 :         return OGRERR_NONE;
    3360             : 
    3361          10 :     if (m_osUpdateStatementSQL != osUpdateStatementSQL)
    3362             :     {
    3363           8 :         if (m_poUpdateStatement)
    3364           5 :             sqlite3_finalize(m_poUpdateStatement);
    3365           8 :         m_poUpdateStatement = nullptr;
    3366             :         /* Prepare the SQL into a statement */
    3367             :         int err =
    3368           8 :             SQLPrepareWithError(m_poDS->GetDB(), osUpdateStatementSQL.c_str(),
    3369           8 :                                 static_cast<int>(osUpdateStatementSQL.size()),
    3370             :                                 &m_poUpdateStatement, nullptr);
    3371           8 :         if (err != SQLITE_OK)
    3372             :         {
    3373           0 :             return OGRERR_FAILURE;
    3374             :         }
    3375           8 :         m_osUpdateStatementSQL = std::move(osUpdateStatementSQL);
    3376             :     }
    3377             : 
    3378             :     /* Bind values onto the statement now */
    3379          10 :     int nColCount = 0;
    3380             :     const OGRErr errOgr =
    3381          10 :         FeatureBindParameters(poFeature, m_poUpdateStatement, &nColCount, false,
    3382             :                               false, nUpdatedFieldsCount, panUpdatedFieldsIdx,
    3383             :                               nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
    3384          10 :     if (errOgr != OGRERR_NONE)
    3385             :     {
    3386           0 :         sqlite3_reset(m_poUpdateStatement);
    3387           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3388           0 :         return errOgr;
    3389             :     }
    3390             : 
    3391             :     // Bind the FID to the "WHERE" clause.
    3392             :     const int sqlite_err =
    3393          10 :         sqlite3_bind_int64(m_poUpdateStatement, nColCount, poFeature->GetFID());
    3394          10 :     if (sqlite_err != SQLITE_OK)
    3395             :     {
    3396           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3397             :                  "failed to bind FID '" CPL_FRMT_GIB "' to statement",
    3398             :                  poFeature->GetFID());
    3399           0 :         sqlite3_reset(m_poUpdateStatement);
    3400           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3401           0 :         return OGRERR_FAILURE;
    3402             :     }
    3403             : 
    3404             :     const sqlite3_int64 nTotalChangesBefore =
    3405             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3406          10 :         sqlite3_total_changes64(m_poDS->GetDB());
    3407             : #else
    3408             :         sqlite3_total_changes(m_poDS->GetDB());
    3409             : #endif
    3410             : 
    3411             :     /* From here execute the statement and check errors */
    3412          10 :     int err = sqlite3_step(m_poUpdateStatement);
    3413          10 :     if (!(err == SQLITE_OK || err == SQLITE_DONE))
    3414             :     {
    3415           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
    3416           0 :                  sqlite3_errmsg(m_poDS->GetDB()));
    3417           0 :         sqlite3_reset(m_poUpdateStatement);
    3418           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3419           0 :         return OGRERR_FAILURE;
    3420             :     }
    3421             : 
    3422          10 :     sqlite3_reset(m_poUpdateStatement);
    3423          10 :     sqlite3_clear_bindings(m_poUpdateStatement);
    3424             : 
    3425             :     const sqlite3_int64 nTotalChangesAfter =
    3426             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3427          10 :         sqlite3_total_changes64(m_poDS->GetDB());
    3428             : #else
    3429             :         sqlite3_total_changes(m_poDS->GetDB());
    3430             : #endif
    3431             : 
    3432             :     /* Only update the envelope if we changed something */
    3433          10 :     OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
    3434          10 :                       ? OGRERR_NONE
    3435             :                       : OGRERR_NON_EXISTING_FEATURE;
    3436          10 :     if (eErr == OGRERR_NONE)
    3437             :     {
    3438             :         /* Update the layer extents with this new object */
    3439           9 :         if (nUpdatedGeomFieldsCount == 1 && IsGeomFieldSet(poFeature))
    3440             :         {
    3441           1 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
    3442           1 :             if (!poGeom->IsEmpty())
    3443             :             {
    3444           1 :                 OGREnvelope oEnv;
    3445           1 :                 poGeom->getEnvelope(&oEnv);
    3446           1 :                 UpdateExtent(&oEnv);
    3447             :             }
    3448             :         }
    3449             : 
    3450           9 :         m_bContentChanged = true;
    3451             :     }
    3452             : 
    3453             :     /* All done! */
    3454          10 :     return eErr;
    3455             : }
    3456             : 
    3457             : /************************************************************************/
    3458             : /*                         SetAttributeFilter()                         */
    3459             : /************************************************************************/
    3460             : 
    3461          87 : OGRErr OGRGeoPackageTableLayer::SetAttributeFilter(const char *pszQuery)
    3462             : 
    3463             : {
    3464          87 :     if (!m_bFeatureDefnCompleted)
    3465           8 :         GetLayerDefn();
    3466          87 :     CPLFree(m_pszAttrQueryString);
    3467          87 :     m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
    3468             : 
    3469          87 :     if (pszQuery == nullptr)
    3470          40 :         osQuery = "";
    3471             :     else
    3472          47 :         osQuery = pszQuery;
    3473             : 
    3474          87 :     BuildWhere();
    3475             : 
    3476          87 :     ResetReading();
    3477             : 
    3478          87 :     return OGRERR_NONE;
    3479             : }
    3480             : 
    3481             : /************************************************************************/
    3482             : /*                      ResetReading()                                  */
    3483             : /************************************************************************/
    3484             : 
    3485       25254 : void OGRGeoPackageTableLayer::ResetReading()
    3486             : {
    3487       25254 :     m_bEOF = false;
    3488       25254 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3489           0 :         return;
    3490             : 
    3491       25254 :     OGRGeoPackageLayer::ResetReading();
    3492             : 
    3493       25254 :     if (m_poInsertStatement)
    3494             :     {
    3495         151 :         sqlite3_finalize(m_poInsertStatement);
    3496         151 :         m_poInsertStatement = nullptr;
    3497             :     }
    3498             : 
    3499       25254 :     if (m_poUpdateStatement)
    3500             :     {
    3501          23 :         sqlite3_finalize(m_poUpdateStatement);
    3502          23 :         m_poUpdateStatement = nullptr;
    3503             :     }
    3504       25254 :     m_osUpdateStatementSQL.clear();
    3505             : 
    3506       25254 :     if (m_poGetFeatureStatement)
    3507             :     {
    3508          22 :         sqlite3_finalize(m_poGetFeatureStatement);
    3509          22 :         m_poGetFeatureStatement = nullptr;
    3510             :     }
    3511             : 
    3512       25254 :     CancelAsyncNextArrowArray();
    3513             : 
    3514       25254 :     m_bGetNextArrowArrayCalledSinceResetReading = false;
    3515             : 
    3516       25254 :     BuildColumns();
    3517             : }
    3518             : 
    3519             : /************************************************************************/
    3520             : /*                           SetNextByIndex()                           */
    3521             : /************************************************************************/
    3522             : 
    3523          17 : OGRErr OGRGeoPackageTableLayer::SetNextByIndex(GIntBig nIndex)
    3524             : {
    3525          17 :     if (nIndex < 0)
    3526             :     {
    3527           3 :         m_bEOF = true;
    3528           3 :         return OGRERR_NON_EXISTING_FEATURE;
    3529             :     }
    3530          14 :     if (m_soColumns.empty())
    3531           2 :         BuildColumns();
    3532          14 :     return ResetStatementInternal(nIndex);
    3533             : }
    3534             : 
    3535             : /************************************************************************/
    3536             : /*                           ResetStatement()                           */
    3537             : /************************************************************************/
    3538             : 
    3539         642 : OGRErr OGRGeoPackageTableLayer::ResetStatement()
    3540             : 
    3541             : {
    3542         642 :     return ResetStatementInternal(0);
    3543             : }
    3544             : 
    3545             : /************************************************************************/
    3546             : /*                       ResetStatementInternal()                       */
    3547             : /************************************************************************/
    3548             : 
    3549         656 : OGRErr OGRGeoPackageTableLayer::ResetStatementInternal(GIntBig nStartIndex)
    3550             : 
    3551             : {
    3552         656 :     ClearStatement();
    3553             : 
    3554             :     /* There is no active query statement set up, */
    3555             :     /* so job #1 is to prepare the statement. */
    3556             :     /* Append the attribute filter, if there is one */
    3557        1312 :     CPLString soSQL;
    3558         656 :     if (!m_soFilter.empty())
    3559             :     {
    3560             :         soSQL.Printf("SELECT %s FROM \"%s\" m WHERE %s", m_soColumns.c_str(),
    3561         202 :                      SQLEscapeName(m_pszTableName).c_str(), m_soFilter.c_str());
    3562             : 
    3563         390 :         if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
    3564         188 :             HasSpatialIndex())
    3565             :         {
    3566         187 :             OGREnvelope sEnvelope;
    3567             : 
    3568         187 :             m_poFilterGeom->getEnvelope(&sEnvelope);
    3569             : 
    3570         187 :             bool bUseSpatialIndex = true;
    3571         368 :             if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
    3572         122 :                 sEnvelope.MinY <= m_poExtent->MinY &&
    3573         466 :                 sEnvelope.MaxX >= m_poExtent->MaxX &&
    3574          98 :                 sEnvelope.MaxY >= m_poExtent->MaxY)
    3575             :             {
    3576             :                 // Selecting from spatial filter on whole extent can be rather
    3577             :                 // slow. So use function based filtering, just in case the
    3578             :                 // advertized global extent might be wrong. Otherwise we might
    3579             :                 // just discard completely the spatial filter.
    3580          95 :                 bUseSpatialIndex = false;
    3581             :             }
    3582             : 
    3583          92 :             if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
    3584         371 :                 !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
    3585          92 :                 !std::isinf(sEnvelope.MaxY))
    3586             :             {
    3587             :                 soSQL.Printf("SELECT %s FROM \"%s\" m "
    3588             :                              "JOIN \"%s\" r "
    3589             :                              "ON m.\"%s\" = r.id WHERE "
    3590             :                              "r.maxx >= %.12f AND r.minx <= %.12f AND "
    3591             :                              "r.maxy >= %.12f AND r.miny <= %.12f",
    3592             :                              m_soColumns.c_str(),
    3593         184 :                              SQLEscapeName(m_pszTableName).c_str(),
    3594         184 :                              SQLEscapeName(m_osRTreeName).c_str(),
    3595         184 :                              SQLEscapeName(m_osFIDForRTree).c_str(),
    3596          92 :                              sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    3597         368 :                              sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
    3598             :             }
    3599             :         }
    3600             :     }
    3601             :     else
    3602             :         soSQL.Printf("SELECT %s FROM \"%s\" m", m_soColumns.c_str(),
    3603         454 :                      SQLEscapeName(m_pszTableName).c_str());
    3604         656 :     if (nStartIndex > 0)
    3605             :     {
    3606          11 :         soSQL += CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB, nStartIndex);
    3607             :     }
    3608             : 
    3609         656 :     CPLDebug("GPKG", "ResetStatement(%s)", soSQL.c_str());
    3610             : 
    3611         656 :     int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
    3612             :                                   &m_poQueryStatement, nullptr);
    3613         656 :     if (err != SQLITE_OK)
    3614             :     {
    3615           1 :         return OGRERR_FAILURE;
    3616             :     }
    3617             : 
    3618         655 :     m_iNextShapeId = nStartIndex;
    3619         655 :     m_bGetNextArrowArrayCalledSinceResetReading = false;
    3620             : 
    3621         655 :     return OGRERR_NONE;
    3622             : }
    3623             : 
    3624             : /************************************************************************/
    3625             : /*                           GetNextFeature()                           */
    3626             : /************************************************************************/
    3627             : 
    3628       10916 : OGRFeature *OGRGeoPackageTableLayer::GetNextFeature()
    3629             : {
    3630       10916 :     if (m_bEOF)
    3631           7 :         return nullptr;
    3632       10909 :     if (!m_bFeatureDefnCompleted)
    3633          33 :         GetLayerDefn();
    3634       10909 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3635           0 :         return nullptr;
    3636             : 
    3637       10909 :     CancelAsyncNextArrowArray();
    3638             : 
    3639       10909 :     if (m_poFilterGeom != nullptr)
    3640             :     {
    3641             :         // Both are exclusive
    3642       10020 :         CreateSpatialIndexIfNecessary();
    3643       10020 :         if (!RunDeferredSpatialIndexUpdate())
    3644           0 :             return nullptr;
    3645             :     }
    3646             : 
    3647       10909 :     OGRFeature *poFeature = OGRGeoPackageLayer::GetNextFeature();
    3648       10909 :     if (poFeature && m_iFIDAsRegularColumnIndex >= 0)
    3649             :     {
    3650           1 :         poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
    3651             :     }
    3652       10909 :     return poFeature;
    3653             : }
    3654             : 
    3655             : /************************************************************************/
    3656             : /*                        GetFeature()                                  */
    3657             : /************************************************************************/
    3658             : 
    3659        1295 : OGRFeature *OGRGeoPackageTableLayer::GetFeature(GIntBig nFID)
    3660             : {
    3661        1295 :     if (!m_bFeatureDefnCompleted)
    3662          11 :         GetLayerDefn();
    3663        1295 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3664           0 :         return nullptr;
    3665        1295 :     CancelAsyncNextArrowArray();
    3666             : 
    3667        1295 :     if (m_pszFidColumn == nullptr)
    3668           1 :         return OGRLayer::GetFeature(nFID);
    3669             : 
    3670        1294 :     if (m_poGetFeatureStatement == nullptr)
    3671             :     {
    3672          50 :         CPLString soSQL;
    3673             :         soSQL.Printf("SELECT %s FROM \"%s\" m "
    3674             :                      "WHERE \"%s\" = ?",
    3675         100 :                      m_soColumns.c_str(), SQLEscapeName(m_pszTableName).c_str(),
    3676         150 :                      SQLEscapeName(m_pszFidColumn).c_str());
    3677             : 
    3678          50 :         const int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
    3679             :                                             &m_poGetFeatureStatement, nullptr);
    3680          50 :         if (err != SQLITE_OK)
    3681             :         {
    3682           0 :             return nullptr;
    3683             :         }
    3684             :     }
    3685             : 
    3686        1294 :     CPL_IGNORE_RET_VAL(sqlite3_bind_int64(m_poGetFeatureStatement, 1, nFID));
    3687             : 
    3688             :     /* Should be only one or zero results */
    3689        1294 :     const int err = sqlite3_step(m_poGetFeatureStatement);
    3690             : 
    3691             :     /* Aha, got one */
    3692        1294 :     if (err == SQLITE_ROW)
    3693             :     {
    3694        1282 :         OGRFeature *poFeature = TranslateFeature(m_poGetFeatureStatement);
    3695        1282 :         if (m_iFIDAsRegularColumnIndex >= 0)
    3696             :         {
    3697           7 :             poFeature->SetField(m_iFIDAsRegularColumnIndex,
    3698             :                                 poFeature->GetFID());
    3699             :         }
    3700             : 
    3701        1282 :         sqlite3_reset(m_poGetFeatureStatement);
    3702        1282 :         sqlite3_clear_bindings(m_poGetFeatureStatement);
    3703             : 
    3704        1282 :         return poFeature;
    3705             :     }
    3706             : 
    3707          12 :     sqlite3_reset(m_poGetFeatureStatement);
    3708          12 :     sqlite3_clear_bindings(m_poGetFeatureStatement);
    3709             : 
    3710             :     /* Error out on all other return codes */
    3711          12 :     return nullptr;
    3712             : }
    3713             : 
    3714             : /************************************************************************/
    3715             : /*                        DeleteFeature()                               */
    3716             : /************************************************************************/
    3717             : 
    3718          60 : OGRErr OGRGeoPackageTableLayer::DeleteFeature(GIntBig nFID)
    3719             : {
    3720          60 :     if (!m_bFeatureDefnCompleted)
    3721           4 :         GetLayerDefn();
    3722          60 :     if (!m_poDS->GetUpdate())
    3723             :     {
    3724           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3725             :                  "DeleteFeature");
    3726           0 :         return OGRERR_FAILURE;
    3727             :     }
    3728          60 :     if (m_pszFidColumn == nullptr)
    3729             :     {
    3730           0 :         return OGRERR_FAILURE;
    3731             :     }
    3732             : 
    3733          60 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3734           0 :         return OGRERR_FAILURE;
    3735             : 
    3736          60 :     CancelAsyncNextArrowArray();
    3737             : 
    3738          60 :     if (m_bThreadRTreeStarted)
    3739          12 :         CancelAsyncRTree();
    3740             : 
    3741          60 :     if (!RunDeferredSpatialIndexUpdate())
    3742           0 :         return OGRERR_FAILURE;
    3743             : 
    3744             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    3745          60 :     if (m_bOGRFeatureCountTriggersEnabled)
    3746             :     {
    3747          17 :         DisableFeatureCountTriggers();
    3748             :     }
    3749             : #endif
    3750             : 
    3751             :     /* Clear out any existing query */
    3752          60 :     ResetReading();
    3753             : 
    3754             :     /* No filters apply, just use the FID */
    3755          60 :     CPLString soSQL;
    3756             :     soSQL.Printf("DELETE FROM \"%s\" WHERE \"%s\" = " CPL_FRMT_GIB,
    3757         120 :                  SQLEscapeName(m_pszTableName).c_str(),
    3758         180 :                  SQLEscapeName(m_pszFidColumn).c_str(), nFID);
    3759             : 
    3760             :     const sqlite3_int64 nTotalChangesBefore =
    3761             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3762          60 :         sqlite3_total_changes64(m_poDS->GetDB());
    3763             : #else
    3764             :         sqlite3_total_changes(m_poDS->GetDB());
    3765             : #endif
    3766             : 
    3767          60 :     OGRErr eErr = SQLCommand(m_poDS->GetDB(), soSQL.c_str());
    3768          60 :     if (eErr == OGRERR_NONE)
    3769             :     {
    3770             :         const sqlite3_int64 nTotalChangesAfter =
    3771             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3772          59 :             sqlite3_total_changes64(m_poDS->GetDB());
    3773             : #else
    3774             :             sqlite3_total_changes(m_poDS->GetDB());
    3775             : #endif
    3776             : 
    3777          59 :         eErr = nTotalChangesAfter != nTotalChangesBefore
    3778          59 :                    ? OGRERR_NONE
    3779             :                    : OGRERR_NON_EXISTING_FEATURE;
    3780             : 
    3781          59 :         if (eErr == OGRERR_NONE)
    3782             :         {
    3783             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    3784          51 :             if (m_nTotalFeatureCount >= 0)
    3785          43 :                 m_nTotalFeatureCount--;
    3786             : #endif
    3787             : 
    3788          51 :             m_bContentChanged = true;
    3789             :         }
    3790             :     }
    3791          60 :     return eErr;
    3792             : }
    3793             : 
    3794             : /************************************************************************/
    3795             : /*                     DoJobAtTransactionCommit()                       */
    3796             : /************************************************************************/
    3797             : 
    3798         379 : bool OGRGeoPackageTableLayer::DoJobAtTransactionCommit()
    3799             : {
    3800         379 :     if (m_bAllowedRTreeThread)
    3801         214 :         return true;
    3802             : 
    3803         330 :     bool ret = RunDeferredCreationIfNecessary() == OGRERR_NONE &&
    3804         165 :                RunDeferredSpatialIndexUpdate();
    3805         165 :     m_nCountInsertInTransaction = 0;
    3806         165 :     m_aoRTreeTriggersSQL.clear();
    3807         165 :     m_aoRTreeEntries.clear();
    3808         165 :     return ret;
    3809             : }
    3810             : 
    3811             : /************************************************************************/
    3812             : /*                    DoJobAtTransactionRollback()                      */
    3813             : /************************************************************************/
    3814             : 
    3815          36 : bool OGRGeoPackageTableLayer::DoJobAtTransactionRollback()
    3816             : {
    3817          36 :     if (m_bThreadRTreeStarted)
    3818          12 :         CancelAsyncRTree();
    3819          36 :     m_nCountInsertInTransaction = 0;
    3820          36 :     m_aoRTreeTriggersSQL.clear();
    3821          36 :     m_aoRTreeEntries.clear();
    3822          36 :     if (m_bTableCreatedInTransaction)
    3823             :     {
    3824           1 :         SyncToDisk();
    3825             :     }
    3826             :     else
    3827             :     {
    3828          35 :         bool bDeferredSpatialIndexCreationBackup =
    3829             :             m_bDeferredSpatialIndexCreation;
    3830          35 :         m_bDeferredSpatialIndexCreation = false;
    3831          35 :         SyncToDisk();
    3832          35 :         m_bDeferredSpatialIndexCreation = bDeferredSpatialIndexCreationBackup;
    3833             :     }
    3834             : 
    3835          36 :     ResetReading();
    3836          36 :     return true;
    3837             : }
    3838             : 
    3839             : /************************************************************************/
    3840             : /*                  StartDeferredSpatialIndexUpdate()                   */
    3841             : /************************************************************************/
    3842             : 
    3843           7 : bool OGRGeoPackageTableLayer::StartDeferredSpatialIndexUpdate()
    3844             : {
    3845           7 :     if (m_poFeatureDefn->GetGeomFieldCount() == 0)
    3846           0 :         return true;
    3847             : 
    3848           7 :     RevertWorkaroundUpdate1TriggerIssue();
    3849             : 
    3850           7 :     m_aoRTreeTriggersSQL.clear();
    3851           7 :     m_aoRTreeEntries.clear();
    3852             : 
    3853           7 :     const char *pszT = m_pszTableName;
    3854           7 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    3855           7 :     m_osRTreeName = "rtree_";
    3856           7 :     m_osRTreeName += pszT;
    3857           7 :     m_osRTreeName += "_";
    3858           7 :     m_osRTreeName += pszC;
    3859             : 
    3860          63 :     char *pszSQL = sqlite3_mprintf(
    3861             :         "SELECT sql FROM sqlite_master WHERE type = 'trigger' "
    3862             :         "AND name IN ('%q', '%q', '%q', '%q', '%q', '%q', "
    3863             :         "'%q', '%q', '%q')",
    3864          14 :         (m_osRTreeName + "_insert").c_str(),
    3865          14 :         (m_osRTreeName + "_update1").c_str(),
    3866          14 :         (m_osRTreeName + "_update2").c_str(),
    3867          14 :         (m_osRTreeName + "_update3").c_str(),
    3868          14 :         (m_osRTreeName + "_update4").c_str(),
    3869             :         // update5 replaces update3 in GPKG 1.4
    3870             :         // cf https://github.com/opengeospatial/geopackage/pull/661
    3871          14 :         (m_osRTreeName + "_update5").c_str(),
    3872             :         // update6 and update7 replace update1 in GPKG 1.4
    3873             :         // cf https://github.com/opengeospatial/geopackage/pull/661
    3874          14 :         (m_osRTreeName + "_update6").c_str(),
    3875          14 :         (m_osRTreeName + "_update7").c_str(),
    3876          14 :         (m_osRTreeName + "_delete").c_str());
    3877          14 :     auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    3878           7 :     sqlite3_free(pszSQL);
    3879           7 :     if (oResult)
    3880             :     {
    3881          53 :         for (int iRecord = 0; iRecord < oResult->RowCount(); iRecord++)
    3882             :         {
    3883          46 :             const char *pszTriggerSQL = oResult->GetValue(0, iRecord);
    3884          46 :             if (pszTriggerSQL)
    3885             :             {
    3886          46 :                 m_aoRTreeTriggersSQL.push_back(pszTriggerSQL);
    3887             :             }
    3888             :         }
    3889             :     }
    3890           7 :     if (m_aoRTreeTriggersSQL.size() != 6 && m_aoRTreeTriggersSQL.size() != 7)
    3891             :     {
    3892           0 :         CPLDebug("GPKG", "Could not find expected RTree triggers");
    3893           0 :         m_aoRTreeTriggersSQL.clear();
    3894           0 :         return false;
    3895             :     }
    3896             : 
    3897           7 :     SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers());
    3898             : 
    3899           7 :     return true;
    3900             : }
    3901             : 
    3902             : /************************************************************************/
    3903             : /*                  FlushPendingSpatialIndexUpdate()                    */
    3904             : /************************************************************************/
    3905             : 
    3906           5 : bool OGRGeoPackageTableLayer::FlushPendingSpatialIndexUpdate()
    3907             : {
    3908           5 :     bool ret = true;
    3909             : 
    3910             :     // CPLDebug("GPKG", "Insert %d features in spatial index",
    3911             :     //          static_cast<int>(m_aoRTreeEntries.size()));
    3912             : 
    3913           5 :     const char *pszT = m_pszTableName;
    3914           5 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    3915             : 
    3916           5 :     m_osRTreeName = "rtree_";
    3917           5 :     m_osRTreeName += pszT;
    3918           5 :     m_osRTreeName += "_";
    3919           5 :     m_osRTreeName += pszC;
    3920             : 
    3921           5 :     char *pszSQL = sqlite3_mprintf("INSERT INTO \"%w\" VALUES (?,?,?,?,?)",
    3922             :                                    m_osRTreeName.c_str());
    3923           5 :     sqlite3_stmt *hInsertStmt = nullptr;
    3924           5 :     if (SQLPrepareWithError(m_poDS->GetDB(), pszSQL, -1, &hInsertStmt,
    3925           5 :                             nullptr) != SQLITE_OK)
    3926             :     {
    3927           0 :         sqlite3_free(pszSQL);
    3928           0 :         m_aoRTreeEntries.clear();
    3929           0 :         return false;
    3930             :     }
    3931           5 :     sqlite3_free(pszSQL);
    3932             : 
    3933         190 :     for (size_t i = 0; i < m_aoRTreeEntries.size(); ++i)
    3934             :     {
    3935         185 :         sqlite3_reset(hInsertStmt);
    3936             : 
    3937         185 :         sqlite3_bind_int64(hInsertStmt, 1, m_aoRTreeEntries[i].nId);
    3938         185 :         sqlite3_bind_double(hInsertStmt, 2, m_aoRTreeEntries[i].fMinX);
    3939         185 :         sqlite3_bind_double(hInsertStmt, 3, m_aoRTreeEntries[i].fMaxX);
    3940         185 :         sqlite3_bind_double(hInsertStmt, 4, m_aoRTreeEntries[i].fMinY);
    3941         185 :         sqlite3_bind_double(hInsertStmt, 5, m_aoRTreeEntries[i].fMaxY);
    3942         185 :         int sqlite_err = sqlite3_step(hInsertStmt);
    3943         185 :         if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
    3944             :         {
    3945           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3946             :                      "failed to execute insertion in RTree : %s",
    3947           0 :                      sqlite3_errmsg(m_poDS->GetDB()));
    3948           0 :             ret = false;
    3949           0 :             break;
    3950             :         }
    3951             :     }
    3952           5 :     sqlite3_finalize(hInsertStmt);
    3953           5 :     m_aoRTreeEntries.clear();
    3954           5 :     return ret;
    3955             : }
    3956             : 
    3957             : /************************************************************************/
    3958             : /*                   RunDeferredSpatialIndexUpdate()                    */
    3959             : /************************************************************************/
    3960             : 
    3961       15627 : bool OGRGeoPackageTableLayer::RunDeferredSpatialIndexUpdate()
    3962             : {
    3963       15627 :     m_nCountInsertInTransaction = 0;
    3964       15627 :     if (m_aoRTreeTriggersSQL.empty())
    3965       15622 :         return true;
    3966             : 
    3967           5 :     bool ret = FlushPendingSpatialIndexUpdate();
    3968             : 
    3969           5 :     RevertWorkaroundUpdate1TriggerIssue();
    3970             : 
    3971          38 :     for (const auto &osSQL : m_aoRTreeTriggersSQL)
    3972             :     {
    3973          33 :         ret &= SQLCommand(m_poDS->GetDB(), osSQL) == OGRERR_NONE;
    3974             :     }
    3975           5 :     m_aoRTreeTriggersSQL.clear();
    3976           5 :     return ret;
    3977             : }
    3978             : 
    3979             : /************************************************************************/
    3980             : /*                        SyncToDisk()                                  */
    3981             : /************************************************************************/
    3982             : 
    3983        9967 : OGRErr OGRGeoPackageTableLayer::SyncToDisk()
    3984             : {
    3985        9967 :     if (!m_bFeatureDefnCompleted)
    3986        4928 :         return OGRERR_NONE;
    3987             : 
    3988        5039 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3989           0 :         return OGRERR_FAILURE;
    3990             : 
    3991             :     // Both are exclusive
    3992        5039 :     CreateSpatialIndexIfNecessary();
    3993        5039 :     if (!RunDeferredSpatialIndexUpdate())
    3994           0 :         return OGRERR_FAILURE;
    3995        5039 :     RevertWorkaroundUpdate1TriggerIssue();
    3996             : 
    3997             :     /* Save metadata back to the database */
    3998        5039 :     SaveExtent();
    3999        5039 :     SaveTimestamp();
    4000             : 
    4001             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4002        5039 :     CreateFeatureCountTriggers();
    4003             : #endif
    4004             : 
    4005        5039 :     return OGRERR_NONE;
    4006             : }
    4007             : 
    4008             : /************************************************************************/
    4009             : /*                        StartTransaction()                            */
    4010             : /************************************************************************/
    4011             : 
    4012         171 : OGRErr OGRGeoPackageTableLayer::StartTransaction()
    4013             : {
    4014         171 :     CancelAsyncNextArrowArray();
    4015         171 :     return m_poDS->StartTransaction();
    4016             : }
    4017             : 
    4018             : /************************************************************************/
    4019             : /*                        CommitTransaction()                           */
    4020             : /************************************************************************/
    4021             : 
    4022         144 : OGRErr OGRGeoPackageTableLayer::CommitTransaction()
    4023             : {
    4024         144 :     return m_poDS->CommitTransaction();
    4025             : }
    4026             : 
    4027             : /************************************************************************/
    4028             : /*                        RollbackTransaction()                         */
    4029             : /************************************************************************/
    4030             : 
    4031          18 : OGRErr OGRGeoPackageTableLayer::RollbackTransaction()
    4032             : {
    4033          18 :     return m_poDS->RollbackTransaction();
    4034             : }
    4035             : 
    4036             : /************************************************************************/
    4037             : /*                      GetTotalFeatureCount()                          */
    4038             : /************************************************************************/
    4039             : 
    4040         470 : GIntBig OGRGeoPackageTableLayer::GetTotalFeatureCount()
    4041             : {
    4042             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4043         470 :     if (m_nTotalFeatureCount < 0 && m_poDS->m_bHasGPKGOGRContents)
    4044             :     {
    4045             :         char *pszSQL =
    4046          18 :             sqlite3_mprintf("SELECT feature_count FROM gpkg_ogr_contents WHERE "
    4047             :                             "lower(table_name) = lower('%q') LIMIT 2",
    4048             :                             m_pszTableName);
    4049          36 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4050          18 :         sqlite3_free(pszSQL);
    4051          18 :         if (oResult && oResult->RowCount() == 1)
    4052             :         {
    4053          15 :             const char *pszFeatureCount = oResult->GetValue(0, 0);
    4054          15 :             if (pszFeatureCount)
    4055             :             {
    4056           6 :                 m_nTotalFeatureCount =
    4057           6 :                     std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
    4058             :             }
    4059             :         }
    4060             :     }
    4061         470 :     return m_nTotalFeatureCount;
    4062             : #else
    4063             :     return 0;
    4064             : #endif
    4065             : }
    4066             : 
    4067             : /************************************************************************/
    4068             : /*                        GetFeatureCount()                             */
    4069             : /************************************************************************/
    4070             : 
    4071       23083 : GIntBig OGRGeoPackageTableLayer::GetFeatureCount(int /*bForce*/)
    4072             : {
    4073       23083 :     if (!m_bFeatureDefnCompleted)
    4074          89 :         GetLayerDefn();
    4075             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4076       23083 :     if (m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr)
    4077             :     {
    4078         204 :         const auto nCount = GetTotalFeatureCount();
    4079         204 :         if (nCount >= 0)
    4080         194 :             return nCount;
    4081             :     }
    4082             : #endif
    4083             : 
    4084       22889 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4085           0 :         return 0;
    4086             : 
    4087       22889 :     CancelAsyncNextArrowArray();
    4088             : 
    4089             :     /* Ignore bForce, because we always do a full count on the database */
    4090             :     OGRErr err;
    4091       45778 :     CPLString soSQL;
    4092       22889 :     bool bUnregisterSQLFunction = false;
    4093       22888 :     if (m_bIsTable && m_poFilterGeom != nullptr &&
    4094       45777 :         m_pszAttrQueryString == nullptr && HasSpatialIndex())
    4095             :     {
    4096       22865 :         OGREnvelope sEnvelope;
    4097             : 
    4098       22865 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    4099             : 
    4100       45730 :         if (!std::isinf(sEnvelope.MinX) && !std::isinf(sEnvelope.MinY) &&
    4101       45730 :             !std::isinf(sEnvelope.MaxX) && !std::isinf(sEnvelope.MaxY))
    4102             :         {
    4103             :             soSQL.Printf("SELECT COUNT(*) FROM \"%s\" WHERE "
    4104             :                          "maxx >= %.12f AND minx <= %.12f AND "
    4105             :                          "maxy >= %.12f AND miny <= %.12f",
    4106       45730 :                          SQLEscapeName(m_osRTreeName).c_str(),
    4107       22865 :                          sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    4108       45730 :                          sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
    4109             : 
    4110       45730 :             if (OGRGeometryFactory::haveGEOS() &&
    4111       22865 :                 !(m_bFilterIsEnvelope &&
    4112       22865 :                   wkbFlatten(
    4113             :                       m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
    4114             :                           ->GetType()) == wkbPoint))
    4115             :             {
    4116       22853 :                 bUnregisterSQLFunction = true;
    4117       22853 :                 sqlite3_create_function(
    4118       22853 :                     m_poDS->hDB, "OGR_GPKG_Intersects_Spatial_Filter", 1,
    4119             :                     SQLITE_UTF8, this, OGR_GPKG_Intersects_Spatial_Filter,
    4120             :                     nullptr, nullptr);
    4121             :                 const char *pszC =
    4122       22853 :                     m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
    4123       22853 :                         ->GetNameRef();
    4124             :                 soSQL.Printf("SELECT COUNT(*) FROM \"%s\" m "
    4125             :                              "JOIN \"%s\" r "
    4126             :                              "ON m.\"%s\" = r.id WHERE "
    4127             :                              "r.maxx >= %.12f AND r.minx <= %.12f AND "
    4128             :                              "r.maxy >= %.12f AND r.miny <= %.12f AND "
    4129             :                              "OGR_GPKG_Intersects_Spatial_Filter(m.\"%s\")",
    4130       45706 :                              SQLEscapeName(m_pszTableName).c_str(),
    4131       45706 :                              SQLEscapeName(m_osRTreeName).c_str(),
    4132       45706 :                              SQLEscapeName(m_osFIDForRTree).c_str(),
    4133       22853 :                              sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    4134       22853 :                              sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11,
    4135      114265 :                              SQLEscapeName(pszC).c_str());
    4136             :             }
    4137             :         }
    4138             :     }
    4139             : 
    4140       22889 :     if (soSQL.empty())
    4141             :     {
    4142          24 :         if (!m_soFilter.empty())
    4143             :             soSQL.Printf("SELECT Count(*) FROM \"%s\" WHERE %s",
    4144          28 :                          SQLEscapeName(m_pszTableName).c_str(),
    4145          28 :                          m_soFilter.c_str());
    4146             :         else
    4147             :             soSQL.Printf("SELECT Count(*) FROM \"%s\"",
    4148          10 :                          SQLEscapeName(m_pszTableName).c_str());
    4149             :     }
    4150             : 
    4151             :     /* Just run the query directly and get back integer */
    4152             :     GIntBig iFeatureCount =
    4153       22889 :         SQLGetInteger64(m_poDS->GetDB(), soSQL.c_str(), &err);
    4154             : 
    4155       22889 :     if (bUnregisterSQLFunction)
    4156             :     {
    4157       22853 :         sqlite3_create_function(m_poDS->hDB,
    4158             :                                 "OGR_GPKG_Intersects_Spatial_Filter", 1,
    4159             :                                 SQLITE_UTF8, this, nullptr, nullptr, nullptr);
    4160             :     }
    4161             : 
    4162             :     /* Generic implementation uses -1 for error condition, so we will too */
    4163       22889 :     if (err == OGRERR_NONE)
    4164             :     {
    4165             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4166       22889 :         if (m_bIsTable && m_poFilterGeom == nullptr &&
    4167          16 :             m_pszAttrQueryString == nullptr)
    4168             :         {
    4169          10 :             m_nTotalFeatureCount = iFeatureCount;
    4170             : 
    4171          10 :             if (m_poDS->GetUpdate() && m_poDS->m_bHasGPKGOGRContents)
    4172             :             {
    4173             :                 const char *pszCount =
    4174           5 :                     CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
    4175           5 :                 char *pszSQL = sqlite3_mprintf(
    4176             :                     "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
    4177             :                     "lower(table_name )= lower('%q')",
    4178             :                     pszCount, m_pszTableName);
    4179           5 :                 SQLCommand(m_poDS->GetDB(), pszSQL);
    4180           5 :                 sqlite3_free(pszSQL);
    4181             :             }
    4182             :         }
    4183             : #endif
    4184       22889 :         return iFeatureCount;
    4185             :     }
    4186             :     else
    4187           0 :         return -1;
    4188             : }
    4189             : 
    4190             : /************************************************************************/
    4191             : /*                      GetExtentFromRTree()                            */
    4192             : /************************************************************************/
    4193             : 
    4194         112 : static bool GetExtentFromRTree(sqlite3 *hDB, const std::string &osRTreeName,
    4195             :                                double &minx, double &miny, double &maxx,
    4196             :                                double &maxy)
    4197             : {
    4198             :     // Cf https://github.com/sqlite/sqlite/blob/master/ext/rtree/rtree.c
    4199             :     // for the description of the content of the rtree _node table
    4200             :     // We fetch the root node (nodeno = 1) and iterates over its cells, to
    4201             :     // take the min/max of their minx/maxx/miny/maxy values.
    4202         112 :     char *pszSQL = sqlite3_mprintf(
    4203             :         "SELECT data FROM \"%w_node\" WHERE nodeno = 1", osRTreeName.c_str());
    4204         112 :     sqlite3_stmt *hStmt = nullptr;
    4205         112 :     CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(hDB, pszSQL, -1, &hStmt, nullptr));
    4206         112 :     sqlite3_free(pszSQL);
    4207         112 :     bool bOK = false;
    4208         112 :     if (hStmt)
    4209             :     {
    4210         224 :         if (sqlite3_step(hStmt) == SQLITE_ROW &&
    4211         112 :             sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
    4212             :         {
    4213         112 :             const int nBytes = sqlite3_column_bytes(hStmt, 0);
    4214             :             // coverity[tainted_data_return]
    4215             :             const GByte *pabyData =
    4216         112 :                 static_cast<const GByte *>(sqlite3_column_blob(hStmt, 0));
    4217         112 :             constexpr int BLOB_HEADER_SIZE = 4;
    4218         112 :             if (nBytes > BLOB_HEADER_SIZE)
    4219             :             {
    4220         112 :                 const int nCellCount = (pabyData[2] << 8) | pabyData[3];
    4221         112 :                 constexpr int SIZEOF_CELL = 24;  // int64_t + 4 float
    4222         112 :                 if (nCellCount >= 1 &&
    4223         105 :                     nBytes >= BLOB_HEADER_SIZE + SIZEOF_CELL * nCellCount)
    4224             :                 {
    4225         105 :                     minx = std::numeric_limits<double>::max();
    4226         105 :                     miny = std::numeric_limits<double>::max();
    4227         105 :                     maxx = -std::numeric_limits<double>::max();
    4228         105 :                     maxy = -std::numeric_limits<double>::max();
    4229         105 :                     size_t offset = BLOB_HEADER_SIZE;
    4230         397 :                     for (int i = 0; i < nCellCount; ++i)
    4231             :                     {
    4232         292 :                         offset += sizeof(int64_t);
    4233             : 
    4234             :                         float fMinX;
    4235         292 :                         memcpy(&fMinX, pabyData + offset, sizeof(float));
    4236         292 :                         offset += sizeof(float);
    4237         292 :                         CPL_MSBPTR32(&fMinX);
    4238         292 :                         minx = std::min(minx, static_cast<double>(fMinX));
    4239             : 
    4240             :                         float fMaxX;
    4241         292 :                         memcpy(&fMaxX, pabyData + offset, sizeof(float));
    4242         292 :                         offset += sizeof(float);
    4243         292 :                         CPL_MSBPTR32(&fMaxX);
    4244         292 :                         maxx = std::max(maxx, static_cast<double>(fMaxX));
    4245             : 
    4246             :                         float fMinY;
    4247         292 :                         memcpy(&fMinY, pabyData + offset, sizeof(float));
    4248         292 :                         offset += sizeof(float);
    4249         292 :                         CPL_MSBPTR32(&fMinY);
    4250         292 :                         miny = std::min(miny, static_cast<double>(fMinY));
    4251             : 
    4252             :                         float fMaxY;
    4253         292 :                         memcpy(&fMaxY, pabyData + offset, sizeof(float));
    4254         292 :                         offset += sizeof(float);
    4255         292 :                         CPL_MSBPTR32(&fMaxY);
    4256         292 :                         maxy = std::max(maxy, static_cast<double>(fMaxY));
    4257             :                     }
    4258             : 
    4259         105 :                     bOK = true;
    4260             :                 }
    4261             :             }
    4262             :         }
    4263         112 :         sqlite3_finalize(hStmt);
    4264             :     }
    4265         112 :     return bOK;
    4266             : }
    4267             : 
    4268             : /************************************************************************/
    4269             : /*                              IGetExtent()                            */
    4270             : /************************************************************************/
    4271             : 
    4272         304 : OGRErr OGRGeoPackageTableLayer::IGetExtent(int /* iGeomField  */,
    4273             :                                            OGREnvelope *psExtent, bool bForce)
    4274             : {
    4275         304 :     if (!m_bFeatureDefnCompleted)
    4276           0 :         GetLayerDefn();
    4277             :     /* Extent already calculated! We're done. */
    4278         304 :     if (m_poExtent != nullptr)
    4279             :     {
    4280         289 :         if (psExtent)
    4281             :         {
    4282         289 :             *psExtent = *m_poExtent;
    4283             :         }
    4284         289 :         return OGRERR_NONE;
    4285             :     }
    4286             : 
    4287          15 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4288           0 :         return OGRERR_FAILURE;
    4289             : 
    4290          15 :     CancelAsyncNextArrowArray();
    4291             : 
    4292          24 :     if (m_poFeatureDefn->GetGeomFieldCount() && HasSpatialIndex() &&
    4293           9 :         CPLTestBool(
    4294             :             CPLGetConfigOption("OGR_GPKG_USE_RTREE_FOR_GET_EXTENT", "TRUE")))
    4295             :     {
    4296           9 :         if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, psExtent->MinX,
    4297           9 :                                psExtent->MinY, psExtent->MaxX, psExtent->MaxY))
    4298             :         {
    4299           2 :             m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
    4300           2 :             m_bExtentChanged = true;
    4301           2 :             SaveExtent();
    4302           2 :             return OGRERR_NONE;
    4303             :         }
    4304             :         else
    4305             :         {
    4306           7 :             UpdateContentsToNullExtent();
    4307           7 :             return OGRERR_FAILURE;
    4308             :         }
    4309             :     }
    4310             : 
    4311             :     /* User is OK with expensive calculation */
    4312           6 :     if (bForce && m_poFeatureDefn->GetGeomFieldCount())
    4313             :     {
    4314             :         /* fall back to default implementation (scan all features) and save */
    4315             :         /* the result for later */
    4316           4 :         const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4317           4 :         char *pszSQL = sqlite3_mprintf(
    4318             :             "SELECT MIN(ST_MinX(\"%w\")), MIN(ST_MinY(\"%w\")), "
    4319             :             "MAX(ST_MaxX(\"%w\")), MAX(ST_MaxY(\"%w\")) FROM \"%w\" WHERE "
    4320             :             "\"%w\" IS NOT NULL AND NOT ST_IsEmpty(\"%w\")",
    4321             :             pszC, pszC, pszC, pszC, m_pszTableName, pszC, pszC);
    4322           8 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4323           4 :         sqlite3_free(pszSQL);
    4324           4 :         m_poExtent.reset();
    4325           8 :         if (oResult && oResult->RowCount() == 1 &&
    4326           4 :             oResult->GetValue(0, 0) != nullptr)
    4327             :         {
    4328           3 :             psExtent->MinX = CPLAtof(oResult->GetValue(0, 0));
    4329           3 :             psExtent->MinY = CPLAtof(oResult->GetValue(1, 0));
    4330           3 :             psExtent->MaxX = CPLAtof(oResult->GetValue(2, 0));
    4331           3 :             psExtent->MaxY = CPLAtof(oResult->GetValue(3, 0));
    4332           3 :             m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
    4333           3 :             m_bExtentChanged = true;
    4334           3 :             SaveExtent();
    4335             :         }
    4336             :         else
    4337             :         {
    4338           1 :             UpdateContentsToNullExtent();
    4339           1 :             return OGRERR_FAILURE;  // we didn't get an extent
    4340             :         }
    4341           3 :         return OGRERR_NONE;
    4342             :     }
    4343             : 
    4344           2 :     return OGRERR_FAILURE;
    4345             : }
    4346             : 
    4347             : /************************************************************************/
    4348             : /*                     UpdateContentsToNullExtent()                     */
    4349             : /************************************************************************/
    4350             : 
    4351           8 : void OGRGeoPackageTableLayer::UpdateContentsToNullExtent()
    4352             : {
    4353           8 :     if (m_poDS->GetUpdate())
    4354             :     {
    4355             :         char *pszSQL =
    4356           3 :             sqlite3_mprintf("UPDATE gpkg_contents SET "
    4357             :                             "min_x = NULL, min_y = NULL, "
    4358             :                             "max_x = NULL, max_y = NULL "
    4359             :                             "WHERE lower(table_name) = lower('%q') AND "
    4360             :                             "Lower(data_type) = 'features'",
    4361             :                             m_pszTableName);
    4362           3 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    4363           3 :         sqlite3_free(pszSQL);
    4364             :     }
    4365           8 :     m_bExtentChanged = false;
    4366           8 : }
    4367             : 
    4368             : /************************************************************************/
    4369             : /*                      RecomputeExtent()                               */
    4370             : /************************************************************************/
    4371             : 
    4372           4 : void OGRGeoPackageTableLayer::RecomputeExtent()
    4373             : {
    4374           4 :     m_bExtentChanged = true;
    4375           4 :     m_poExtent.reset();
    4376           4 :     OGREnvelope sExtent;
    4377           4 :     CPL_IGNORE_RET_VAL(GetExtent(&sExtent, true));
    4378           4 : }
    4379             : 
    4380             : /************************************************************************/
    4381             : /*                      TestCapability()                                */
    4382             : /************************************************************************/
    4383             : 
    4384        1674 : int OGRGeoPackageTableLayer::TestCapability(const char *pszCap) const
    4385             : {
    4386        1674 :     if (!m_bFeatureDefnCompleted)
    4387           6 :         GetLayerDefn();
    4388        1674 :     if (EQUAL(pszCap, OLCSequentialWrite))
    4389             :     {
    4390          26 :         return m_poDS->GetUpdate();
    4391             :     }
    4392        1648 :     else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
    4393        1619 :              EQUAL(pszCap, OLCAlterFieldDefn) ||
    4394        1612 :              EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
    4395        1611 :              EQUAL(pszCap, OLCReorderFields) || EQUAL(pszCap, OLCRename))
    4396             :     {
    4397          45 :         return m_poDS->GetUpdate() && m_bIsTable;
    4398             :     }
    4399        1603 :     else if (EQUAL(pszCap, OLCDeleteFeature) ||
    4400        1596 :              EQUAL(pszCap, OLCUpsertFeature) ||
    4401        1590 :              EQUAL(pszCap, OLCUpdateFeature) || EQUAL(pszCap, OLCRandomWrite))
    4402             :     {
    4403          18 :         return m_poDS->GetUpdate() && m_pszFidColumn != nullptr;
    4404             :     }
    4405        1585 :     else if (EQUAL(pszCap, OLCRandomRead))
    4406             :     {
    4407           6 :         return m_pszFidColumn != nullptr;
    4408             :     }
    4409        1579 :     else if (EQUAL(pszCap, OLCTransactions))
    4410             :     {
    4411          16 :         return TRUE;
    4412             :     }
    4413             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4414        1563 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    4415             :     {
    4416           8 :         return m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr &&
    4417           8 :                m_nTotalFeatureCount >= 0;
    4418             :     }
    4419             : #endif
    4420        1559 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    4421             :     {
    4422           3 :         return HasSpatialIndex() || m_bDeferredSpatialIndexCreation;
    4423             :     }
    4424        1556 :     else if (EQUAL(pszCap, OLCFastSetNextByIndex))
    4425             :     {
    4426             :         // Fast may not be that true on large layers, but better than the
    4427             :         // default implementation for sure...
    4428           0 :         return TRUE;
    4429             :     }
    4430        1556 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    4431             :     {
    4432           5 :         return (m_poExtent != nullptr);
    4433             :     }
    4434        1551 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    4435         800 :         return TRUE;
    4436         751 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
    4437         683 :         return TRUE;
    4438          68 :     else if (EQUAL(pszCap, OLCZGeometries))
    4439           9 :         return TRUE;
    4440          59 :     if (EQUAL(pszCap, OLCFastGetExtent3D))
    4441           0 :         return TRUE;
    4442             :     else
    4443             :     {
    4444          59 :         return OGRGeoPackageLayer::TestCapability(pszCap);
    4445             :     }
    4446             : }
    4447             : 
    4448             : /************************************************************************/
    4449             : /*                     CreateSpatialIndexIfNecessary()                  */
    4450             : /************************************************************************/
    4451             : 
    4452       19171 : void OGRGeoPackageTableLayer::CreateSpatialIndexIfNecessary()
    4453             : {
    4454       19171 :     if (m_bDeferredSpatialIndexCreation)
    4455             :     {
    4456         729 :         CreateSpatialIndex();
    4457             :     }
    4458       19171 : }
    4459             : 
    4460             : /************************************************************************/
    4461             : /*                       CreateSpatialIndex()                           */
    4462             : /************************************************************************/
    4463             : 
    4464         739 : bool OGRGeoPackageTableLayer::CreateSpatialIndex(const char *pszTableName)
    4465             : {
    4466             :     OGRErr err;
    4467             : 
    4468         739 :     if (!m_bFeatureDefnCompleted)
    4469           2 :         GetLayerDefn();
    4470             : 
    4471         739 :     if (!CheckUpdatableTable("CreateSpatialIndex"))
    4472           1 :         return false;
    4473             : 
    4474         738 :     if (m_bDropRTreeTable)
    4475             :     {
    4476           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4477             :                  "Cannot run CreateSpatialIndex() after non-completed deferred "
    4478             :                  "DropSpatialIndex()");
    4479           0 :         return false;
    4480             :     }
    4481             : 
    4482         738 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4483           0 :         return false;
    4484             : 
    4485         738 :     CancelAsyncNextArrowArray();
    4486             : 
    4487         738 :     m_bDeferredSpatialIndexCreation = false;
    4488             : 
    4489         738 :     if (m_pszFidColumn == nullptr)
    4490           0 :         return false;
    4491             : 
    4492         738 :     if (HasSpatialIndex())
    4493             :     {
    4494           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Spatial index already existing");
    4495           1 :         return false;
    4496             :     }
    4497             : 
    4498         737 :     if (m_poFeatureDefn->GetGeomFieldCount() == 0)
    4499             :     {
    4500           1 :         CPLError(CE_Failure, CPLE_AppDefined, "No geometry column");
    4501           1 :         return false;
    4502             :     }
    4503         736 :     if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
    4504           0 :         return false;
    4505             : 
    4506         736 :     const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
    4507         736 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4508         736 :     const char *pszI = GetFIDColumn();
    4509             : 
    4510         736 :     m_osRTreeName = "rtree_";
    4511         736 :     m_osRTreeName += pszT;
    4512         736 :     m_osRTreeName += "_";
    4513         736 :     m_osRTreeName += pszC;
    4514         736 :     m_osFIDForRTree = m_pszFidColumn;
    4515             : 
    4516         736 :     bool bPopulateFromThreadRTree = false;
    4517         736 :     if (m_bThreadRTreeStarted)
    4518             :     {
    4519          12 :         const bool bThreadHasFinished = m_oQueueRTreeEntries.empty();
    4520          12 :         if (!m_aoRTreeEntries.empty())
    4521           4 :             m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
    4522          12 :         m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
    4523          12 :         m_oQueueRTreeEntries.push(m_aoRTreeEntries);
    4524          12 :         if (!bThreadHasFinished)
    4525           2 :             CPLDebug("GPKG", "Waiting for background RTree building to finish");
    4526          12 :         m_oThreadRTree.join();
    4527          12 :         if (!bThreadHasFinished)
    4528             :         {
    4529           2 :             CPLDebug("GPKG", "Background RTree building finished");
    4530             :         }
    4531          12 :         m_bAllowedRTreeThread = false;
    4532          12 :         m_bThreadRTreeStarted = false;
    4533             : 
    4534          12 :         if (m_hAsyncDBHandle)
    4535             :         {
    4536           8 :             sqlite3_close(m_hAsyncDBHandle);
    4537           8 :             m_hAsyncDBHandle = nullptr;
    4538             :         }
    4539          12 :         if (m_bErrorDuringRTreeThread)
    4540             :         {
    4541           4 :             RemoveAsyncRTreeTempDB();
    4542             :         }
    4543             :         else
    4544             :         {
    4545           8 :             bPopulateFromThreadRTree = true;
    4546             :         }
    4547             :     }
    4548             : 
    4549         736 :     m_poDS->SoftStartTransaction();
    4550             : 
    4551         736 :     if (m_hRTree)
    4552             :     {
    4553           4 :         if (!FlushInMemoryRTree(m_poDS->GetDB(), m_osRTreeName.c_str()))
    4554             :         {
    4555           0 :             m_poDS->SoftRollbackTransaction();
    4556           0 :             return false;
    4557             :         }
    4558             :     }
    4559         732 :     else if (bPopulateFromThreadRTree)
    4560             :     {
    4561             :         /* Create virtual table */
    4562           4 :         char *pszSQL = sqlite3_mprintf("CREATE VIRTUAL TABLE \"%w\" USING "
    4563             :                                        "rtree(id, minx, maxx, miny, maxy)",
    4564             :                                        m_osRTreeName.c_str());
    4565           4 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    4566           4 :         sqlite3_free(pszSQL);
    4567           4 :         if (err != OGRERR_NONE)
    4568             :         {
    4569           0 :             m_poDS->SoftRollbackTransaction();
    4570           0 :             return false;
    4571             :         }
    4572             : 
    4573           4 :         pszSQL = sqlite3_mprintf(
    4574             :             "DELETE FROM \"%w_node\";\n"
    4575             :             "INSERT INTO \"%w_node\" SELECT * FROM \"%w\".my_rtree_node;\n"
    4576             :             "INSERT INTO \"%w_rowid\" SELECT * FROM "
    4577             :             "\"%w\".my_rtree_rowid;\n"
    4578             :             "INSERT INTO \"%w_parent\" SELECT * FROM "
    4579             :             "\"%w\".my_rtree_parent;\n",
    4580             :             m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    4581             :             m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
    4582             :             m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
    4583             :             m_osAsyncDBAttachName.c_str());
    4584           4 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    4585           4 :         sqlite3_free(pszSQL);
    4586           4 :         if (err != OGRERR_NONE)
    4587             :         {
    4588           0 :             m_poDS->SoftRollbackTransaction();
    4589           0 :             RemoveAsyncRTreeTempDB();
    4590           0 :             return false;
    4591             :         }
    4592             :     }
    4593             :     else
    4594             :     {
    4595             :         /* Populate the RTree */
    4596         728 :         const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
    4597         728 :         char *pszErrMsg = nullptr;
    4598             : 
    4599             :         struct ProgressCbk
    4600             :         {
    4601         474 :             static bool progressCbk(const char *pszMessage, void *)
    4602             :             {
    4603         474 :                 CPLDebug("GPKG", "%s", pszMessage);
    4604         474 :                 return true;
    4605             :             }
    4606             :         };
    4607             : 
    4608        1456 :         if (!gdal_sqlite_rtree_bl_from_feature_table(
    4609         728 :                 m_poDS->GetDB(), pszT, pszI, pszC, m_osRTreeName.c_str(), "id",
    4610             :                 "minx", "miny", "maxx", "maxy", nMaxRAMUsageAllowed, &pszErrMsg,
    4611             :                 ProgressCbk::progressCbk, nullptr))
    4612             :         {
    4613           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4614             :                      "gdal_sqlite_rtree_bl_from_feature_table() failed "
    4615             :                      "with %s",
    4616           0 :                      pszErrMsg ? pszErrMsg : "(null)");
    4617           0 :             m_poDS->SoftRollbackTransaction();
    4618           0 :             sqlite3_free(pszErrMsg);
    4619           0 :             return false;
    4620             :         }
    4621             :     }
    4622             : 
    4623        1472 :     CPLString osSQL;
    4624             : 
    4625             :     /* Register the table in gpkg_extensions */
    4626         736 :     char *pszSQL = sqlite3_mprintf(
    4627             :         "INSERT INTO gpkg_extensions "
    4628             :         "(table_name,column_name,extension_name,definition,scope) "
    4629             :         "VALUES ('%q', '%q', 'gpkg_rtree_index', "
    4630             :         "'http://www.geopackage.org/spec120/#extension_rtree', 'write-only')",
    4631             :         pszT, pszC);
    4632         736 :     osSQL += pszSQL;
    4633         736 :     sqlite3_free(pszSQL);
    4634             : 
    4635             :     /* Define Triggers to Maintain Spatial Index Values */
    4636         736 :     osSQL += ";" + ReturnSQLCreateSpatialIndexTriggers(pszTableName, nullptr);
    4637             : 
    4638         736 :     err = SQLCommand(m_poDS->GetDB(), osSQL);
    4639         736 :     if (err != OGRERR_NONE)
    4640             :     {
    4641           0 :         m_poDS->SoftRollbackTransaction();
    4642           0 :         if (bPopulateFromThreadRTree)
    4643             :         {
    4644           0 :             RemoveAsyncRTreeTempDB();
    4645             :         }
    4646           0 :         return false;
    4647             :     }
    4648             : 
    4649         736 :     m_poDS->SoftCommitTransaction();
    4650             : 
    4651         736 :     if (bPopulateFromThreadRTree)
    4652             :     {
    4653           8 :         RemoveAsyncRTreeTempDB();
    4654             :     }
    4655             : 
    4656         736 :     m_bHasSpatialIndex = true;
    4657             : 
    4658         736 :     return true;
    4659             : }
    4660             : 
    4661             : /************************************************************************/
    4662             : /*                   WorkaroundUpdate1TriggerIssue()                    */
    4663             : /************************************************************************/
    4664             : 
    4665          18 : void OGRGeoPackageTableLayer::WorkaroundUpdate1TriggerIssue()
    4666             : {
    4667             :     // Workaround issue of https://sqlite.org/forum/forumpost/8c8de6ff91
    4668             :     // Basically the official _update1 spatial index trigger doesn't work
    4669             :     // with current versions of SQLite when invoked from an UPSERT statement.
    4670             :     // In GeoPackage 1.4, the update6 and update7 triggers replace update1
    4671             : 
    4672          18 :     if (m_bHasUpdate6And7Triggers || m_poFeatureDefn->GetGeomFieldCount() == 0)
    4673          12 :         return;
    4674             : 
    4675          11 :     const char *pszT = m_pszTableName;
    4676          11 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4677          11 :     const char *pszI = GetFIDColumn();
    4678             : 
    4679          11 :     CPLString osRTreeName = "rtree_";
    4680          11 :     osRTreeName += pszT;
    4681          11 :     osRTreeName += "_";
    4682          11 :     osRTreeName += pszC;
    4683             : 
    4684             :     // Check if update6 and update7 triggers are there
    4685             :     {
    4686          22 :         char *pszSQL = sqlite3_mprintf(
    4687             :             "SELECT * FROM sqlite_master WHERE type = 'trigger' "
    4688             :             "AND name IN ('%q', '%q')",
    4689          22 :             (m_osRTreeName + "_update6").c_str(),
    4690          22 :             (m_osRTreeName + "_update7").c_str());
    4691          11 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4692          11 :         sqlite3_free(pszSQL);
    4693          11 :         if (oResult && oResult->RowCount() == 2)
    4694             :         {
    4695           5 :             m_bHasUpdate6And7Triggers = true;
    4696           5 :             return;
    4697             :         }
    4698             :     }
    4699             : 
    4700             :     char *pszSQL =
    4701           6 :         sqlite3_mprintf("SELECT sql FROM sqlite_master WHERE type = 'trigger' "
    4702             :                         "AND name = '%q'",
    4703          12 :                         (m_osRTreeName + "_update1").c_str());
    4704           6 :     auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4705           6 :     sqlite3_free(pszSQL);
    4706           6 :     if (oResult && oResult->RowCount() == 1)
    4707             :     {
    4708           6 :         const char *pszTriggerSQL = oResult->GetValue(0, 0);
    4709           6 :         if (pszTriggerSQL)
    4710             :         {
    4711           6 :             m_osUpdate1Trigger = pszTriggerSQL;
    4712             :         }
    4713             :     }
    4714           6 :     if (m_osUpdate1Trigger.empty())
    4715           0 :         return;
    4716             : 
    4717           6 :     m_bUpdate1TriggerDisabled = true;
    4718             : 
    4719             :     pszSQL =
    4720           6 :         sqlite3_mprintf("DROP TRIGGER \"%w_update1\"", osRTreeName.c_str());
    4721           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4722           6 :     sqlite3_free(pszSQL);
    4723             : 
    4724           6 :     pszSQL = sqlite3_mprintf(
    4725             :         "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
    4726             :         "ON \"%w\" "
    4727             :         "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4728             :         "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4729             :         "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
    4730             :         "BEGIN "
    4731             :         "UPDATE \"%w\" SET "
    4732             :         "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
    4733             :         "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
    4734             :         "WHERE id = NEW.\"%w\";"
    4735             :         "END",
    4736             :         osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4737             :         osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
    4738           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4739           6 :     sqlite3_free(pszSQL);
    4740             : 
    4741           6 :     pszSQL = sqlite3_mprintf(
    4742             :         "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
    4743             :         "\"%w\" "
    4744             :         "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4745             :         "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4746             :         "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
    4747             :         "BEGIN "
    4748             :         "INSERT INTO \"%w\" VALUES ("
    4749             :         "NEW.\"%w\","
    4750             :         "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4751             :         "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4752             :         "); "
    4753             :         "END",
    4754             :         osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4755             :         osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4756           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4757           6 :     sqlite3_free(pszSQL);
    4758             : }
    4759             : 
    4760             : /************************************************************************/
    4761             : /*                RevertWorkaroundUpdate1TriggerIssue()                 */
    4762             : /************************************************************************/
    4763             : 
    4764        5059 : void OGRGeoPackageTableLayer::RevertWorkaroundUpdate1TriggerIssue()
    4765             : {
    4766        5059 :     if (!m_bUpdate1TriggerDisabled)
    4767        5053 :         return;
    4768           6 :     m_bUpdate1TriggerDisabled = false;
    4769           6 :     CPLAssert(!m_bHasUpdate6And7Triggers);
    4770             : 
    4771           6 :     const char *pszT = m_pszTableName;
    4772           6 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4773             : 
    4774          12 :     CPLString osRTreeName = "rtree_";
    4775           6 :     osRTreeName += pszT;
    4776           6 :     osRTreeName += "_";
    4777           6 :     osRTreeName += pszC;
    4778             : 
    4779             :     char *pszSQL;
    4780             : 
    4781           6 :     SQLCommand(m_poDS->GetDB(), m_osUpdate1Trigger.c_str());
    4782           6 :     m_osUpdate1Trigger.clear();
    4783             : 
    4784             :     pszSQL =
    4785           6 :         sqlite3_mprintf("DROP TRIGGER \"%w_update6\"", osRTreeName.c_str());
    4786           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4787           6 :     sqlite3_free(pszSQL);
    4788             : 
    4789             :     pszSQL =
    4790           6 :         sqlite3_mprintf("DROP TRIGGER \"%w_update7\"", osRTreeName.c_str());
    4791           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4792           6 :     sqlite3_free(pszSQL);
    4793             : }
    4794             : 
    4795             : /************************************************************************/
    4796             : /*                ReturnSQLCreateSpatialIndexTriggers()                 */
    4797             : /************************************************************************/
    4798             : 
    4799         748 : CPLString OGRGeoPackageTableLayer::ReturnSQLCreateSpatialIndexTriggers(
    4800             :     const char *pszTableName, const char *pszGeomColName)
    4801             : {
    4802             :     char *pszSQL;
    4803         748 :     CPLString osSQL;
    4804             : 
    4805         748 :     const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
    4806             :     const char *pszC = (pszGeomColName)
    4807         748 :                            ? pszGeomColName
    4808         744 :                            : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4809         748 :     const char *pszI = GetFIDColumn();
    4810             : 
    4811        1496 :     CPLString osRTreeName = "rtree_";
    4812         748 :     osRTreeName += pszT;
    4813         748 :     osRTreeName += "_";
    4814         748 :     osRTreeName += pszC;
    4815             : 
    4816             :     /* Conditions: Insertion of non-empty geometry
    4817             :        Actions   : Insert record into rtree */
    4818         748 :     pszSQL = sqlite3_mprintf(
    4819             :         "CREATE TRIGGER \"%w_insert\" AFTER INSERT ON \"%w\" "
    4820             :         "WHEN (new.\"%w\" NOT NULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
    4821             :         "BEGIN "
    4822             :         "INSERT OR REPLACE INTO \"%w\" VALUES ("
    4823             :         "NEW.\"%w\","
    4824             :         "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4825             :         "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4826             :         "); "
    4827             :         "END",
    4828             :         osRTreeName.c_str(), pszT, pszC, pszC, osRTreeName.c_str(), pszI, pszC,
    4829             :         pszC, pszC, pszC);
    4830         748 :     osSQL += pszSQL;
    4831         748 :     sqlite3_free(pszSQL);
    4832             : 
    4833         748 :     if (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
    4834         747 :         m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
    4835             :     {
    4836             :         /* Conditions: Update a non-empty geometry with another non-empty geometry
    4837             :            Actions   : Replace record from R-tree
    4838             :         */
    4839         736 :         pszSQL = sqlite3_mprintf(
    4840             :             "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
    4841             :             "ON \"%w\" "
    4842             :             "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4843             :             "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4844             :             "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
    4845             :             "BEGIN "
    4846             :             "UPDATE \"%w\" SET "
    4847             :             "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
    4848             :             "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
    4849             :             "WHERE id = NEW.\"%w\";"
    4850             :             "END",
    4851             :             osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4852             :             osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
    4853         736 :         osSQL += ";";
    4854         736 :         osSQL += pszSQL;
    4855         736 :         sqlite3_free(pszSQL);
    4856             : 
    4857             :         /* Conditions: Update a null/empty geometry with a non-empty geometry
    4858             :            Actions : Insert record into R-tree
    4859             :         */
    4860         736 :         pszSQL = sqlite3_mprintf(
    4861             :             "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
    4862             :             "\"%w\" "
    4863             :             "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4864             :             "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4865             :             "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
    4866             :             "BEGIN "
    4867             :             "INSERT INTO \"%w\" VALUES ("
    4868             :             "NEW.\"%w\","
    4869             :             "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4870             :             "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4871             :             "); "
    4872             :             "END",
    4873             :             osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4874             :             osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4875         736 :         osSQL += ";";
    4876         736 :         osSQL += pszSQL;
    4877         736 :         sqlite3_free(pszSQL);
    4878             :     }
    4879             :     else
    4880             :     {
    4881             :         /* Conditions: Update of geometry column to non-empty geometry
    4882             :                    No row ID change
    4883             :            Actions   : Update record in rtree */
    4884          12 :         pszSQL = sqlite3_mprintf(
    4885             :             "CREATE TRIGGER \"%w_update1\" AFTER UPDATE OF \"%w\" ON \"%w\" "
    4886             :             "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4887             :             "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
    4888             :             "BEGIN "
    4889             :             "INSERT OR REPLACE INTO \"%w\" VALUES ("
    4890             :             "NEW.\"%w\","
    4891             :             "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4892             :             "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4893             :             "); "
    4894             :             "END",
    4895             :             osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
    4896             :             osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4897          12 :         osSQL += ";";
    4898          12 :         osSQL += pszSQL;
    4899          12 :         sqlite3_free(pszSQL);
    4900             :     }
    4901             : 
    4902             :     /* Conditions: Update of geometry column to empty geometry
    4903             :                No row ID change
    4904             :        Actions   : Remove record from rtree */
    4905         748 :     pszSQL = sqlite3_mprintf(
    4906             :         "CREATE TRIGGER \"%w_update2\" AFTER UPDATE OF \"%w\" ON \"%w\" "
    4907             :         "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4908             :         "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
    4909             :         "BEGIN "
    4910             :         "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
    4911             :         "END",
    4912             :         osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
    4913             :         osRTreeName.c_str(), pszI);
    4914         748 :     osSQL += ";";
    4915         748 :     osSQL += pszSQL;
    4916         748 :     sqlite3_free(pszSQL);
    4917             : 
    4918             :     /* Conditions: Update of any column
    4919             :                     Row ID change
    4920             :                     Non-empty geometry
    4921             :         Actions   : Remove record from rtree for old <i>
    4922             :                     Insert record into rtree for new <i> */
    4923             :     pszSQL =
    4924        1496 :         sqlite3_mprintf("CREATE TRIGGER \"%w_%s\" AFTER UPDATE ON \"%w\" "
    4925             :                         "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
    4926             :                         "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
    4927             :                         "BEGIN "
    4928             :                         "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
    4929             :                         "INSERT OR REPLACE INTO \"%w\" VALUES ("
    4930             :                         "NEW.\"%w\","
    4931             :                         "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4932             :                         "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4933             :                         "); "
    4934             :                         "END",
    4935             :                         osRTreeName.c_str(),
    4936         748 :                         (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
    4937         747 :                          m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
    4938             :                             ? "update5"
    4939             :                             : "update3",
    4940             :                         pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(), pszI,
    4941             :                         osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4942         748 :     osSQL += ";";
    4943         748 :     osSQL += pszSQL;
    4944         748 :     sqlite3_free(pszSQL);
    4945             : 
    4946             :     /* Conditions: Update of any column
    4947             :                     Row ID change
    4948             :                     Empty geometry
    4949             :         Actions   : Remove record from rtree for old and new <i> */
    4950         748 :     pszSQL = sqlite3_mprintf(
    4951             :         "CREATE TRIGGER \"%w_update4\" AFTER UPDATE ON \"%w\" "
    4952             :         "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
    4953             :         "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
    4954             :         "BEGIN "
    4955             :         "DELETE FROM \"%w\" WHERE id IN (OLD.\"%w\", NEW.\"%w\"); "
    4956             :         "END",
    4957             :         osRTreeName.c_str(), pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(),
    4958             :         pszI, pszI);
    4959         748 :     osSQL += ";";
    4960         748 :     osSQL += pszSQL;
    4961         748 :     sqlite3_free(pszSQL);
    4962             : 
    4963             :     /* Conditions: Row deleted
    4964             :         Actions   : Remove record from rtree for old <i> */
    4965         748 :     pszSQL = sqlite3_mprintf(
    4966             :         "CREATE TRIGGER \"%w_delete\" AFTER DELETE ON \"%w\" "
    4967             :         "WHEN old.\"%w\" NOT NULL "
    4968             :         "BEGIN "
    4969             :         "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
    4970             :         "END",
    4971             :         osRTreeName.c_str(), pszT, pszC, osRTreeName.c_str(), pszI);
    4972         748 :     osSQL += ";";
    4973         748 :     osSQL += pszSQL;
    4974         748 :     sqlite3_free(pszSQL);
    4975             : 
    4976        1496 :     return osSQL;
    4977             : }
    4978             : 
    4979             : /************************************************************************/
    4980             : /*                    CheckUnknownExtensions()                          */
    4981             : /************************************************************************/
    4982             : 
    4983         897 : void OGRGeoPackageTableLayer::CheckUnknownExtensions()
    4984             : {
    4985             :     const std::map<CPLString, std::vector<GPKGExtensionDesc>> &oMap =
    4986         897 :         m_poDS->GetUnknownExtensionsTableSpecific();
    4987         897 :     const auto oIter = oMap.find(CPLString(m_pszTableName).toupper());
    4988         897 :     if (oIter != oMap.end())
    4989             :     {
    4990          14 :         for (size_t i = 0; i < oIter->second.size(); i++)
    4991             :         {
    4992           7 :             const char *pszExtName = oIter->second[i].osExtensionName.c_str();
    4993           7 :             const char *pszDefinition = oIter->second[i].osDefinition.c_str();
    4994           7 :             const char *pszScope = oIter->second[i].osScope.c_str();
    4995           7 :             if (m_poDS->GetUpdate() && EQUAL(pszScope, "write-only"))
    4996             :             {
    4997           1 :                 CPLError(
    4998             :                     CE_Warning, CPLE_AppDefined,
    4999             :                     "Layer %s relies on the '%s' (%s) extension that should "
    5000             :                     "be implemented for safe write-support, but is not "
    5001             :                     "currently. "
    5002             :                     "Update of that layer are strongly discouraged to avoid "
    5003             :                     "corruption.",
    5004             :                     GetName(), pszExtName, pszDefinition);
    5005             :             }
    5006           6 :             else if (m_poDS->GetUpdate() && EQUAL(pszScope, "read-write"))
    5007             :             {
    5008           1 :                 CPLError(
    5009             :                     CE_Warning, CPLE_AppDefined,
    5010             :                     "Layer %s relies on the '%s' (%s) extension that should "
    5011             :                     "be implemented in order to read/write it safely, but is "
    5012             :                     "not currently. "
    5013             :                     "Some data may be missing while reading that layer, and "
    5014             :                     "updates are strongly discouraged.",
    5015             :                     GetName(), pszExtName, pszDefinition);
    5016             :             }
    5017           5 :             else if (EQUAL(pszScope, "read-write") &&
    5018             :                      // None of the NGA extensions at
    5019             :                      // http://ngageoint.github.io/GeoPackage/docs/extensions/
    5020             :                      // affect read-only scenarios
    5021           3 :                      !STARTS_WITH(pszExtName, "nga_"))
    5022             :             {
    5023           3 :                 CPLError(
    5024             :                     CE_Warning, CPLE_AppDefined,
    5025             :                     "Layer %s relies on the '%s' (%s) extension that should "
    5026             :                     "be implemented in order to read it safely, but is not "
    5027             :                     "currently. "
    5028             :                     "Some data may be missing while reading that layer.",
    5029             :                     GetName(), pszExtName, pszDefinition);
    5030             :             }
    5031             :         }
    5032             :     }
    5033         897 : }
    5034             : 
    5035             : /************************************************************************/
    5036             : /*                     CreateGeometryExtensionIfNecessary()             */
    5037             : /************************************************************************/
    5038             : 
    5039      261404 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
    5040             :     const OGRGeometry *poGeom)
    5041             : {
    5042      261404 :     bool bRet = true;
    5043      261404 :     if (poGeom != nullptr)
    5044             :     {
    5045      261404 :         OGRwkbGeometryType eGType = wkbFlatten(poGeom->getGeometryType());
    5046      261404 :         if (eGType >= wkbGeometryCollection)
    5047             :         {
    5048          22 :             if (eGType > wkbGeometryCollection)
    5049          12 :                 CreateGeometryExtensionIfNecessary(eGType);
    5050             :             const OGRGeometryCollection *poGC =
    5051          22 :                 dynamic_cast<const OGRGeometryCollection *>(poGeom);
    5052          22 :             if (poGC != nullptr)
    5053             :             {
    5054          11 :                 const int nSubGeoms = poGC->getNumGeometries();
    5055          32 :                 for (int i = 0; i < nSubGeoms; i++)
    5056             :                 {
    5057          21 :                     bRet &= CreateGeometryExtensionIfNecessary(
    5058          21 :                         poGC->getGeometryRef(i));
    5059             :                 }
    5060             :             }
    5061             :         }
    5062             :     }
    5063      261404 :     return bRet;
    5064             : }
    5065             : 
    5066             : /************************************************************************/
    5067             : /*                     CreateGeometryExtensionIfNecessary()             */
    5068             : /************************************************************************/
    5069             : 
    5070          19 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
    5071             :     OGRwkbGeometryType eGType)
    5072             : {
    5073          19 :     eGType = wkbFlatten(eGType);
    5074          19 :     CPLAssert(eGType > wkbGeometryCollection && eGType <= wkbTriangle);
    5075          19 :     if (m_abHasGeometryExtension[eGType])
    5076           3 :         return true;
    5077             : 
    5078          16 :     if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
    5079           0 :         return false;
    5080             : 
    5081          16 :     const char *pszT = m_pszTableName;
    5082          16 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5083          16 :     const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
    5084             : 
    5085             :     // Check first if the extension isn't registered
    5086          16 :     char *pszSQL = sqlite3_mprintf(
    5087             :         "SELECT 1 FROM gpkg_extensions WHERE lower(table_name) = lower('%q') "
    5088             :         "AND "
    5089             :         "lower(column_name) = lower('%q') AND extension_name = 'gpkg_geom_%s'",
    5090             :         pszT, pszC, pszGeometryType);
    5091          16 :     const bool bExists = SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
    5092          16 :     sqlite3_free(pszSQL);
    5093             : 
    5094          16 :     if (!bExists)
    5095             :     {
    5096          15 :         if (eGType == wkbPolyhedralSurface || eGType == wkbTIN ||
    5097             :             eGType == wkbTriangle)
    5098             :         {
    5099           2 :             CPLError(CE_Warning, CPLE_AppDefined,
    5100             :                      "Registering non-standard gpkg_geom_%s extension",
    5101             :                      pszGeometryType);
    5102             :         }
    5103             : 
    5104             :         /* Register the table in gpkg_extensions */
    5105          15 :         pszSQL = sqlite3_mprintf(
    5106             :             "INSERT INTO gpkg_extensions "
    5107             :             "(table_name,column_name,extension_name,definition,scope) "
    5108             :             "VALUES ('%q', '%q', 'gpkg_geom_%s', "
    5109             :             "'http://www.geopackage.org/spec120/#extension_geometry_types', "
    5110             :             "'read-write')",
    5111             :             pszT, pszC, pszGeometryType);
    5112          15 :         OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
    5113          15 :         sqlite3_free(pszSQL);
    5114          15 :         if (err != OGRERR_NONE)
    5115           0 :             return false;
    5116             :     }
    5117             : 
    5118          16 :     m_abHasGeometryExtension[eGType] = true;
    5119          16 :     return true;
    5120             : }
    5121             : 
    5122             : /************************************************************************/
    5123             : /*                        HasSpatialIndex()                             */
    5124             : /************************************************************************/
    5125             : 
    5126       47477 : bool OGRGeoPackageTableLayer::HasSpatialIndex() const
    5127             : {
    5128       47477 :     if (!m_bFeatureDefnCompleted)
    5129           3 :         GetLayerDefn();
    5130       47477 :     if (m_bHasSpatialIndex >= 0)
    5131       46503 :         return CPL_TO_BOOL(m_bHasSpatialIndex);
    5132         974 :     m_bHasSpatialIndex = false;
    5133             : 
    5134        2922 :     if (m_pszFidColumn == nullptr ||
    5135        1938 :         m_poFeatureDefn->GetGeomFieldCount() == 0 ||
    5136         964 :         !m_poDS->HasExtensionsTable())
    5137         588 :         return false;
    5138             : 
    5139         386 :     const char *pszT = m_pszTableName;
    5140         386 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5141             :     CPLString osRTreeName(
    5142         772 :         CPLString("rtree_").append(pszT).append("_").append(pszC));
    5143             :     const std::map<CPLString, CPLString> &oMap =
    5144         386 :         m_poDS->GetNameTypeMapFromSQliteMaster();
    5145         386 :     if (cpl::contains(oMap, CPLString(osRTreeName).toupper()))
    5146             :     {
    5147         209 :         m_bHasSpatialIndex = true;
    5148         209 :         m_osRTreeName = std::move(osRTreeName);
    5149         209 :         m_osFIDForRTree = m_pszFidColumn;
    5150             :     }
    5151             : 
    5152             :     // Add heuristics to try to detect corrupted RTree generated by GDAL 3.6.0
    5153             :     // Cf https://github.com/OSGeo/gdal/pull/6911
    5154         386 :     if (m_bHasSpatialIndex)
    5155             :     {
    5156             :         const auto nFC =
    5157         209 :             const_cast<OGRGeoPackageTableLayer *>(this)->GetTotalFeatureCount();
    5158         209 :         if (nFC >= atoi(CPLGetConfigOption(
    5159             :                        "OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100000")))
    5160             :         {
    5161           2 :             CPLString osSQL = "SELECT 1 FROM \"";
    5162           1 :             osSQL += SQLEscapeName(pszT);
    5163           1 :             osSQL += "\" WHERE \"";
    5164           2 :             osSQL += SQLEscapeName(
    5165           1 :                 const_cast<OGRGeoPackageTableLayer *>(this)->GetFIDColumn());
    5166           1 :             osSQL += "\" = ";
    5167           1 :             osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
    5168           1 :             osSQL += " AND \"";
    5169           1 :             osSQL += SQLEscapeName(pszC);
    5170           1 :             osSQL += "\" IS NOT NULL AND NOT ST_IsEmpty(\"";
    5171           1 :             osSQL += SQLEscapeName(pszC);
    5172           1 :             osSQL += "\")";
    5173           1 :             if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 1)
    5174             :             {
    5175           1 :                 osSQL = "SELECT 1 FROM \"";
    5176           1 :                 osSQL += SQLEscapeName(m_osRTreeName);
    5177           1 :                 osSQL += "\" WHERE id = ";
    5178           1 :                 osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
    5179           1 :                 if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 0)
    5180             :                 {
    5181           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    5182             :                              "Spatial index (perhaps created with GDAL 3.6.0) "
    5183             :                              "of table %s is corrupted. Disabling its use. "
    5184             :                              "This file should be recreated or its spatial "
    5185             :                              "index recreated",
    5186           1 :                              m_pszTableName);
    5187           1 :                     m_bHasSpatialIndex = false;
    5188             :                 }
    5189             :             }
    5190             :         }
    5191             :     }
    5192             : 
    5193         386 :     return CPL_TO_BOOL(m_bHasSpatialIndex);
    5194             : }
    5195             : 
    5196             : /************************************************************************/
    5197             : /*                        DropSpatialIndex()                            */
    5198             : /************************************************************************/
    5199             : 
    5200          41 : bool OGRGeoPackageTableLayer::DropSpatialIndex(bool bCalledFromSQLFunction)
    5201             : {
    5202          41 :     if (!m_bFeatureDefnCompleted)
    5203           0 :         GetLayerDefn();
    5204          41 :     if (!CheckUpdatableTable("DropSpatialIndex"))
    5205           1 :         return false;
    5206             : 
    5207          40 :     if (m_bDropRTreeTable)
    5208             :     {
    5209           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5210             :                  "Cannot run DropSpatialIndex() after non-completed deferred "
    5211             :                  "DropSpatialIndex()");
    5212           0 :         return false;
    5213             :     }
    5214             : 
    5215          40 :     if (!HasSpatialIndex())
    5216             :     {
    5217           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Spatial index not existing");
    5218           1 :         return false;
    5219             :     }
    5220             : 
    5221          39 :     const char *pszT = m_pszTableName;
    5222          39 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5223             :     {
    5224          39 :         char *pszSQL = sqlite3_mprintf(
    5225             :             "DELETE FROM gpkg_extensions WHERE lower(table_name)=lower('%q') "
    5226             :             "AND lower(column_name)=lower('%q') AND "
    5227             :             "extension_name='gpkg_rtree_index'",
    5228             :             pszT, pszC);
    5229          39 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    5230          39 :         sqlite3_free(pszSQL);
    5231             :     }
    5232             : 
    5233          39 :     if (bCalledFromSQLFunction)
    5234             :     {
    5235             :         /* We cannot drop a table from a SQLite function call, so we just */
    5236             :         /* memorize that we will have to delete the table later */
    5237           6 :         m_bDropRTreeTable = true;
    5238             :     }
    5239             :     else
    5240             :     {
    5241             :         char *pszSQL =
    5242          33 :             sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
    5243          33 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    5244          33 :         sqlite3_free(pszSQL);
    5245             :     }
    5246             : 
    5247          39 :     m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
    5248             : 
    5249          39 :     SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers().c_str());
    5250             : 
    5251          39 :     m_bHasSpatialIndex = false;
    5252          39 :     return true;
    5253             : }
    5254             : 
    5255             : /************************************************************************/
    5256             : /*               RunDeferredDropRTreeTableIfNecessary()                 */
    5257             : /************************************************************************/
    5258             : 
    5259        1595 : bool OGRGeoPackageTableLayer::RunDeferredDropRTreeTableIfNecessary()
    5260             : {
    5261        1595 :     bool ret = true;
    5262        1595 :     if (m_bDropRTreeTable)
    5263             :     {
    5264           6 :         OGRGeoPackageTableLayer::ResetReading();
    5265             : 
    5266             :         char *pszSQL =
    5267           6 :             sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
    5268           6 :         ret = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
    5269           6 :         sqlite3_free(pszSQL);
    5270           6 :         m_bDropRTreeTable = false;
    5271             :     }
    5272        1595 :     return ret;
    5273             : }
    5274             : 
    5275             : /************************************************************************/
    5276             : /*                   ReturnSQLDropSpatialIndexTriggers()                */
    5277             : /************************************************************************/
    5278             : 
    5279          58 : CPLString OGRGeoPackageTableLayer::ReturnSQLDropSpatialIndexTriggers()
    5280             : {
    5281          58 :     char *pszSQL = sqlite3_mprintf(
    5282             :         "DROP TRIGGER \"%w_insert\";"
    5283             :         "DROP TRIGGER IF EXISTS \"%w_update1\";"  // replaced by update6 and update7 in GPKG 1.4
    5284             :         "DROP TRIGGER \"%w_update2\";"
    5285             :         "DROP TRIGGER IF EXISTS \"%w_update3\";"  // replace by update5 in GPKG 1.4
    5286             :         "DROP TRIGGER \"%w_update4\";"
    5287             :         "DROP TRIGGER IF EXISTS \"%w_update5\";"  // replace update3 in GPKG 1.4
    5288             :         "DROP TRIGGER IF EXISTS \"%w_update6\";"  // replace update1 in GPKG 1.4
    5289             :         "DROP TRIGGER IF EXISTS \"%w_update7\";"  // replace update1 in GPKG 1.4
    5290             :         "DROP TRIGGER \"%w_delete\";",
    5291             :         m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    5292             :         m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    5293             :         m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str());
    5294          58 :     CPLString osSQL(pszSQL);
    5295          58 :     sqlite3_free(pszSQL);
    5296             : 
    5297          58 :     return osSQL;
    5298             : }
    5299             : 
    5300             : /************************************************************************/
    5301             : /*                           Rename()                                   */
    5302             : /************************************************************************/
    5303             : 
    5304          12 : OGRErr OGRGeoPackageTableLayer::Rename(const char *pszDstTableName)
    5305             : {
    5306          12 :     if (!m_bFeatureDefnCompleted)
    5307           2 :         GetLayerDefn();
    5308          12 :     if (!CheckUpdatableTable("Rename"))
    5309           0 :         return OGRERR_FAILURE;
    5310             : 
    5311          12 :     ResetReading();
    5312          12 :     SyncToDisk();
    5313             : 
    5314          12 :     char *pszSQL = sqlite3_mprintf(
    5315             :         "SELECT 1 FROM sqlite_master WHERE lower(name) = lower('%q') "
    5316             :         "AND type IN ('table', 'view')",
    5317             :         pszDstTableName);
    5318             :     const bool bAlreadyExists =
    5319          12 :         SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
    5320          12 :     sqlite3_free(pszSQL);
    5321          12 :     if (bAlreadyExists)
    5322             :     {
    5323           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
    5324             :                  pszDstTableName);
    5325           3 :         return OGRERR_FAILURE;
    5326             :     }
    5327             : 
    5328             :     // Temporary remove foreign key checks
    5329             :     const GPKGTemporaryForeignKeyCheckDisabler
    5330          18 :         oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
    5331             : 
    5332           9 :     if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    5333             :     {
    5334           0 :         return OGRERR_FAILURE;
    5335             :     }
    5336             : 
    5337             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    5338           9 :     DisableFeatureCountTriggers(false);
    5339             : #endif
    5340             : 
    5341          18 :     CPLString osSQL;
    5342             : 
    5343           9 :     pszSQL = sqlite3_mprintf(
    5344             :         "UPDATE gpkg_geometry_columns SET table_name = '%q' WHERE "
    5345             :         "lower(table_name )= lower('%q');",
    5346             :         pszDstTableName, m_pszTableName);
    5347           9 :     osSQL += pszSQL;
    5348           9 :     sqlite3_free(pszSQL);
    5349             : 
    5350             :     // Rename the identifier if it defaulted to the table name
    5351           9 :     pszSQL = sqlite3_mprintf(
    5352             :         "UPDATE gpkg_contents SET identifier = '%q' WHERE "
    5353             :         "lower(table_name) = lower('%q') AND identifier = '%q';",
    5354             :         pszDstTableName, m_pszTableName, m_pszTableName);
    5355           9 :     osSQL += pszSQL;
    5356           9 :     sqlite3_free(pszSQL);
    5357             : 
    5358           9 :     pszSQL = sqlite3_mprintf("UPDATE gpkg_contents SET table_name = '%q' WHERE "
    5359             :                              "lower(table_name )= lower('%q');",
    5360             :                              pszDstTableName, m_pszTableName);
    5361           9 :     osSQL += pszSQL;
    5362           9 :     sqlite3_free(pszSQL);
    5363             : 
    5364           9 :     if (m_poDS->HasExtensionsTable())
    5365             :     {
    5366           8 :         pszSQL = sqlite3_mprintf(
    5367             :             "UPDATE gpkg_extensions SET table_name = '%q' WHERE "
    5368             :             "lower(table_name )= lower('%q');",
    5369             :             pszDstTableName, m_pszTableName);
    5370           8 :         osSQL += pszSQL;
    5371           8 :         sqlite3_free(pszSQL);
    5372             :     }
    5373             : 
    5374           9 :     if (m_poDS->HasMetadataTables())
    5375             :     {
    5376           4 :         pszSQL = sqlite3_mprintf(
    5377             :             "UPDATE gpkg_metadata_reference SET table_name = '%q' WHERE "
    5378             :             "lower(table_name )= lower('%q');",
    5379             :             pszDstTableName, m_pszTableName);
    5380           4 :         osSQL += pszSQL;
    5381           4 :         sqlite3_free(pszSQL);
    5382             :     }
    5383             : 
    5384           9 :     if (m_poDS->HasDataColumnsTable())
    5385             :     {
    5386           1 :         pszSQL = sqlite3_mprintf(
    5387             :             "UPDATE gpkg_data_columns SET table_name = '%q' WHERE "
    5388             :             "lower(table_name )= lower('%q');",
    5389             :             pszDstTableName, m_pszTableName);
    5390           1 :         osSQL += pszSQL;
    5391           1 :         sqlite3_free(pszSQL);
    5392             :     }
    5393             : 
    5394             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    5395           9 :     if (m_poDS->m_bHasGPKGOGRContents)
    5396             :     {
    5397           9 :         pszSQL = sqlite3_mprintf(
    5398             :             "UPDATE gpkg_ogr_contents SET table_name = '%q' WHERE "
    5399             :             "lower(table_name )= lower('%q');",
    5400             :             pszDstTableName, m_pszTableName);
    5401           9 :         osSQL += pszSQL;
    5402           9 :         sqlite3_free(pszSQL);
    5403             :     }
    5404             : #endif
    5405             : 
    5406           9 :     if (m_poDS->HasGpkgextRelationsTable())
    5407             :     {
    5408           2 :         pszSQL = sqlite3_mprintf(
    5409             :             "UPDATE gpkgext_relations SET base_table_name = '%q' WHERE "
    5410             :             "lower(base_table_name )= lower('%q');",
    5411             :             pszDstTableName, m_pszTableName);
    5412           2 :         osSQL += pszSQL;
    5413           2 :         sqlite3_free(pszSQL);
    5414             : 
    5415           2 :         pszSQL = sqlite3_mprintf(
    5416             :             "UPDATE gpkgext_relations SET related_table_name = '%q' WHERE "
    5417             :             "lower(related_table_name )= lower('%q');",
    5418             :             pszDstTableName, m_pszTableName);
    5419           2 :         osSQL += pszSQL;
    5420             :         ;
    5421           2 :         sqlite3_free(pszSQL);
    5422             : 
    5423           2 :         pszSQL = sqlite3_mprintf(
    5424             :             "UPDATE gpkgext_relations SET mapping_table_name = '%q' WHERE "
    5425             :             "lower(mapping_table_name )= lower('%q');",
    5426             :             pszDstTableName, m_pszTableName);
    5427           2 :         osSQL += pszSQL;
    5428           2 :         sqlite3_free(pszSQL);
    5429             :     }
    5430             : 
    5431           9 :     if (m_poDS->HasQGISLayerStyles())
    5432             :     {
    5433             :         pszSQL =
    5434           1 :             sqlite3_mprintf("UPDATE layer_styles SET f_table_name = '%q' WHERE "
    5435             :                             "f_table_name = '%q';",
    5436             :                             pszDstTableName, m_pszTableName);
    5437           1 :         osSQL += pszSQL;
    5438           1 :         sqlite3_free(pszSQL);
    5439             :     }
    5440             : 
    5441           9 :     pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
    5442             :                              m_pszTableName, pszDstTableName);
    5443           9 :     osSQL += pszSQL;
    5444           9 :     sqlite3_free(pszSQL);
    5445             : 
    5446           9 :     const bool bHasSpatialIndex = HasSpatialIndex();
    5447           9 :     CPLString osRTreeNameNew;
    5448           9 :     if (bHasSpatialIndex)
    5449             :     {
    5450           8 :         osRTreeNameNew = "rtree_";
    5451           8 :         osRTreeNameNew += pszDstTableName;
    5452           8 :         osRTreeNameNew += "_";
    5453           8 :         osRTreeNameNew += m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5454             : 
    5455           8 :         osSQL += ReturnSQLDropSpatialIndexTriggers();
    5456           8 :         osSQL += ';';
    5457             : 
    5458           8 :         pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
    5459             :                                  m_osRTreeName.c_str(), osRTreeNameNew.c_str());
    5460           8 :         osSQL += pszSQL;
    5461           8 :         sqlite3_free(pszSQL);
    5462             : 
    5463           8 :         osSQL += ReturnSQLCreateSpatialIndexTriggers(pszDstTableName, nullptr);
    5464             :     }
    5465             : 
    5466           9 :     OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL);
    5467             : 
    5468             :     // Check foreign key integrity
    5469           9 :     if (eErr == OGRERR_NONE)
    5470             :     {
    5471           9 :         eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
    5472             :     }
    5473             : 
    5474           9 :     if (eErr == OGRERR_NONE)
    5475             :     {
    5476             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    5477           9 :         CreateFeatureCountTriggers(pszDstTableName);
    5478             : #endif
    5479             : 
    5480           9 :         eErr = m_poDS->SoftCommitTransaction();
    5481           9 :         if (eErr == OGRERR_NONE)
    5482             :         {
    5483           9 :             m_poDS->RemoveTableFromSQLiteMasterCache(m_pszTableName);
    5484             : 
    5485           9 :             CPLFree(m_pszTableName);
    5486           9 :             m_pszTableName = CPLStrdup(pszDstTableName);
    5487             : 
    5488           9 :             if (bHasSpatialIndex)
    5489             :             {
    5490           8 :                 m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
    5491           8 :                 m_osRTreeName = std::move(osRTreeNameNew);
    5492             :             }
    5493             :         }
    5494             :     }
    5495             :     else
    5496             :     {
    5497           0 :         m_poDS->SoftRollbackTransaction();
    5498             :     }
    5499             : 
    5500           9 :     if (eErr == OGRERR_NONE)
    5501             :     {
    5502           9 :         m_poDS->ClearCachedRelationships();
    5503             : 
    5504           9 :         SetDescription(pszDstTableName);
    5505           9 :         whileUnsealing(m_poFeatureDefn)->SetName(pszDstTableName);
    5506             :     }
    5507             : 
    5508           9 :     return eErr;
    5509             : }
    5510             : 
    5511             : /************************************************************************/
    5512             : /*                         ISetSpatialFilter()                          */
    5513             : /************************************************************************/
    5514             : 
    5515       23099 : OGRErr OGRGeoPackageTableLayer::ISetSpatialFilter(int /*iGeomField*/,
    5516             :                                                   const OGRGeometry *poGeomIn)
    5517             : 
    5518             : {
    5519       23099 :     if (!m_bFeatureDefnCompleted)
    5520           0 :         GetLayerDefn();
    5521       23099 :     if (InstallFilter(poGeomIn))
    5522             :     {
    5523       23066 :         BuildWhere();
    5524             : 
    5525       23066 :         ResetReading();
    5526             :     }
    5527       23099 :     return OGRERR_NONE;
    5528             : }
    5529             : 
    5530             : /************************************************************************/
    5531             : /*                        HasFastSpatialFilter()                        */
    5532             : /************************************************************************/
    5533             : 
    5534           2 : bool OGRGeoPackageTableLayer::HasFastSpatialFilter(int m_iGeomColIn)
    5535             : {
    5536           4 :     if (m_iGeomColIn < 0 ||
    5537           2 :         m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
    5538           0 :         return false;
    5539           2 :     return HasSpatialIndex();
    5540             : }
    5541             : 
    5542             : /************************************************************************/
    5543             : /*                           GetSpatialWhere()                          */
    5544             : /************************************************************************/
    5545             : 
    5546       23173 : CPLString OGRGeoPackageTableLayer::GetSpatialWhere(int m_iGeomColIn,
    5547             :                                                    OGRGeometry *poFilterGeom)
    5548             : {
    5549       23173 :     CPLString osSpatialWHERE;
    5550             : 
    5551       46346 :     if (m_iGeomColIn < 0 ||
    5552       23173 :         m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
    5553           2 :         return osSpatialWHERE;
    5554             : 
    5555       23171 :     if (poFilterGeom != nullptr)
    5556             :     {
    5557       23083 :         OGREnvelope sEnvelope;
    5558             : 
    5559       23083 :         poFilterGeom->getEnvelope(&sEnvelope);
    5560             : 
    5561             :         const char *pszC =
    5562       23083 :             m_poFeatureDefn->GetGeomFieldDefn(m_iGeomColIn)->GetNameRef();
    5563             : 
    5564       23091 :         if (std::isinf(sEnvelope.MinX) && sEnvelope.MinX < 0 &&
    5565          12 :             std::isinf(sEnvelope.MinY) && sEnvelope.MinY < 0 &&
    5566          12 :             std::isinf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 &&
    5567       23091 :             std::isinf(sEnvelope.MaxY) && sEnvelope.MaxY > 0)
    5568             :         {
    5569             :             osSpatialWHERE.Printf(
    5570             :                 "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
    5571           4 :                 SQLEscapeName(pszC).c_str(), SQLEscapeName(pszC).c_str());
    5572         106 :             return osSpatialWHERE;
    5573             :         }
    5574             : 
    5575       23079 :         bool bUseSpatialIndex = true;
    5576       46140 :         if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
    5577         174 :             sEnvelope.MinY <= m_poExtent->MinY &&
    5578       46249 :             sEnvelope.MaxX >= m_poExtent->MaxX &&
    5579         109 :             sEnvelope.MaxY >= m_poExtent->MaxY)
    5580             :         {
    5581             :             // Selecting from spatial filter on whole extent can be rather
    5582             :             // slow. So use function based filtering, just in case the
    5583             :             // advertized global extent might be wrong. Otherwise we might
    5584             :             // just discard completely the spatial filter.
    5585         106 :             bUseSpatialIndex = false;
    5586             :         }
    5587             : 
    5588       23079 :         if (bUseSpatialIndex && HasSpatialIndex())
    5589             :         {
    5590             :             osSpatialWHERE.Printf(
    5591             :                 "\"%s\" IN ( SELECT id FROM \"%s\" WHERE "
    5592             :                 "maxx >= %.12f AND minx <= %.12f AND "
    5593             :                 "maxy >= %.12f AND miny <= %.12f)",
    5594       45940 :                 SQLEscapeName(m_osFIDForRTree).c_str(),
    5595       45940 :                 SQLEscapeName(m_osRTreeName).c_str(), sEnvelope.MinX - 1e-11,
    5596       22970 :                 sEnvelope.MaxX + 1e-11, sEnvelope.MinY - 1e-11,
    5597       68910 :                 sEnvelope.MaxY + 1e-11);
    5598             :         }
    5599             :         else
    5600             :         {
    5601         109 :             if (HasSpatialIndex())
    5602             :             {
    5603             :                 // If we do have a spatial index, and our filter contains the
    5604             :                 // bounding box of the RTree, then just filter on non-null
    5605             :                 // non-empty geometries.
    5606             :                 double minx, miny, maxx, maxy;
    5607         103 :                 if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, minx,
    5608         103 :                                        miny, maxx, maxy) &&
    5609         103 :                     sEnvelope.MinX <= minx && sEnvelope.MinY <= miny &&
    5610         206 :                     sEnvelope.MaxX >= maxx && sEnvelope.MaxY >= maxy)
    5611             :                 {
    5612             :                     osSpatialWHERE.Printf(
    5613             :                         "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
    5614         204 :                         SQLEscapeName(pszC).c_str(),
    5615         306 :                         SQLEscapeName(pszC).c_str());
    5616         102 :                     return osSpatialWHERE;
    5617             :                 }
    5618             :             }
    5619             : 
    5620             :             /* A bit inefficient but still faster than OGR filtering */
    5621             :             osSpatialWHERE.Printf(
    5622             :                 "ST_EnvelopesIntersects(\"%s\", %.12f, %.12f, %.12f, %.12f)",
    5623           7 :                 SQLEscapeName(pszC).c_str(), sEnvelope.MinX - 1e-11,
    5624           7 :                 sEnvelope.MinY - 1e-11, sEnvelope.MaxX + 1e-11,
    5625           7 :                 sEnvelope.MaxY + 1e-11);
    5626             :         }
    5627             :     }
    5628             : 
    5629       23065 :     return osSpatialWHERE;
    5630             : }
    5631             : 
    5632             : /************************************************************************/
    5633             : /*                             BuildWhere()                             */
    5634             : /*                                                                      */
    5635             : /*      Build the WHERE statement appropriate to the current set of     */
    5636             : /*      criteria (spatial and attribute queries).                       */
    5637             : /************************************************************************/
    5638             : 
    5639       23153 : void OGRGeoPackageTableLayer::BuildWhere()
    5640             : 
    5641             : {
    5642       23153 :     m_soFilter = "";
    5643             : 
    5644             :     CPLString osSpatialWHERE =
    5645       46306 :         GetSpatialWhere(m_iGeomFieldFilter, m_poFilterGeom);
    5646       23153 :     if (!osSpatialWHERE.empty())
    5647             :     {
    5648       23063 :         m_soFilter += osSpatialWHERE;
    5649             :     }
    5650             : 
    5651       23153 :     if (!osQuery.empty())
    5652             :     {
    5653          45 :         if (m_soFilter.empty())
    5654             :         {
    5655          39 :             m_soFilter += osQuery;
    5656             :         }
    5657             :         else
    5658             :         {
    5659           6 :             m_soFilter += " AND (";
    5660           6 :             m_soFilter += osQuery;
    5661           6 :             m_soFilter += ")";
    5662             :         }
    5663             :     }
    5664       23153 :     CPLDebug("GPKG", "Filter: %s", m_soFilter.c_str());
    5665       23153 : }
    5666             : 
    5667             : /************************************************************************/
    5668             : /*                        SetOpeningParameters()                        */
    5669             : /************************************************************************/
    5670             : 
    5671        3148 : void OGRGeoPackageTableLayer::SetOpeningParameters(
    5672             :     const char *pszTableName, const char *pszObjectType, bool bIsInGpkgContents,
    5673             :     bool bIsSpatial, const char *pszGeomColName, const char *pszGeomType,
    5674             :     bool bHasZ, bool bHasM)
    5675             : {
    5676        3148 :     CPLFree(m_pszTableName);
    5677        3148 :     m_pszTableName = CPLStrdup(pszTableName);
    5678        3148 :     m_bIsTable = EQUAL(pszObjectType, "table");
    5679        3148 :     m_bIsInGpkgContents = bIsInGpkgContents;
    5680        3148 :     m_bIsSpatial = bIsSpatial;
    5681        3148 :     if (pszGeomType)
    5682             :     {
    5683             :         OGRwkbGeometryType eType =
    5684        1034 :             GPkgGeometryTypeToWKB(pszGeomType, bHasZ, bHasM);
    5685        1034 :         m_poFeatureDefn->SetGeomType(eType);
    5686        1034 :         if (eType != wkbNone)
    5687             :         {
    5688        1034 :             m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName);
    5689             :         }
    5690             :     }
    5691        3148 : }
    5692             : 
    5693             : /************************************************************************/
    5694             : /*                        SetCreationParameters()                       */
    5695             : /************************************************************************/
    5696             : 
    5697         823 : void OGRGeoPackageTableLayer::SetCreationParameters(
    5698             :     OGRwkbGeometryType eGType, const char *pszGeomColumnName, int bGeomNullable,
    5699             :     const OGRSpatialReference *poSRS, const char *pszSRID,
    5700             :     const OGRGeomCoordinatePrecision &oCoordPrec, bool bDiscardCoordLSB,
    5701             :     bool bUndoDiscardCoordLSBOnReading, const char *pszFIDColumnName,
    5702             :     const char *pszIdentifier, const char *pszDescription)
    5703             : {
    5704         823 :     m_bIsSpatial = eGType != wkbNone;
    5705         823 :     m_bIsInGpkgContents =
    5706         895 :         m_bIsSpatial ||
    5707          72 :         !m_poDS->HasNonSpatialTablesNonRegisteredInGpkgContents();
    5708         823 :     m_bFeatureDefnCompleted = true;
    5709         823 :     m_bDeferredCreation = true;
    5710         823 :     m_bTableCreatedInTransaction = m_poDS->IsInTransaction();
    5711         823 :     m_bHasTriedDetectingFID64 = true;
    5712         823 :     m_pszFidColumn = CPLStrdup(pszFIDColumnName);
    5713         823 :     m_bUndoDiscardCoordLSBOnReading = bUndoDiscardCoordLSBOnReading;
    5714             : 
    5715         823 :     if (eGType != wkbNone)
    5716             :     {
    5717         751 :         m_nZFlag = wkbHasZ(eGType) ? 1 : 0;
    5718         751 :         m_nMFlag = wkbHasM(eGType) ? 1 : 0;
    5719             : 
    5720             :         std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser>
    5721         751 :             poGotSRS;
    5722        1502 :         OGRGeomFieldDefn oGeomFieldDefn(pszGeomColumnName, eGType);
    5723             : 
    5724         751 :         oGeomFieldDefn.SetSpatialRef(poSRS);
    5725         751 :         if (pszSRID)
    5726             :         {
    5727          10 :             m_iSrs = atoi(pszSRID);
    5728          10 :             if (m_iSrs == GDALGeoPackageDataset::FIRST_CUSTOM_SRSID - 1)
    5729             :             {
    5730           1 :                 m_iSrs = m_poDS->GetSrsId(nullptr);
    5731           1 :                 oGeomFieldDefn.SetSpatialRef(nullptr);
    5732             :             }
    5733             :             else
    5734             :             {
    5735             :                 poGotSRS =
    5736          18 :                     m_poDS->GetSpatialRef(m_iSrs, /* bFallbackToEPSG = */ false,
    5737           9 :                                           /* bEmitErrorIfNotFound = */ false);
    5738           9 :                 if (poGotSRS)
    5739             :                 {
    5740           6 :                     oGeomFieldDefn.SetSpatialRef(poGotSRS.get());
    5741             :                 }
    5742             :                 else
    5743             :                 {
    5744           3 :                     bool bOK = false;
    5745           3 :                     OGRSpatialReference *poSRSTmp = new OGRSpatialReference();
    5746           3 :                     if (m_iSrs < 32767)
    5747             :                     {
    5748             :                         CPLErrorHandlerPusher oErrorHandler(
    5749           4 :                             CPLQuietErrorHandler);
    5750           4 :                         CPLErrorStateBackuper oBackuper;
    5751           2 :                         if (poSRSTmp->importFromEPSG(m_iSrs) == OGRERR_NONE)
    5752             :                         {
    5753           1 :                             bOK = true;
    5754           1 :                             poSRSTmp->SetAxisMappingStrategy(
    5755             :                                 OAMS_TRADITIONAL_GIS_ORDER);
    5756           1 :                             m_iSrs = m_poDS->GetSrsId(poSRSTmp);
    5757           1 :                             oGeomFieldDefn.SetSpatialRef(poSRSTmp);
    5758             :                         }
    5759             :                     }
    5760           3 :                     if (!bOK)
    5761             :                     {
    5762           2 :                         CPLError(
    5763             :                             CE_Warning, CPLE_AppDefined,
    5764             :                             "No entry in gpkg_spatial_ref_sys matching SRID=%s",
    5765             :                             pszSRID);
    5766             :                     }
    5767           3 :                     poSRSTmp->Release();
    5768             :                 }
    5769             :             }
    5770             :         }
    5771             :         else
    5772             :         {
    5773         741 :             m_iSrs = m_poDS->GetSrsId(poSRS);
    5774             :         }
    5775         751 :         oGeomFieldDefn.SetNullable(bGeomNullable);
    5776         751 :         oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec);
    5777             : 
    5778         751 :         if (bDiscardCoordLSB)
    5779           2 :             m_sBinaryPrecision.SetFrom(oCoordPrec);
    5780             : 
    5781             :         // Save coordinate precision in gpkg_metadata/gpkg_metadata_reference
    5782        2243 :         if ((oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
    5783         741 :              oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
    5784        1502 :              oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN) &&
    5785          10 :             (m_poDS->HasMetadataTables() || m_poDS->CreateMetadataTables()))
    5786             :         {
    5787          20 :             std::string osCoordPrecision = "<CoordinatePrecision ";
    5788          10 :             if (oCoordPrec.dfXYResolution !=
    5789             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    5790             :                 osCoordPrecision += CPLSPrintf(" xy_resolution=\"%g\"",
    5791          10 :                                                oCoordPrec.dfXYResolution);
    5792          10 :             if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    5793             :                 osCoordPrecision += CPLSPrintf(" z_resolution=\"%g\"",
    5794           7 :                                                oCoordPrec.dfZResolution);
    5795          10 :             if (oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    5796             :                 osCoordPrecision += CPLSPrintf(" m_resolution=\"%g\"",
    5797           7 :                                                oCoordPrec.dfMResolution);
    5798             :             osCoordPrecision += CPLSPrintf(" discard_coord_lsb=\"%s\"",
    5799          10 :                                            bDiscardCoordLSB ? "true" : "false");
    5800             :             osCoordPrecision +=
    5801             :                 CPLSPrintf(" undo_discard_coord_lsb_on_reading=\"%s\"",
    5802          10 :                            m_bUndoDiscardCoordLSBOnReading ? "true" : "false");
    5803          10 :             osCoordPrecision += " />";
    5804             : 
    5805          10 :             char *pszSQL = sqlite3_mprintf(
    5806             :                 "INSERT INTO gpkg_metadata "
    5807             :                 "(md_scope, md_standard_uri, mime_type, metadata) VALUES "
    5808             :                 "('dataset','http://gdal.org','text/xml','%q')",
    5809             :                 osCoordPrecision.c_str());
    5810          10 :             CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
    5811          10 :             sqlite3_free(pszSQL);
    5812             : 
    5813             :             const sqlite_int64 nFID =
    5814          10 :                 sqlite3_last_insert_rowid(m_poDS->GetDB());
    5815          10 :             pszSQL = sqlite3_mprintf(
    5816             :                 "INSERT INTO gpkg_metadata_reference (reference_scope, "
    5817             :                 "table_name, column_name, timestamp, md_file_id) VALUES "
    5818             :                 "('column', '%q', '%q', %s, %d)",
    5819             :                 m_pszTableName, pszGeomColumnName,
    5820          20 :                 m_poDS->GetCurrentDateEscapedSQL().c_str(),
    5821             :                 static_cast<int>(nFID));
    5822          10 :             CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
    5823          10 :             sqlite3_free(pszSQL);
    5824             :         }
    5825             : 
    5826         751 :         m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
    5827             :     }
    5828         823 :     if (pszIdentifier)
    5829             :     {
    5830           3 :         m_osIdentifierLCO = pszIdentifier;
    5831           3 :         OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
    5832             :     }
    5833         823 :     if (pszDescription)
    5834             :     {
    5835           4 :         m_osDescriptionLCO = pszDescription;
    5836           4 :         OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
    5837             :     }
    5838             : 
    5839         823 :     m_poFeatureDefn->Seal(/* bSealFields = */ true);
    5840         823 : }
    5841             : 
    5842             : /************************************************************************/
    5843             : /*                      RegisterGeometryColumn()                        */
    5844             : /************************************************************************/
    5845             : 
    5846         754 : OGRErr OGRGeoPackageTableLayer::RegisterGeometryColumn()
    5847             : {
    5848         754 :     OGRwkbGeometryType eGType = GetGeomType();
    5849         754 :     const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
    5850             :     /* Requirement 27: The z value in a gpkg_geometry_columns table row */
    5851             :     /* SHALL be one of 0 (none), 1 (mandatory), or 2 (optional) */
    5852             : 
    5853             :     /* Update gpkg_geometry_columns with the table info */
    5854             :     char *pszSQL =
    5855         754 :         sqlite3_mprintf("INSERT INTO gpkg_geometry_columns "
    5856             :                         "(table_name,column_name,geometry_type_name,srs_id,z,m)"
    5857             :                         " VALUES "
    5858             :                         "('%q','%q','%q',%d,%d,%d)",
    5859             :                         GetName(), GetGeometryColumn(), pszGeometryType, m_iSrs,
    5860             :                         m_nZFlag, m_nMFlag);
    5861             : 
    5862         754 :     OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
    5863         754 :     sqlite3_free(pszSQL);
    5864         754 :     if (err != OGRERR_NONE)
    5865           0 :         return OGRERR_FAILURE;
    5866             : 
    5867         754 :     if (wkbFlatten(eGType) > wkbGeometryCollection)
    5868             :     {
    5869           6 :         CreateGeometryExtensionIfNecessary(eGType);
    5870             :     }
    5871             : 
    5872         754 :     return OGRERR_NONE;
    5873             : }
    5874             : 
    5875             : /************************************************************************/
    5876             : /*                        GetColumnsOfCreateTable()                     */
    5877             : /************************************************************************/
    5878             : 
    5879         848 : CPLString OGRGeoPackageTableLayer::GetColumnsOfCreateTable(
    5880             :     const std::vector<OGRFieldDefn *> &apoFields)
    5881             : {
    5882         848 :     CPLString osSQL;
    5883             : 
    5884         848 :     char *pszSQL = nullptr;
    5885         848 :     bool bNeedComma = false;
    5886         848 :     if (m_pszFidColumn != nullptr)
    5887             :     {
    5888             :         pszSQL =
    5889         848 :             sqlite3_mprintf("\"%w\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
    5890             :                             m_pszFidColumn);
    5891         848 :         osSQL += pszSQL;
    5892         848 :         sqlite3_free(pszSQL);
    5893         848 :         bNeedComma = true;
    5894             :     }
    5895             : 
    5896         848 :     const OGRwkbGeometryType eGType = GetGeomType();
    5897         848 :     if (eGType != wkbNone)
    5898             :     {
    5899         777 :         if (bNeedComma)
    5900             :         {
    5901         777 :             osSQL += ", ";
    5902             :         }
    5903         777 :         bNeedComma = true;
    5904             : 
    5905             :         /* Requirement 25: The geometry_type_name value in a
    5906             :          * gpkg_geometry_columns */
    5907             :         /* row SHALL be one of the uppercase geometry type names specified in */
    5908             :         /* Geometry Types (Normative). */
    5909         777 :         const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
    5910             : 
    5911             :         pszSQL =
    5912         777 :             sqlite3_mprintf("\"%w\" %s", GetGeometryColumn(), pszGeometryType);
    5913         777 :         osSQL += pszSQL;
    5914         777 :         sqlite3_free(pszSQL);
    5915         777 :         if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsNullable())
    5916             :         {
    5917           2 :             osSQL += " NOT NULL";
    5918             :         }
    5919             :     }
    5920             : 
    5921        5127 :     for (size_t i = 0; i < apoFields.size(); i++)
    5922             :     {
    5923        4279 :         OGRFieldDefn *poFieldDefn = apoFields[i];
    5924             :         // Eg. when a geometry type is specified + an sql statement returns no
    5925             :         // or NULL geometry values, the geom column is incorrectly treated as
    5926             :         // an attribute column as well with the same name. Not ideal, but skip
    5927             :         // this column here to avoid duplicate column name error. Issue: #6976.
    5928        8417 :         if ((eGType != wkbNone) &&
    5929        4138 :             (EQUAL(poFieldDefn->GetNameRef(), GetGeometryColumn())))
    5930             :         {
    5931           0 :             continue;
    5932             :         }
    5933        4279 :         if (bNeedComma)
    5934             :         {
    5935        4279 :             osSQL += ", ";
    5936             :         }
    5937        4279 :         bNeedComma = true;
    5938             : 
    5939        4279 :         pszSQL = sqlite3_mprintf("\"%w\" %s", poFieldDefn->GetNameRef(),
    5940             :                                  GPkgFieldFromOGR(poFieldDefn->GetType(),
    5941             :                                                   poFieldDefn->GetSubType(),
    5942             :                                                   poFieldDefn->GetWidth()));
    5943        4279 :         osSQL += pszSQL;
    5944        4279 :         sqlite3_free(pszSQL);
    5945        4279 :         if (!poFieldDefn->IsNullable())
    5946             :         {
    5947          13 :             osSQL += " NOT NULL";
    5948             :         }
    5949        4279 :         if (poFieldDefn->IsUnique())
    5950             :         {
    5951          17 :             osSQL += " UNIQUE";
    5952             :         }
    5953        4279 :         const char *pszDefault = poFieldDefn->GetDefault();
    5954        4293 :         if (pszDefault != nullptr &&
    5955          14 :             (!poFieldDefn->IsDefaultDriverSpecific() ||
    5956           1 :              (pszDefault[0] == '(' &&
    5957           1 :               pszDefault[strlen(pszDefault) - 1] == ')' &&
    5958           1 :               (STARTS_WITH_CI(pszDefault + 1, "strftime") ||
    5959           0 :                STARTS_WITH_CI(pszDefault + 1, " strftime")))))
    5960             :         {
    5961          14 :             osSQL += " DEFAULT ";
    5962             :             OGRField sField;
    5963          18 :             if (poFieldDefn->GetType() == OFTDateTime &&
    5964           4 :                 OGRParseDate(pszDefault, &sField, 0))
    5965             :             {
    5966             :                 char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    5967           0 :                 OGRGetISO8601DateTime(&sField, false, szBuffer);
    5968           0 :                 osSQL += szBuffer;
    5969             :             }
    5970             :             /* Make sure CURRENT_TIMESTAMP is translated into appropriate format
    5971             :              * for GeoPackage */
    5972          18 :             else if (poFieldDefn->GetType() == OFTDateTime &&
    5973           4 :                      EQUAL(pszDefault, "CURRENT_TIMESTAMP"))
    5974             :             {
    5975           1 :                 osSQL += "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))";
    5976             :             }
    5977             :             else
    5978             :             {
    5979          13 :                 osSQL += poFieldDefn->GetDefault();
    5980             :             }
    5981             :         }
    5982             :     }
    5983             : 
    5984         848 :     return osSQL;
    5985             : }
    5986             : 
    5987             : /************************************************************************/
    5988             : /*                      RunDeferredCreationIfNecessary()                */
    5989             : /************************************************************************/
    5990             : 
    5991        5291 : OGRErr OGRGeoPackageTableLayer::RunDeferredCreationIfNecessary()
    5992             : {
    5993        5291 :     if (!m_bDeferredCreation)
    5994        4468 :         return OGRERR_NONE;
    5995         823 :     m_bDeferredCreation = false;
    5996             : 
    5997         823 :     const char *pszLayerName = m_poFeatureDefn->GetName();
    5998             : 
    5999             :     /* Create the table! */
    6000        1646 :     CPLString osCommand;
    6001             : 
    6002         823 :     char *pszSQL = sqlite3_mprintf("CREATE TABLE \"%w\" ( ", pszLayerName);
    6003         823 :     osCommand += pszSQL;
    6004         823 :     sqlite3_free(pszSQL);
    6005             : 
    6006        1646 :     std::vector<OGRFieldDefn *> apoFields;
    6007        5044 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    6008             :     {
    6009        4221 :         if (i == m_iFIDAsRegularColumnIndex)
    6010           4 :             continue;
    6011        4217 :         apoFields.push_back(m_poFeatureDefn->GetFieldDefn(i));
    6012             :     }
    6013             : 
    6014         823 :     osCommand += GetColumnsOfCreateTable(apoFields);
    6015             : 
    6016         823 :     osCommand += ")";
    6017             : 
    6018             : #ifdef DEBUG
    6019         823 :     CPLDebug("GPKG", "exec(%s)", osCommand.c_str());
    6020             : #endif
    6021         823 :     OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
    6022         823 :     if (OGRERR_NONE != err)
    6023           0 :         return OGRERR_FAILURE;
    6024             : 
    6025        5040 :     for (auto &poField : apoFields)
    6026             :     {
    6027        4217 :         if (!DoSpecialProcessingForColumnCreation(poField))
    6028             :         {
    6029           0 :             return OGRERR_FAILURE;
    6030             :         }
    6031             :     }
    6032             : 
    6033             :     /* Update gpkg_contents with the table info */
    6034         823 :     const OGRwkbGeometryType eGType = GetGeomType();
    6035         823 :     const bool bIsSpatial = (eGType != wkbNone);
    6036             : 
    6037         823 :     if (bIsSpatial || m_eASpatialVariant == GPKG_ATTRIBUTES)
    6038             :     {
    6039         811 :         const char *pszIdentifier = GetMetadataItem("IDENTIFIER");
    6040         811 :         if (pszIdentifier == nullptr)
    6041         808 :             pszIdentifier = pszLayerName;
    6042         811 :         const char *pszDescription = GetMetadataItem("DESCRIPTION");
    6043         811 :         if (pszDescription == nullptr)
    6044         807 :             pszDescription = "";
    6045             : 
    6046         811 :         pszSQL = sqlite3_mprintf(
    6047             :             "INSERT INTO gpkg_contents "
    6048             :             "(table_name,data_type,identifier,description,last_change,srs_id) "
    6049             :             "VALUES "
    6050             :             "('%q','%q','%q','%q',%s,%d)",
    6051             :             pszLayerName, (bIsSpatial ? "features" : "attributes"),
    6052             :             pszIdentifier, pszDescription,
    6053        1622 :             GDALGeoPackageDataset::GetCurrentDateEscapedSQL().c_str(), m_iSrs);
    6054             : 
    6055         811 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    6056         811 :         sqlite3_free(pszSQL);
    6057         811 :         if (err != OGRERR_NONE)
    6058           0 :             return OGRERR_FAILURE;
    6059             :     }
    6060             : 
    6061         823 :     if (bIsSpatial)
    6062             :     {
    6063             :         // Insert into gpkg_geometry_columns after gpkg_contents because of
    6064             :         // foreign key constraints
    6065         752 :         err = RegisterGeometryColumn();
    6066         752 :         if (err != OGRERR_NONE)
    6067           0 :             return OGRERR_FAILURE;
    6068             :     }
    6069             : 
    6070             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    6071         823 :     if (m_poDS->m_bHasGPKGOGRContents)
    6072             :     {
    6073         818 :         pszSQL = sqlite3_mprintf("DELETE FROM gpkg_ogr_contents WHERE "
    6074             :                                  "lower(table_name) = lower('%q')",
    6075             :                                  pszLayerName);
    6076         818 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    6077         818 :         sqlite3_free(pszSQL);
    6078             : 
    6079         818 :         pszSQL = sqlite3_mprintf(
    6080             :             "INSERT INTO gpkg_ogr_contents (table_name, feature_count) "
    6081             :             "VALUES ('%q', 0)",
    6082             :             pszLayerName);
    6083         818 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    6084         818 :         sqlite3_free(pszSQL);
    6085         818 :         if (err == OGRERR_NONE)
    6086             :         {
    6087         818 :             m_nTotalFeatureCount = 0;
    6088         818 :             m_bAddOGRFeatureCountTriggers = true;
    6089             :         }
    6090             :     }
    6091             : #endif
    6092             : 
    6093         823 :     ResetReading();
    6094             : 
    6095         823 :     return OGRERR_NONE;
    6096             : }
    6097             : 
    6098             : /************************************************************************/
    6099             : /*                            GetMetadata()                             */
    6100             : /************************************************************************/
    6101             : 
    6102       10864 : char **OGRGeoPackageTableLayer::GetMetadata(const char *pszDomain)
    6103             : 
    6104             : {
    6105       10864 :     if (!m_bFeatureDefnCompleted)
    6106         286 :         GetLayerDefn();
    6107       10864 :     if (!m_bHasTriedDetectingFID64 && m_pszFidColumn != nullptr)
    6108             :     {
    6109         361 :         m_bHasTriedDetectingFID64 = true;
    6110             : 
    6111             :         /* --------------------------------------------------------------------
    6112             :          */
    6113             :         /*      Find if the FID holds 64bit values */
    6114             :         /* --------------------------------------------------------------------
    6115             :          */
    6116             : 
    6117             :         // Normally the fid should be AUTOINCREMENT, so check sqlite_sequence
    6118         361 :         OGRErr err = OGRERR_NONE;
    6119             :         char *pszSQL =
    6120         361 :             sqlite3_mprintf("SELECT seq FROM sqlite_sequence WHERE name = '%q'",
    6121             :                             m_pszTableName);
    6122         361 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6123         361 :         GIntBig nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
    6124         361 :         CPLPopErrorHandler();
    6125         361 :         sqlite3_free(pszSQL);
    6126         361 :         if (err != OGRERR_NONE)
    6127             :         {
    6128          42 :             CPLErrorReset();
    6129             : 
    6130             :             // In case of error, fallback to taking the MAX of the FID
    6131          42 :             pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
    6132             :                                      m_pszFidColumn, m_pszTableName);
    6133             : 
    6134          42 :             nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, nullptr);
    6135          42 :             sqlite3_free(pszSQL);
    6136             :         }
    6137         361 :         if (nMaxId > INT_MAX)
    6138           1 :             OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
    6139             :     }
    6140             : 
    6141       10864 :     if (m_bHasReadMetadataFromStorage)
    6142        9689 :         return OGRLayer::GetMetadata(pszDomain);
    6143             : 
    6144        1175 :     m_bHasReadMetadataFromStorage = true;
    6145             : 
    6146        1175 :     if (!m_poDS->HasMetadataTables())
    6147         912 :         return OGRLayer::GetMetadata(pszDomain);
    6148             : 
    6149         263 :     char *pszSQL = sqlite3_mprintf(
    6150             :         "SELECT md.metadata, md.md_standard_uri, md.mime_type, "
    6151             :         "mdr.reference_scope "
    6152             :         "FROM gpkg_metadata md "
    6153             :         "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
    6154             :         "WHERE lower(mdr.table_name) = lower('%q') ORDER BY md.id "
    6155             :         "LIMIT 1000",  // to avoid denial of service
    6156             :         m_pszTableName);
    6157             : 
    6158         526 :     auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    6159         263 :     sqlite3_free(pszSQL);
    6160         263 :     if (!oResult)
    6161             :     {
    6162           0 :         return OGRLayer::GetMetadata(pszDomain);
    6163             :     }
    6164             : 
    6165         263 :     char **papszMetadata = CSLDuplicate(OGRLayer::GetMetadata());
    6166             : 
    6167             :     /* GDAL metadata */
    6168         500 :     for (int i = 0; i < oResult->RowCount(); i++)
    6169             :     {
    6170         237 :         const char *pszMetadata = oResult->GetValue(0, i);
    6171         237 :         const char *pszMDStandardURI = oResult->GetValue(1, i);
    6172         237 :         const char *pszMimeType = oResult->GetValue(2, i);
    6173         237 :         const char *pszReferenceScope = oResult->GetValue(3, i);
    6174         237 :         if (pszMetadata && pszMDStandardURI && pszMimeType &&
    6175         237 :             pszReferenceScope && EQUAL(pszMDStandardURI, "http://gdal.org") &&
    6176         237 :             EQUAL(pszMimeType, "text/xml") && EQUAL(pszReferenceScope, "table"))
    6177             :         {
    6178         218 :             CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
    6179         218 :             if (psXMLNode)
    6180             :             {
    6181         436 :                 GDALMultiDomainMetadata oLocalMDMD;
    6182         218 :                 oLocalMDMD.XMLInit(psXMLNode, FALSE);
    6183             : 
    6184             :                 papszMetadata =
    6185         218 :                     CSLMerge(papszMetadata, oLocalMDMD.GetMetadata());
    6186         218 :                 CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
    6187         218 :                 CSLConstList papszIter = papszDomainList;
    6188         462 :                 while (papszIter && *papszIter)
    6189             :                 {
    6190         244 :                     if (!EQUAL(*papszIter, ""))
    6191          27 :                         oMDMD.SetMetadata(oLocalMDMD.GetMetadata(*papszIter),
    6192             :                                           *papszIter);
    6193         244 :                     papszIter++;
    6194             :                 }
    6195             : 
    6196         218 :                 CPLDestroyXMLNode(psXMLNode);
    6197             :             }
    6198             :         }
    6199             :     }
    6200             : 
    6201         263 :     OGRLayer::SetMetadata(papszMetadata);
    6202         263 :     CSLDestroy(papszMetadata);
    6203         263 :     papszMetadata = nullptr;
    6204             : 
    6205             :     /* Add non-GDAL metadata now */
    6206         263 :     int nNonGDALMDILocal = 1;
    6207         500 :     for (int i = 0; i < oResult->RowCount(); i++)
    6208             :     {
    6209         237 :         const char *pszMetadata = oResult->GetValue(0, i);
    6210         237 :         const char *pszMDStandardURI = oResult->GetValue(1, i);
    6211         237 :         const char *pszMimeType = oResult->GetValue(2, i);
    6212             :         // const char* pszReferenceScope = oResult->GetValue(3, i);
    6213         237 :         if (pszMetadata == nullptr || pszMDStandardURI == nullptr ||
    6214             :             pszMimeType == nullptr
    6215             :             /* || pszReferenceScope == nullptr */)
    6216             :         {
    6217             :             // should not happen as there are NOT NULL constraints
    6218             :             // But a database could lack such NOT NULL constraints or have
    6219             :             // large values that would cause a memory allocation failure.
    6220           0 :             continue;
    6221             :         }
    6222             :         // int bIsGPKGScope = EQUAL(pszReferenceScope, "geopackage");
    6223         237 :         if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
    6224         237 :             EQUAL(pszMimeType, "text/xml"))
    6225         237 :             continue;
    6226             : 
    6227           0 :         if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
    6228           0 :             EQUAL(pszMimeType, "text/plain"))
    6229             :         {
    6230           0 :             if (STARTS_WITH_CI(pszMetadata, "coordinate_epoch="))
    6231             :             {
    6232           0 :                 continue;
    6233             :             }
    6234             :         }
    6235             : 
    6236             :         /*if( strcmp( pszMDStandardURI, "http://www.isotc211.org/2005/gmd" ) ==
    6237             :         0 && strcmp( pszMimeType, "text/xml" ) == 0 )
    6238             :         {
    6239             :             char* apszMD[2];
    6240             :             apszMD[0] = (char*)pszMetadata;
    6241             :             apszMD[1] = NULL;
    6242             :             oMDMD.SetMetadata(apszMD, "xml:MD_Metadata");
    6243             :         }
    6244             :         else*/
    6245             :         {
    6246           0 :             oMDMD.SetMetadataItem(
    6247             :                 CPLSPrintf("GPKG_METADATA_ITEM_%d", nNonGDALMDILocal),
    6248             :                 pszMetadata);
    6249           0 :             nNonGDALMDILocal++;
    6250             :         }
    6251             :     }
    6252             : 
    6253         263 :     return OGRLayer::GetMetadata(pszDomain);
    6254             : }
    6255             : 
    6256             : /************************************************************************/
    6257             : /*                          GetMetadataItem()                           */
    6258             : /************************************************************************/
    6259             : 
    6260        9409 : const char *OGRGeoPackageTableLayer::GetMetadataItem(const char *pszName,
    6261             :                                                      const char *pszDomain)
    6262             : {
    6263        9409 :     return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
    6264             : }
    6265             : 
    6266             : /************************************************************************/
    6267             : /*                      GetMetadataDomainList()                         */
    6268             : /************************************************************************/
    6269             : 
    6270         274 : char **OGRGeoPackageTableLayer::GetMetadataDomainList()
    6271             : {
    6272         274 :     GetMetadata();
    6273         274 :     return OGRLayer::GetMetadataDomainList();
    6274             : }
    6275             : 
    6276             : /************************************************************************/
    6277             : /*                            SetMetadata()                             */
    6278             : /************************************************************************/
    6279             : 
    6280         126 : CPLErr OGRGeoPackageTableLayer::SetMetadata(char **papszMetadata,
    6281             :                                             const char *pszDomain)
    6282             : {
    6283         126 :     GetMetadata(); /* force loading from storage if needed */
    6284         126 :     CPLErr eErr = OGRLayer::SetMetadata(papszMetadata, pszDomain);
    6285         126 :     m_poDS->SetMetadataDirty();
    6286         126 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    6287             :     {
    6288          73 :         if (!m_osIdentifierLCO.empty())
    6289           1 :             OGRLayer::SetMetadataItem("IDENTIFIER", m_osIdentifierLCO);
    6290          73 :         if (!m_osDescriptionLCO.empty())
    6291           1 :             OGRLayer::SetMetadataItem("DESCRIPTION", m_osDescriptionLCO);
    6292             :     }
    6293         126 :     return eErr;
    6294             : }
    6295             : 
    6296             : /************************************************************************/
    6297             : /*                          SetMetadataItem()                           */
    6298             : /************************************************************************/
    6299             : 
    6300         297 : CPLErr OGRGeoPackageTableLayer::SetMetadataItem(const char *pszName,
    6301             :                                                 const char *pszValue,
    6302             :                                                 const char *pszDomain)
    6303             : {
    6304         297 :     GetMetadata(); /* force loading from storage if needed */
    6305         298 :     if (!m_osIdentifierLCO.empty() && EQUAL(pszName, "IDENTIFIER") &&
    6306           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    6307           1 :         return CE_None;
    6308         297 :     if (!m_osDescriptionLCO.empty() && EQUAL(pszName, "DESCRIPTION") &&
    6309           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    6310           1 :         return CE_None;
    6311         295 :     m_poDS->SetMetadataDirty();
    6312         295 :     return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
    6313             : }
    6314             : 
    6315             : /************************************************************************/
    6316             : /*                          RecreateTable()                             */
    6317             : /************************************************************************/
    6318             : 
    6319             : OGRErr
    6320          10 : OGRGeoPackageTableLayer::RecreateTable(const CPLString &osColumnsForCreate,
    6321             :                                        const CPLString &osFieldListForSelect)
    6322             : {
    6323             :     /* -------------------------------------------------------------------- */
    6324             :     /*      Save existing related triggers and index                        */
    6325             :     /* -------------------------------------------------------------------- */
    6326          10 :     sqlite3 *hDB = m_poDS->GetDB();
    6327             : 
    6328          10 :     char *pszSQL = sqlite3_mprintf(
    6329             :         "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') "
    6330             :         "AND lower(tbl_name)=lower('%q') LIMIT 10000",
    6331             :         m_pszTableName);
    6332          10 :     OGRErr eErr = OGRERR_NONE;
    6333          10 :     auto oTriggers = SQLQuery(hDB, pszSQL);
    6334          10 :     sqlite3_free(pszSQL);
    6335             : 
    6336             :     /* -------------------------------------------------------------------- */
    6337             :     /*      Make a temporary table with new content.                        */
    6338             :     /* -------------------------------------------------------------------- */
    6339          10 :     if (oTriggers)
    6340             :     {
    6341          10 :         pszSQL = sqlite3_mprintf("CREATE TABLE \"%w_ogr_tmp\" (%s)",
    6342             :                                  m_pszTableName, osColumnsForCreate.c_str());
    6343          10 :         eErr = SQLCommand(hDB, pszSQL);
    6344          10 :         sqlite3_free(pszSQL);
    6345             :     }
    6346             :     else
    6347             :     {
    6348           0 :         eErr = OGRERR_FAILURE;
    6349             :     }
    6350             : 
    6351          10 :     if (eErr == OGRERR_NONE)
    6352             :     {
    6353          10 :         pszSQL = sqlite3_mprintf(
    6354             :             "INSERT INTO \"%w_ogr_tmp\" SELECT %s FROM \"%w\"", m_pszTableName,
    6355             :             osFieldListForSelect.c_str(), m_pszTableName);
    6356          10 :         eErr = SQLCommand(hDB, pszSQL);
    6357          10 :         sqlite3_free(pszSQL);
    6358             :     }
    6359             : 
    6360             :     /* -------------------------------------------------------------------- */
    6361             :     /*      Drop the original table                                         */
    6362             :     /* -------------------------------------------------------------------- */
    6363          10 :     if (eErr == OGRERR_NONE)
    6364             :     {
    6365           8 :         pszSQL = sqlite3_mprintf("DROP TABLE \"%w\"", m_pszTableName);
    6366           8 :         eErr = SQLCommand(hDB, pszSQL);
    6367           8 :         sqlite3_free(pszSQL);
    6368             :     }
    6369             : 
    6370             :     /* -------------------------------------------------------------------- */
    6371             :     /*      Rename temporary table as new table                             */
    6372             :     /* -------------------------------------------------------------------- */
    6373          10 :     if (eErr == OGRERR_NONE)
    6374             :     {
    6375           8 :         pszSQL = sqlite3_mprintf("ALTER TABLE \"%w_ogr_tmp\" RENAME TO \"%w\"",
    6376             :                                  m_pszTableName, m_pszTableName);
    6377           8 :         eErr = SQLCommand(hDB, pszSQL);
    6378           8 :         sqlite3_free(pszSQL);
    6379             :     }
    6380             : 
    6381             :     /* -------------------------------------------------------------------- */
    6382             :     /*      Recreate existing related tables, triggers and index            */
    6383             :     /* -------------------------------------------------------------------- */
    6384          69 :     for (int i = 0;
    6385          69 :          oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
    6386             :     {
    6387          59 :         const char *pszSQLTriggerIdx = oTriggers->GetValue(0, i);
    6388          59 :         if (pszSQLTriggerIdx != nullptr && *pszSQLTriggerIdx != '\0')
    6389             :         {
    6390          58 :             eErr = SQLCommand(hDB, pszSQLTriggerIdx);
    6391             :         }
    6392             :     }
    6393             : 
    6394          20 :     return eErr;
    6395             : }
    6396             : 
    6397             : /************************************************************************/
    6398             : /*                          BuildSelectFieldList()                      */
    6399             : /************************************************************************/
    6400             : 
    6401          10 : CPLString OGRGeoPackageTableLayer::BuildSelectFieldList(
    6402             :     const std::vector<OGRFieldDefn *> &apoFields)
    6403             : {
    6404          10 :     CPLString osFieldListForSelect;
    6405             : 
    6406          10 :     char *pszSQL = nullptr;
    6407          10 :     bool bNeedComma = false;
    6408             : 
    6409          10 :     if (m_pszFidColumn != nullptr)
    6410             :     {
    6411          10 :         pszSQL = sqlite3_mprintf("\"%w\"", m_pszFidColumn);
    6412          10 :         osFieldListForSelect += pszSQL;
    6413          10 :         sqlite3_free(pszSQL);
    6414          10 :         bNeedComma = true;
    6415             :     }
    6416             : 
    6417          10 :     if (GetGeomType() != wkbNone)
    6418             :     {
    6419          10 :         if (bNeedComma)
    6420             :         {
    6421          10 :             osFieldListForSelect += ", ";
    6422             :         }
    6423          10 :         bNeedComma = true;
    6424             : 
    6425          10 :         pszSQL = sqlite3_mprintf("\"%w\"", GetGeometryColumn());
    6426          10 :         osFieldListForSelect += pszSQL;
    6427          10 :         sqlite3_free(pszSQL);
    6428             :     }
    6429             : 
    6430          29 :     for (size_t iField = 0; iField < apoFields.size(); iField++)
    6431             :     {
    6432          19 :         if (bNeedComma)
    6433             :         {
    6434          19 :             osFieldListForSelect += ", ";
    6435             :         }
    6436          19 :         bNeedComma = true;
    6437             : 
    6438          19 :         OGRFieldDefn *poFieldDefn = apoFields[iField];
    6439          19 :         pszSQL = sqlite3_mprintf("\"%w\"", poFieldDefn->GetNameRef());
    6440          19 :         osFieldListForSelect += pszSQL;
    6441          19 :         sqlite3_free(pszSQL);
    6442             :     }
    6443             : 
    6444          10 :     return osFieldListForSelect;
    6445             : }
    6446             : 
    6447             : /************************************************************************/
    6448             : /*                             DeleteField()                            */
    6449             : /************************************************************************/
    6450             : 
    6451         163 : OGRErr OGRGeoPackageTableLayer::DeleteField(int iFieldToDelete)
    6452             : {
    6453         163 :     if (!m_bFeatureDefnCompleted)
    6454           3 :         GetLayerDefn();
    6455         163 :     if (!CheckUpdatableTable("DeleteField"))
    6456           2 :         return OGRERR_FAILURE;
    6457             : 
    6458         321 :     if (iFieldToDelete < 0 ||
    6459         160 :         iFieldToDelete >= m_poFeatureDefn->GetFieldCount())
    6460             :     {
    6461           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    6462           2 :         return OGRERR_FAILURE;
    6463             :     }
    6464             : 
    6465         159 :     ResetReading();
    6466         159 :     RunDeferredCreationIfNecessary();
    6467         159 :     if (!RunDeferredSpatialIndexUpdate())
    6468           0 :         return OGRERR_FAILURE;
    6469             : 
    6470             :     const char *pszFieldName =
    6471         159 :         m_poFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
    6472             : 
    6473             :     /* -------------------------------------------------------------------- */
    6474             :     /*      Drop any iterator since we change the DB structure              */
    6475             :     /* -------------------------------------------------------------------- */
    6476         159 :     m_poDS->ResetReadingAllLayers();
    6477             : 
    6478             :     // Temporary remove foreign key checks
    6479             :     const GPKGTemporaryForeignKeyCheckDisabler
    6480         318 :         oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
    6481             : 
    6482         174 :     if (m_poDS->GetCurrentSavepoint().empty() &&
    6483          15 :         m_poDS->SoftStartTransaction() != OGRERR_NONE)
    6484             :     {
    6485           0 :         return OGRERR_FAILURE;
    6486             :     }
    6487             : 
    6488             :     // ALTER TABLE ... DROP COLUMN ... was first implemented in 3.35.0 but
    6489             :     // there was bug fixes related to it until 3.35.5
    6490             : #if SQLITE_VERSION_NUMBER >= 3035005L
    6491         159 :     OGRErr eErr = SQLCommand(
    6492         318 :         m_poDS->GetDB(), CPLString()
    6493             :                              .Printf("ALTER TABLE \"%s\" DROP COLUMN \"%s\"",
    6494         318 :                                      SQLEscapeName(m_pszTableName).c_str(),
    6495         477 :                                      SQLEscapeName(pszFieldName).c_str())
    6496             :                              .c_str());
    6497             : #else
    6498             :     /* -------------------------------------------------------------------- */
    6499             :     /*      Recreate table in a transaction                                 */
    6500             :     /*      Build list of old fields, and the list of new fields.           */
    6501             :     /* -------------------------------------------------------------------- */
    6502             :     std::vector<OGRFieldDefn *> apoFields;
    6503             :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    6504             :     {
    6505             :         if (iField == iFieldToDelete)
    6506             :             continue;
    6507             : 
    6508             :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
    6509             :         apoFields.push_back(poFieldDefn);
    6510             :     }
    6511             : 
    6512             :     CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
    6513             :     CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
    6514             : 
    6515             :     OGRErr eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
    6516             : #endif
    6517             : 
    6518             :     /* -------------------------------------------------------------------- */
    6519             :     /*      Update gpkg_extensions if needed.                               */
    6520             :     /* -------------------------------------------------------------------- */
    6521         159 :     if (eErr == OGRERR_NONE && m_poDS->HasExtensionsTable())
    6522             :     {
    6523         154 :         char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_extensions WHERE "
    6524             :                                        "lower(table_name) = lower('%q') AND "
    6525             :                                        "lower(column_name) = lower('%q')",
    6526             :                                        m_pszTableName, pszFieldName);
    6527         154 :         eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6528         154 :         sqlite3_free(pszSQL);
    6529             :     }
    6530             : 
    6531             :     /* -------------------------------------------------------------------- */
    6532             :     /*      Update gpkg_data_columns if needed.                             */
    6533             :     /* -------------------------------------------------------------------- */
    6534         159 :     if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
    6535             :     {
    6536           4 :         char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_data_columns WHERE "
    6537             :                                        "lower(table_name) = lower('%q') AND "
    6538             :                                        "lower(column_name) = lower('%q')",
    6539             :                                        m_pszTableName, pszFieldName);
    6540           4 :         eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6541           4 :         sqlite3_free(pszSQL);
    6542             :     }
    6543             : 
    6544             :     /* -------------------------------------------------------------------- */
    6545             :     /*      Update gpkg_metadata_reference if needed.                       */
    6546             :     /* -------------------------------------------------------------------- */
    6547         159 :     if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
    6548             :     {
    6549             :         {
    6550             :             // Delete from gpkg_metadata metadata records that are only
    6551             :             // referenced by the column we are about to drop
    6552           3 :             char *pszSQL = sqlite3_mprintf(
    6553             :                 "DELETE FROM gpkg_metadata WHERE id IN ("
    6554             :                 "SELECT DISTINCT md_file_id FROM "
    6555             :                 "gpkg_metadata_reference WHERE "
    6556             :                 "lower(table_name) = lower('%q') "
    6557             :                 "AND lower(column_name) = lower('%q') "
    6558             :                 "AND md_parent_id is NULL) "
    6559             :                 "AND id NOT IN ("
    6560             :                 "SELECT DISTINCT md_file_id FROM gpkg_metadata_reference WHERE "
    6561             :                 "md_file_id IN ("
    6562             :                 "SELECT DISTINCT md_file_id FROM "
    6563             :                 "gpkg_metadata_reference WHERE "
    6564             :                 "lower(table_name) = lower('%q') "
    6565             :                 "AND lower(column_name) = lower('%q') "
    6566             :                 "AND md_parent_id is NULL) "
    6567             :                 "AND ("
    6568             :                 "lower(table_name) <> lower('%q') "
    6569             :                 "OR column_name IS NULL "
    6570             :                 "OR lower(column_name) <> lower('%q')))",
    6571             :                 m_pszTableName, pszFieldName, m_pszTableName, pszFieldName,
    6572             :                 m_pszTableName, pszFieldName);
    6573           3 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6574           3 :             sqlite3_free(pszSQL);
    6575             :         }
    6576             : 
    6577           3 :         if (eErr == OGRERR_NONE)
    6578             :         {
    6579             :             char *pszSQL =
    6580           3 :                 sqlite3_mprintf("DELETE FROM gpkg_metadata_reference WHERE "
    6581             :                                 "lower(table_name) = lower('%q') AND "
    6582             :                                 "lower(column_name) = lower('%q')",
    6583             :                                 m_pszTableName, pszFieldName);
    6584           3 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6585           3 :             sqlite3_free(pszSQL);
    6586             :         }
    6587             :     }
    6588             : 
    6589             :     /* -------------------------------------------------------------------- */
    6590             :     /*      Check foreign key integrity if enforcement of foreign keys      */
    6591             :     /*      constraint is enabled.                                          */
    6592             :     /* -------------------------------------------------------------------- */
    6593         318 :     if (eErr == OGRERR_NONE &&
    6594         159 :         SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
    6595             :     {
    6596           0 :         CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
    6597           0 :         eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
    6598             :     }
    6599             : 
    6600             :     /* -------------------------------------------------------------------- */
    6601             :     /*      Finish                                                          */
    6602             :     /* -------------------------------------------------------------------- */
    6603         159 :     if (eErr == OGRERR_NONE)
    6604             :     {
    6605             : 
    6606         159 :         if (m_poDS->GetCurrentSavepoint().empty())
    6607          15 :             eErr = m_poDS->SoftCommitTransaction();
    6608             : 
    6609         159 :         if (eErr == OGRERR_NONE)
    6610             :         {
    6611             : 
    6612         159 :             if (m_poDS->IsInTransaction())
    6613             :             {
    6614         153 :                 auto poFieldDefn{whileUnsealing(m_poFeatureDefn)
    6615         306 :                                      ->StealFieldDefn(iFieldToDelete)};
    6616         153 :                 if (poFieldDefn)
    6617             :                 {
    6618             :                     m_apoFieldDefnChanges.emplace_back(
    6619         153 :                         std::move(poFieldDefn), iFieldToDelete,
    6620         153 :                         FieldChangeType::DELETE_FIELD,
    6621         306 :                         m_poDS->GetCurrentSavepoint());
    6622             :                 }
    6623             :                 else
    6624             :                 {
    6625           0 :                     eErr = OGRERR_FAILURE;
    6626             :                 }
    6627             :             }
    6628             :             else
    6629             :             {
    6630          12 :                 eErr = whileUnsealing(m_poFeatureDefn)
    6631           6 :                            ->DeleteFieldDefn(iFieldToDelete);
    6632             :             }
    6633         159 :             ResetReading();
    6634             :         }
    6635             :     }
    6636           0 :     else if (m_poDS->GetCurrentSavepoint().empty())
    6637             :     {
    6638           0 :         m_poDS->SoftRollbackTransaction();
    6639             :     }
    6640             : 
    6641         159 :     return eErr;
    6642             : }
    6643             : 
    6644             : /************************************************************************/
    6645             : /*                    RenameFieldInAuxiliaryTables()                    */
    6646             : /************************************************************************/
    6647             : 
    6648             : OGRErr
    6649          16 : OGRGeoPackageTableLayer::RenameFieldInAuxiliaryTables(const char *pszOldName,
    6650             :                                                       const char *pszNewName)
    6651             : {
    6652          16 :     OGRErr eErr = OGRERR_NONE;
    6653          16 :     sqlite3 *hDB = m_poDS->GetDB();
    6654             : 
    6655             :     /* -------------------------------------------------------------------- */
    6656             :     /*      Update gpkg_extensions if needed.                               */
    6657             :     /* -------------------------------------------------------------------- */
    6658          16 :     if (/* eErr == OGRERR_NONE && */ m_poDS->HasExtensionsTable())
    6659             :     {
    6660          14 :         char *pszSQL = sqlite3_mprintf(
    6661             :             "UPDATE gpkg_extensions SET column_name = '%q' WHERE "
    6662             :             "lower(table_name) = lower('%q') AND lower(column_name) = "
    6663             :             "lower('%q')",
    6664             :             pszNewName, m_pszTableName, pszOldName);
    6665          14 :         eErr = SQLCommand(hDB, pszSQL);
    6666          14 :         sqlite3_free(pszSQL);
    6667             :     }
    6668             : 
    6669             :     /* -------------------------------------------------------------------- */
    6670             :     /*      Update gpkg_data_columns if needed.                             */
    6671             :     /* -------------------------------------------------------------------- */
    6672          16 :     if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
    6673             :     {
    6674           6 :         char *pszSQL = sqlite3_mprintf(
    6675             :             "UPDATE gpkg_data_columns SET column_name = '%q' WHERE "
    6676             :             "lower(table_name) = lower('%q') AND lower(column_name) = "
    6677             :             "lower('%q')",
    6678             :             pszNewName, m_pszTableName, pszOldName);
    6679           6 :         eErr = SQLCommand(hDB, pszSQL);
    6680           6 :         sqlite3_free(pszSQL);
    6681             :     }
    6682             : 
    6683             :     /* -------------------------------------------------------------------- */
    6684             :     /*      Update gpkg_metadata_reference if needed.                       */
    6685             :     /* -------------------------------------------------------------------- */
    6686          16 :     if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
    6687             :     {
    6688           7 :         char *pszSQL = sqlite3_mprintf(
    6689             :             "UPDATE gpkg_metadata_reference SET column_name = '%q' WHERE "
    6690             :             "lower(table_name) = lower('%q') AND lower(column_name) = "
    6691             :             "lower('%q')",
    6692             :             pszNewName, m_pszTableName, pszOldName);
    6693           7 :         eErr = SQLCommand(hDB, pszSQL);
    6694           7 :         sqlite3_free(pszSQL);
    6695             :     }
    6696             : 
    6697          16 :     return eErr;
    6698             : }
    6699             : 
    6700             : /************************************************************************/
    6701             : /*                           AlterFieldDefn()                           */
    6702             : /************************************************************************/
    6703             : 
    6704          31 : OGRErr OGRGeoPackageTableLayer::AlterFieldDefn(int iFieldToAlter,
    6705             :                                                OGRFieldDefn *poNewFieldDefn,
    6706             :                                                int nFlagsIn)
    6707             : {
    6708          31 :     if (!m_bFeatureDefnCompleted)
    6709           1 :         GetLayerDefn();
    6710          31 :     if (!CheckUpdatableTable("AlterFieldDefn"))
    6711           2 :         return OGRERR_FAILURE;
    6712             : 
    6713          29 :     if (iFieldToAlter < 0 || iFieldToAlter >= m_poFeatureDefn->GetFieldCount())
    6714             :     {
    6715           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    6716           1 :         return OGRERR_FAILURE;
    6717             :     }
    6718             : 
    6719             :     /* -------------------------------------------------------------------- */
    6720             :     /*      Deferred actions, reset state.                                   */
    6721             :     /* -------------------------------------------------------------------- */
    6722          28 :     ResetReading();
    6723          28 :     RunDeferredCreationIfNecessary();
    6724          28 :     if (m_bThreadRTreeStarted)
    6725           0 :         CancelAsyncRTree();
    6726          28 :     if (!RunDeferredSpatialIndexUpdate())
    6727           0 :         return OGRERR_FAILURE;
    6728             : 
    6729             :     /* -------------------------------------------------------------------- */
    6730             :     /*      Check that the new column name is not a duplicate.              */
    6731             :     /* -------------------------------------------------------------------- */
    6732             : 
    6733             :     OGRFieldDefn *poFieldDefnToAlter =
    6734          28 :         m_poFeatureDefn->GetFieldDefn(iFieldToAlter);
    6735          56 :     const CPLString osOldColName(poFieldDefnToAlter->GetNameRef());
    6736          28 :     const CPLString osNewColName((nFlagsIn & ALTER_NAME_FLAG)
    6737             :                                      ? CPLString(poNewFieldDefn->GetNameRef())
    6738          56 :                                      : osOldColName);
    6739             : 
    6740             :     const bool bRenameCol =
    6741          54 :         (nFlagsIn & ALTER_NAME_FLAG) &&
    6742          26 :         strcmp(poNewFieldDefn->GetNameRef(), osOldColName) != 0;
    6743          28 :     if (bRenameCol)
    6744             :     {
    6745          51 :         if ((m_pszFidColumn &&
    6746          17 :              strcmp(poNewFieldDefn->GetNameRef(), m_pszFidColumn) == 0) ||
    6747          16 :             (GetGeomType() != wkbNone &&
    6748          16 :              strcmp(poNewFieldDefn->GetNameRef(),
    6749          50 :                     m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()) == 0) ||
    6750          15 :             m_poFeatureDefn->GetFieldIndex(poNewFieldDefn->GetNameRef()) >= 0)
    6751             :         {
    6752           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    6753             :                      "Field name %s is already used for another field",
    6754             :                      poNewFieldDefn->GetNameRef());
    6755           4 :             return OGRERR_FAILURE;
    6756             :         }
    6757             :     }
    6758             : 
    6759             :     /* -------------------------------------------------------------------- */
    6760             :     /*      Build the modified field definition from the flags.             */
    6761             :     /* -------------------------------------------------------------------- */
    6762          48 :     OGRFieldDefn oTmpFieldDefn(poFieldDefnToAlter);
    6763          24 :     bool bUseRewriteSchemaMethod = (m_poDS->m_nSoftTransactionLevel == 0);
    6764          24 :     int nActualFlags = 0;
    6765          24 :     if (bRenameCol)
    6766             :     {
    6767          13 :         nActualFlags |= ALTER_NAME_FLAG;
    6768          13 :         oTmpFieldDefn.SetName(poNewFieldDefn->GetNameRef());
    6769             :     }
    6770          44 :     if ((nFlagsIn & ALTER_TYPE_FLAG) != 0 &&
    6771          20 :         (poFieldDefnToAlter->GetType() != poNewFieldDefn->GetType() ||
    6772          14 :          poFieldDefnToAlter->GetSubType() != poNewFieldDefn->GetSubType()))
    6773             :     {
    6774           8 :         nActualFlags |= ALTER_TYPE_FLAG;
    6775           8 :         oTmpFieldDefn.SetSubType(OFSTNone);
    6776           8 :         oTmpFieldDefn.SetType(poNewFieldDefn->GetType());
    6777           8 :         oTmpFieldDefn.SetSubType(poNewFieldDefn->GetSubType());
    6778             :     }
    6779          62 :     if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) != 0 &&
    6780          38 :         (poFieldDefnToAlter->GetWidth() != poNewFieldDefn->GetWidth() ||
    6781          18 :          poFieldDefnToAlter->GetPrecision() != poNewFieldDefn->GetPrecision()))
    6782             :     {
    6783           2 :         nActualFlags |= ALTER_WIDTH_PRECISION_FLAG;
    6784           2 :         oTmpFieldDefn.SetWidth(poNewFieldDefn->GetWidth());
    6785           2 :         oTmpFieldDefn.SetPrecision(poNewFieldDefn->GetPrecision());
    6786             :     }
    6787          44 :     if ((nFlagsIn & ALTER_NULLABLE_FLAG) != 0 &&
    6788          20 :         poFieldDefnToAlter->IsNullable() != poNewFieldDefn->IsNullable())
    6789             :     {
    6790           4 :         nActualFlags |= ALTER_NULLABLE_FLAG;
    6791           4 :         bUseRewriteSchemaMethod = false;
    6792           4 :         oTmpFieldDefn.SetNullable(poNewFieldDefn->IsNullable());
    6793             :     }
    6794          44 :     if ((nFlagsIn & ALTER_DEFAULT_FLAG) != 0 &&
    6795          20 :         !((poFieldDefnToAlter->GetDefault() == nullptr &&
    6796          17 :            poNewFieldDefn->GetDefault() == nullptr) ||
    6797           4 :           (poFieldDefnToAlter->GetDefault() != nullptr &&
    6798           3 :            poNewFieldDefn->GetDefault() != nullptr &&
    6799           2 :            strcmp(poFieldDefnToAlter->GetDefault(),
    6800             :                   poNewFieldDefn->GetDefault()) == 0)))
    6801             :     {
    6802           2 :         nActualFlags |= ALTER_DEFAULT_FLAG;
    6803           2 :         oTmpFieldDefn.SetDefault(poNewFieldDefn->GetDefault());
    6804             :     }
    6805          44 :     if ((nFlagsIn & ALTER_UNIQUE_FLAG) != 0 &&
    6806          20 :         poFieldDefnToAlter->IsUnique() != poNewFieldDefn->IsUnique())
    6807             :     {
    6808           2 :         nActualFlags |= ALTER_UNIQUE_FLAG;
    6809           2 :         bUseRewriteSchemaMethod = false;
    6810           2 :         oTmpFieldDefn.SetUnique(poNewFieldDefn->IsUnique());
    6811             :     }
    6812          44 :     if ((nFlagsIn & ALTER_DOMAIN_FLAG) != 0 &&
    6813          20 :         poFieldDefnToAlter->GetDomainName() != poNewFieldDefn->GetDomainName())
    6814             :     {
    6815           3 :         nActualFlags |= ALTER_DOMAIN_FLAG;
    6816           3 :         oTmpFieldDefn.SetDomainName(poNewFieldDefn->GetDomainName());
    6817             :     }
    6818          45 :     if ((nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG) != 0 &&
    6819          21 :         strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
    6820             :                poNewFieldDefn->GetAlternativeNameRef()) != 0)
    6821             :     {
    6822           5 :         nActualFlags |= ALTER_ALTERNATIVE_NAME_FLAG;
    6823           5 :         oTmpFieldDefn.SetAlternativeName(
    6824             :             poNewFieldDefn->GetAlternativeNameRef());
    6825             :     }
    6826          45 :     if ((nFlagsIn & ALTER_COMMENT_FLAG) != 0 &&
    6827          21 :         poFieldDefnToAlter->GetComment() != poNewFieldDefn->GetComment())
    6828             :     {
    6829           3 :         nActualFlags |= ALTER_COMMENT_FLAG;
    6830           3 :         oTmpFieldDefn.SetComment(poNewFieldDefn->GetComment());
    6831             :     }
    6832             : 
    6833             :     /* -------------------------------------------------------------------- */
    6834             :     /*      Build list of old fields, and the list of new fields.           */
    6835             :     /* -------------------------------------------------------------------- */
    6836          48 :     std::vector<OGRFieldDefn *> apoFields;
    6837          48 :     std::vector<OGRFieldDefn *> apoFieldsOld;
    6838          84 :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    6839             :     {
    6840             :         OGRFieldDefn *poFieldDefn;
    6841          60 :         if (iField == iFieldToAlter)
    6842             :         {
    6843          24 :             poFieldDefn = &oTmpFieldDefn;
    6844             :         }
    6845             :         else
    6846             :         {
    6847          36 :             poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
    6848             :         }
    6849          60 :         apoFields.push_back(poFieldDefn);
    6850          60 :         apoFieldsOld.push_back(m_poFeatureDefn->GetFieldDefn(iField));
    6851             :     }
    6852             : 
    6853          48 :     const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
    6854             : 
    6855             :     /* -------------------------------------------------------------------- */
    6856             :     /*      Drop any iterator since we change the DB structure              */
    6857             :     /* -------------------------------------------------------------------- */
    6858          24 :     m_poDS->ResetReadingAllLayers();
    6859             : 
    6860          24 :     const bool bUseRenameColumn = (nActualFlags == ALTER_NAME_FLAG);
    6861          24 :     if (bUseRenameColumn)
    6862           4 :         bUseRewriteSchemaMethod = false;
    6863             : 
    6864          24 :     if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    6865           0 :         return OGRERR_FAILURE;
    6866             : 
    6867          24 :     sqlite3 *hDB = m_poDS->GetDB();
    6868          24 :     OGRErr eErr = OGRERR_NONE;
    6869             : 
    6870             :     /* -------------------------------------------------------------------- */
    6871             :     /*      Drop triggers and index that look like to be related to the     */
    6872             :     /*      column if renaming. We re-install some indexes afterwards.      */
    6873             :     /* -------------------------------------------------------------------- */
    6874           0 :     std::unique_ptr<SQLResult> oTriggers;
    6875             :     // cppcheck-suppress knownConditionTrueFalse
    6876          24 :     if (bRenameCol && !bUseRenameColumn)
    6877             :     {
    6878           9 :         char *pszSQL = sqlite3_mprintf(
    6879             :             "SELECT name, type, sql FROM sqlite_master WHERE "
    6880             :             "type IN ('trigger','index') "
    6881             :             "AND lower(tbl_name)=lower('%q') AND sql LIKE '%%%q%%' LIMIT 10000",
    6882          18 :             m_pszTableName, SQLEscapeName(osOldColName).c_str());
    6883           9 :         oTriggers = SQLQuery(hDB, pszSQL);
    6884           9 :         sqlite3_free(pszSQL);
    6885             : 
    6886           9 :         if (!oTriggers)
    6887             :         {
    6888           0 :             eErr = OGRERR_FAILURE;
    6889             :         }
    6890             : 
    6891          11 :         for (int i = 0; oTriggers && i < oTriggers->RowCount(); i++)
    6892             :         {
    6893             :             pszSQL =
    6894           2 :                 sqlite3_mprintf("DROP %s \"%w\"", oTriggers->GetValue(1, i),
    6895             :                                 oTriggers->GetValue(0, i));
    6896           2 :             eErr = SQLCommand(hDB, pszSQL);
    6897           2 :             sqlite3_free(pszSQL);
    6898             :         }
    6899             :     }
    6900             : 
    6901          24 :     if (bUseRenameColumn)
    6902             :     {
    6903           4 :         if (eErr == OGRERR_NONE)
    6904             :         {
    6905           4 :             CPLDebug("GPKG", "Running ALTER TABLE RENAME COLUMN");
    6906           4 :             eErr = SQLCommand(
    6907           4 :                 m_poDS->GetDB(),
    6908           8 :                 CPLString()
    6909             :                     .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
    6910           8 :                             SQLEscapeName(m_pszTableName).c_str(),
    6911           8 :                             SQLEscapeName(osOldColName).c_str(),
    6912          16 :                             SQLEscapeName(osNewColName).c_str())
    6913             :                     .c_str());
    6914             :         }
    6915             :     }
    6916          20 :     else if (!bUseRewriteSchemaMethod)
    6917             :     {
    6918             :         /* --------------------------------------------------------------------
    6919             :          */
    6920             :         /*      If we are within a transaction, we cannot use the method */
    6921             :         /*      that consists in altering the database in a raw way. */
    6922             :         /* --------------------------------------------------------------------
    6923             :          */
    6924             :         const CPLString osFieldListForSelect(
    6925          18 :             BuildSelectFieldList(apoFieldsOld));
    6926             : 
    6927           9 :         if (eErr == OGRERR_NONE)
    6928             :         {
    6929           9 :             eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
    6930             :         }
    6931             :     }
    6932             :     else
    6933             :     {
    6934             :         /* --------------------------------------------------------------------
    6935             :          */
    6936             :         /*      Rewrite schema in a transaction by altering the database */
    6937             :         /*      schema in a rather raw way, as described at bottom of */
    6938             :         /*      https://www.sqlite.org/lang_altertable.html */
    6939             :         /* --------------------------------------------------------------------
    6940             :          */
    6941             : 
    6942             :         /* --------------------------------------------------------------------
    6943             :          */
    6944             :         /*      Collect schema version number. */
    6945             :         /* --------------------------------------------------------------------
    6946             :          */
    6947          11 :         int nSchemaVersion = SQLGetInteger(hDB, "PRAGMA schema_version", &eErr);
    6948             : 
    6949             :         /* --------------------------------------------------------------------
    6950             :          */
    6951             :         /*      Turn on writable schema. */
    6952             :         /* --------------------------------------------------------------------
    6953             :          */
    6954          11 :         if (eErr == OGRERR_NONE)
    6955             :         {
    6956          11 :             eErr = m_poDS->PragmaCheck("writable_schema=ON", "", 0);
    6957             :         }
    6958             : 
    6959             :         /* --------------------------------------------------------------------
    6960             :          */
    6961             :         /*      Rewrite CREATE TABLE statement. */
    6962             :         /* --------------------------------------------------------------------
    6963             :          */
    6964          11 :         if (eErr == OGRERR_NONE)
    6965             :         {
    6966             :             char *psSQLCreateTable =
    6967          11 :                 sqlite3_mprintf("CREATE TABLE \"%w\" (%s)", m_pszTableName,
    6968             :                                 osColumnsForCreate.c_str());
    6969          11 :             char *pszSQL = sqlite3_mprintf("UPDATE sqlite_master SET sql='%q' "
    6970             :                                            "WHERE type='table' AND name='%q'",
    6971             :                                            psSQLCreateTable, m_pszTableName);
    6972          11 :             eErr = SQLCommand(hDB, pszSQL);
    6973          11 :             sqlite3_free(psSQLCreateTable);
    6974          11 :             sqlite3_free(pszSQL);
    6975             :         }
    6976             : 
    6977             :         /* --------------------------------------------------------------------
    6978             :          */
    6979             :         /*      Increment schema number. */
    6980             :         /* --------------------------------------------------------------------
    6981             :          */
    6982          11 :         if (eErr == OGRERR_NONE)
    6983             :         {
    6984          11 :             char *pszSQL = sqlite3_mprintf("PRAGMA schema_version = %d",
    6985             :                                            nSchemaVersion + 1);
    6986          11 :             eErr = SQLCommand(hDB, pszSQL);
    6987          11 :             sqlite3_free(pszSQL);
    6988             :         }
    6989             : 
    6990             :         /* --------------------------------------------------------------------
    6991             :          */
    6992             :         /*      Turn off writable schema. */
    6993             :         /* --------------------------------------------------------------------
    6994             :          */
    6995          11 :         if (eErr == OGRERR_NONE)
    6996             :         {
    6997          11 :             eErr = m_poDS->PragmaCheck("writable_schema=OFF", "", 0);
    6998             :         }
    6999             :     }
    7000             : 
    7001             :     /* -------------------------------------------------------------------- */
    7002             :     /*      Update auxiliary tables                                        */
    7003             :     /* -------------------------------------------------------------------- */
    7004          24 :     if (bRenameCol && eErr == OGRERR_NONE)
    7005             :     {
    7006          12 :         eErr = RenameFieldInAuxiliaryTables(osOldColName.c_str(),
    7007             :                                             poNewFieldDefn->GetNameRef());
    7008             :     }
    7009             : 
    7010             :     /* -------------------------------------------------------------------- */
    7011             :     /*      Update gpkgext_relations if needed.                             */
    7012             :     /* -------------------------------------------------------------------- */
    7013          24 :     if (bRenameCol && eErr == OGRERR_NONE && m_poDS->HasGpkgextRelationsTable())
    7014             :     {
    7015           2 :         char *pszSQL = sqlite3_mprintf(
    7016             :             "UPDATE gpkgext_relations SET base_primary_column = '%q' WHERE "
    7017             :             "lower(base_table_name) = lower('%q') AND "
    7018             :             "lower(base_primary_column) = lower('%q')",
    7019             :             poNewFieldDefn->GetNameRef(), m_pszTableName, osOldColName.c_str());
    7020           2 :         eErr = SQLCommand(hDB, pszSQL);
    7021           2 :         sqlite3_free(pszSQL);
    7022             : 
    7023           2 :         if (eErr == OGRERR_NONE)
    7024             :         {
    7025             :             pszSQL =
    7026           2 :                 sqlite3_mprintf("UPDATE gpkgext_relations SET "
    7027             :                                 "related_primary_column = '%q' WHERE "
    7028             :                                 "lower(related_table_name) = lower('%q') AND "
    7029             :                                 "lower(related_primary_column) = lower('%q')",
    7030             :                                 poNewFieldDefn->GetNameRef(), m_pszTableName,
    7031             :                                 osOldColName.c_str());
    7032           2 :             eErr = SQLCommand(hDB, pszSQL);
    7033           2 :             sqlite3_free(pszSQL);
    7034             :         }
    7035           2 :         m_poDS->ClearCachedRelationships();
    7036             :     }
    7037             : 
    7038             :     /* -------------------------------------------------------------------- */
    7039             :     /*      Run integrity check only if explicitly required.                */
    7040             :     /* -------------------------------------------------------------------- */
    7041          46 :     if (eErr == OGRERR_NONE &&
    7042          22 :         CPLTestBool(CPLGetConfigOption("OGR_GPKG_INTEGRITY_CHECK", "NO")))
    7043             :     {
    7044           0 :         CPLDebug("GPKG", "Running PRAGMA integrity_check");
    7045           0 :         eErr = m_poDS->PragmaCheck("integrity_check", "ok", 1);
    7046             :     }
    7047             : 
    7048             :     /* -------------------------------------------------------------------- */
    7049             :     /*      Otherwise check foreign key integrity if enforcement of foreign */
    7050             :     /*      kets constraint is enabled.                                     */
    7051             :     /* -------------------------------------------------------------------- */
    7052          46 :     else if (eErr == OGRERR_NONE &&
    7053          22 :              SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
    7054             :     {
    7055           0 :         CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
    7056           0 :         eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
    7057             :     }
    7058             : 
    7059             :     /* -------------------------------------------------------------------- */
    7060             :     /*      Finish                                                          */
    7061             :     /* -------------------------------------------------------------------- */
    7062          24 :     if (eErr == OGRERR_NONE)
    7063             :     {
    7064             : 
    7065          22 :         eErr = m_poDS->SoftCommitTransaction();
    7066             : 
    7067             :         // We need to force database reopening due to schema change
    7068          33 :         if (eErr == OGRERR_NONE && bUseRewriteSchemaMethod &&
    7069          11 :             !m_poDS->ReOpenDB())
    7070             :         {
    7071           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen database");
    7072           0 :             eErr = OGRERR_FAILURE;
    7073             :         }
    7074          22 :         hDB = m_poDS->GetDB();
    7075             : 
    7076             :         /* --------------------------------------------------------------------
    7077             :          */
    7078             :         /*      Recreate indices. */
    7079             :         /* --------------------------------------------------------------------
    7080             :          */
    7081          24 :         for (int i = 0;
    7082          24 :              oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
    7083             :         {
    7084           2 :             if (EQUAL(oTriggers->GetValue(1, i), "index"))
    7085             :             {
    7086           4 :                 CPLString osSQL(oTriggers->GetValue(2, i));
    7087             :                 // CREATE INDEX idx_name ON table_name(column_name)
    7088           2 :                 char **papszTokens = SQLTokenize(osSQL);
    7089           2 :                 if (CSLCount(papszTokens) == 8 &&
    7090           2 :                     EQUAL(papszTokens[0], "CREATE") &&
    7091           2 :                     EQUAL(papszTokens[1], "INDEX") &&
    7092           6 :                     EQUAL(papszTokens[3], "ON") && EQUAL(papszTokens[5], "(") &&
    7093           2 :                     EQUAL(papszTokens[7], ")"))
    7094             :                 {
    7095           2 :                     osSQL = "CREATE INDEX ";
    7096           2 :                     osSQL += papszTokens[2];
    7097           2 :                     osSQL += " ON ";
    7098           2 :                     osSQL += papszTokens[4];
    7099           2 :                     osSQL += "(\"";
    7100           2 :                     osSQL += SQLEscapeName(osNewColName);
    7101           2 :                     osSQL += "\")";
    7102           2 :                     eErr = SQLCommand(hDB, osSQL);
    7103             :                 }
    7104           2 :                 CSLDestroy(papszTokens);
    7105             :             }
    7106             :         }
    7107             : 
    7108          22 :         if (eErr == OGRERR_NONE)
    7109             :         {
    7110             : 
    7111          22 :             if (m_poDS->IsInTransaction())
    7112             :             {
    7113             :                 m_apoFieldDefnChanges.emplace_back(
    7114           6 :                     std::make_unique<OGRFieldDefn>(poFieldDefnToAlter),
    7115           6 :                     iFieldToAlter, FieldChangeType::ALTER_FIELD,
    7116          18 :                     m_poDS->GetCurrentSavepoint());
    7117             :             }
    7118             : 
    7119          44 :             auto oTemporaryUnsealer(poFieldDefnToAlter->GetTemporaryUnsealer());
    7120          22 :             bool bNeedsEntryInGpkgDataColumns = false;
    7121             : 
    7122             :             // field type
    7123          22 :             if (nActualFlags & ALTER_TYPE_FLAG)
    7124             :             {
    7125           8 :                 poFieldDefnToAlter->SetSubType(OFSTNone);
    7126           8 :                 poFieldDefnToAlter->SetType(poNewFieldDefn->GetType());
    7127           8 :                 poFieldDefnToAlter->SetSubType(poNewFieldDefn->GetSubType());
    7128             :             }
    7129          31 :             if (poFieldDefnToAlter->GetType() == OFTString &&
    7130           9 :                 poFieldDefnToAlter->GetSubType() == OFSTJSON)
    7131             :             {
    7132           1 :                 bNeedsEntryInGpkgDataColumns = true;
    7133             :             }
    7134             : 
    7135             :             // name
    7136          22 :             if (nActualFlags & ALTER_NAME_FLAG)
    7137             :             {
    7138          12 :                 poFieldDefnToAlter->SetName(poNewFieldDefn->GetNameRef());
    7139             :             }
    7140             : 
    7141             :             // width/precision
    7142          22 :             if (nActualFlags & ALTER_WIDTH_PRECISION_FLAG)
    7143             :             {
    7144           2 :                 poFieldDefnToAlter->SetWidth(poNewFieldDefn->GetWidth());
    7145           2 :                 poFieldDefnToAlter->SetPrecision(
    7146             :                     poNewFieldDefn->GetPrecision());
    7147             :             }
    7148             : 
    7149             :             // constraints
    7150          22 :             if (nActualFlags & ALTER_NULLABLE_FLAG)
    7151           2 :                 poFieldDefnToAlter->SetNullable(poNewFieldDefn->IsNullable());
    7152          22 :             if (nActualFlags & ALTER_DEFAULT_FLAG)
    7153           2 :                 poFieldDefnToAlter->SetDefault(poNewFieldDefn->GetDefault());
    7154          22 :             if (nActualFlags & ALTER_UNIQUE_FLAG)
    7155           2 :                 poFieldDefnToAlter->SetUnique(poNewFieldDefn->IsUnique());
    7156             : 
    7157             :             // domain
    7158          25 :             if ((nActualFlags & ALTER_DOMAIN_FLAG) &&
    7159           3 :                 poFieldDefnToAlter->GetDomainName() !=
    7160           3 :                     poNewFieldDefn->GetDomainName())
    7161             :             {
    7162           3 :                 poFieldDefnToAlter->SetDomainName(
    7163             :                     poNewFieldDefn->GetDomainName());
    7164             :             }
    7165          22 :             if (!poFieldDefnToAlter->GetDomainName().empty())
    7166             :             {
    7167           3 :                 bNeedsEntryInGpkgDataColumns = true;
    7168             :             }
    7169             : 
    7170             :             // alternative name
    7171          27 :             if ((nActualFlags & ALTER_ALTERNATIVE_NAME_FLAG) &&
    7172           5 :                 strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
    7173             :                        poNewFieldDefn->GetAlternativeNameRef()) != 0)
    7174             :             {
    7175           5 :                 poFieldDefnToAlter->SetAlternativeName(
    7176             :                     poNewFieldDefn->GetAlternativeNameRef());
    7177             :             }
    7178          44 :             if (!std::string(poFieldDefnToAlter->GetAlternativeNameRef())
    7179          22 :                      .empty())
    7180             :             {
    7181           7 :                 bNeedsEntryInGpkgDataColumns = true;
    7182             :             }
    7183             : 
    7184             :             // comment
    7185          25 :             if ((nActualFlags & ALTER_COMMENT_FLAG) &&
    7186           3 :                 poFieldDefnToAlter->GetComment() !=
    7187           3 :                     poNewFieldDefn->GetComment())
    7188             :             {
    7189           3 :                 poFieldDefnToAlter->SetComment(poNewFieldDefn->GetComment());
    7190             :             }
    7191          22 :             if (!poFieldDefnToAlter->GetComment().empty())
    7192             :             {
    7193           3 :                 bNeedsEntryInGpkgDataColumns = true;
    7194             :             }
    7195             : 
    7196          22 :             if (m_poDS->HasDataColumnsTable())
    7197             :             {
    7198          14 :                 char *pszSQL = sqlite3_mprintf(
    7199             :                     "DELETE FROM gpkg_data_columns WHERE "
    7200             :                     "lower(table_name) = lower('%q') AND "
    7201             :                     "lower(column_name) = lower('%q')",
    7202             :                     m_pszTableName, poFieldDefnToAlter->GetNameRef());
    7203          14 :                 eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    7204          14 :                 sqlite3_free(pszSQL);
    7205             :             }
    7206             : 
    7207          22 :             if (bNeedsEntryInGpkgDataColumns)
    7208             :             {
    7209          12 :                 if (!DoSpecialProcessingForColumnCreation(poFieldDefnToAlter))
    7210           0 :                     eErr = OGRERR_FAILURE;
    7211             :             }
    7212             : 
    7213          22 :             ResetReading();
    7214             :         }
    7215             :     }
    7216             :     else
    7217             :     {
    7218           2 :         m_poDS->SoftRollbackTransaction();
    7219             :     }
    7220             : 
    7221          24 :     return eErr;
    7222             : }
    7223             : 
    7224             : /************************************************************************/
    7225             : /*                         AlterGeomFieldDefn()                         */
    7226             : /************************************************************************/
    7227             : 
    7228           8 : OGRErr OGRGeoPackageTableLayer::AlterGeomFieldDefn(
    7229             :     int iGeomFieldToAlter, const OGRGeomFieldDefn *poNewGeomFieldDefn,
    7230             :     int nFlagsIn)
    7231             : {
    7232           8 :     if (!m_bFeatureDefnCompleted)
    7233           1 :         GetLayerDefn();
    7234           8 :     if (!CheckUpdatableTable("AlterGeomFieldDefn"))
    7235           0 :         return OGRERR_FAILURE;
    7236             : 
    7237          16 :     if (iGeomFieldToAlter < 0 ||
    7238           8 :         iGeomFieldToAlter >= m_poFeatureDefn->GetGeomFieldCount())
    7239             :     {
    7240           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    7241           0 :         return OGRERR_FAILURE;
    7242             :     }
    7243             : 
    7244             :     /* -------------------------------------------------------------------- */
    7245             :     /*      Deferred actions, reset state.                                   */
    7246             :     /* -------------------------------------------------------------------- */
    7247           8 :     ResetReading();
    7248           8 :     RunDeferredCreationIfNecessary();
    7249           8 :     if (m_bThreadRTreeStarted)
    7250           0 :         CancelAsyncRTree();
    7251           8 :     if (!RunDeferredSpatialIndexUpdate())
    7252           0 :         return OGRERR_FAILURE;
    7253           8 :     RevertWorkaroundUpdate1TriggerIssue();
    7254             : 
    7255             :     /* -------------------------------------------------------------------- */
    7256             :     /*      Drop any iterator since we change the DB structure              */
    7257             :     /* -------------------------------------------------------------------- */
    7258           8 :     m_poDS->ResetReadingAllLayers();
    7259             : 
    7260           8 :     auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter);
    7261          16 :     auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
    7262             : 
    7263           8 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
    7264             :     {
    7265             :         // could be potentially done. Requires rewriting the CREATE TABLE
    7266             :         // statement
    7267           0 :         if (poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
    7268             :         {
    7269           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7270             :                      "Altering the geometry field type is not currently "
    7271             :                      "supported for "
    7272             :                      "GeoPackage");
    7273           0 :             return OGRERR_FAILURE;
    7274             :         }
    7275             :     }
    7276             : 
    7277           8 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
    7278             :     {
    7279             :         // could be potentially done. Requires rewriting the CREATE TABLE
    7280             :         // statement
    7281           0 :         if (poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
    7282             :         {
    7283           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7284             :                      "Altering the nullable state of the geometry field "
    7285             :                      "is not currently supported for GeoPackage");
    7286           0 :             return OGRERR_FAILURE;
    7287             :         }
    7288             :     }
    7289             : 
    7290          12 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0 &&
    7291           4 :         strcmp(poGeomFieldDefn->GetNameRef(),
    7292             :                poNewGeomFieldDefn->GetNameRef()) != 0)
    7293             :     {
    7294           4 :         const bool bHasSpatialIndex = HasSpatialIndex();
    7295             : 
    7296           4 :         if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    7297           0 :             return OGRERR_FAILURE;
    7298             : 
    7299             :         // Rename geometry field
    7300           4 :         auto eErr = SQLCommand(
    7301           4 :             m_poDS->GetDB(),
    7302           4 :             CPLString()
    7303             :                 .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
    7304           8 :                         SQLEscapeName(m_pszTableName).c_str(),
    7305           8 :                         SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7306          16 :                         SQLEscapeName(poNewGeomFieldDefn->GetNameRef()).c_str())
    7307             :                 .c_str());
    7308           4 :         if (eErr != OGRERR_NONE)
    7309             :         {
    7310           0 :             m_poDS->SoftRollbackTransaction();
    7311           0 :             return OGRERR_FAILURE;
    7312             :         }
    7313             : 
    7314             :         // Update gpkg_geometry_columns
    7315           4 :         eErr = SQLCommand(
    7316           4 :             m_poDS->GetDB(),
    7317           4 :             CPLString()
    7318             :                 .Printf(
    7319             :                     "UPDATE gpkg_geometry_columns SET column_name = '%s' "
    7320             :                     "WHERE lower(table_name) = lower('%s') "
    7321             :                     "AND lower(column_name) = lower('%s')",
    7322           8 :                     SQLEscapeLiteral(poNewGeomFieldDefn->GetNameRef()).c_str(),
    7323           8 :                     SQLEscapeLiteral(m_pszTableName).c_str(),
    7324          16 :                     SQLEscapeLiteral(poGeomFieldDefn->GetNameRef()).c_str())
    7325             :                 .c_str());
    7326           4 :         if (eErr != OGRERR_NONE)
    7327             :         {
    7328           0 :             m_poDS->SoftRollbackTransaction();
    7329           0 :             return OGRERR_FAILURE;
    7330             :         }
    7331             : 
    7332             :         // Update auxiliary tables
    7333           4 :         eErr = RenameFieldInAuxiliaryTables(poGeomFieldDefn->GetNameRef(),
    7334             :                                             poNewGeomFieldDefn->GetNameRef());
    7335           4 :         if (eErr != OGRERR_NONE)
    7336             :         {
    7337           0 :             m_poDS->SoftRollbackTransaction();
    7338           0 :             return OGRERR_FAILURE;
    7339             :         }
    7340             : 
    7341           4 :         std::string osNewRTreeName;
    7342           4 :         if (bHasSpatialIndex)
    7343             :         {
    7344           4 :             osNewRTreeName = "rtree_";
    7345           4 :             osNewRTreeName += m_pszTableName;
    7346           4 :             osNewRTreeName += "_";
    7347           4 :             osNewRTreeName += poNewGeomFieldDefn->GetNameRef();
    7348             : 
    7349             :             // Rename spatial index tables (not strictly needed, but for
    7350             :             // consistency)
    7351             :             eErr =
    7352           4 :                 SQLCommand(m_poDS->GetDB(),
    7353           4 :                            CPLString().Printf(
    7354             :                                "ALTER TABLE \"%s\" RENAME TO \"%s\"",
    7355           8 :                                SQLEscapeName(m_osRTreeName.c_str()).c_str(),
    7356          12 :                                SQLEscapeName(osNewRTreeName.c_str()).c_str()));
    7357           4 :             if (eErr != OGRERR_NONE)
    7358             :             {
    7359           0 :                 m_poDS->SoftRollbackTransaction();
    7360           0 :                 return OGRERR_FAILURE;
    7361             :             }
    7362             : 
    7363             :             // Finally rename triggers (not strictly needed, but for
    7364             :             // consistency)
    7365           4 :             std::string osTriggerSQL;
    7366           4 :             osTriggerSQL = ReturnSQLDropSpatialIndexTriggers();
    7367           4 :             osTriggerSQL += ";";
    7368           8 :             osTriggerSQL += ReturnSQLCreateSpatialIndexTriggers(
    7369           4 :                 nullptr, poNewGeomFieldDefn->GetNameRef());
    7370           4 :             eErr = SQLCommand(m_poDS->GetDB(), osTriggerSQL.c_str());
    7371           4 :             if (eErr != OGRERR_NONE)
    7372             :             {
    7373           0 :                 m_poDS->SoftRollbackTransaction();
    7374           0 :                 return OGRERR_FAILURE;
    7375             :             }
    7376             :         }
    7377             : 
    7378           4 :         eErr = m_poDS->SoftCommitTransaction();
    7379           4 :         if (eErr != OGRERR_NONE)
    7380             :         {
    7381           0 :             return OGRERR_FAILURE;
    7382             :         }
    7383             : 
    7384           4 :         poGeomFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
    7385             : 
    7386           4 :         if (bHasSpatialIndex)
    7387             :         {
    7388           4 :             m_osRTreeName = osNewRTreeName;
    7389             :         }
    7390             :     }
    7391             : 
    7392           8 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0 ||
    7393           5 :         (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
    7394             :     {
    7395           4 :         const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
    7396           4 :         const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
    7397             : 
    7398           0 :         std::unique_ptr<OGRSpatialReference> poNewSRS;
    7399           4 :         if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0)
    7400             :         {
    7401           3 :             if (poNewSRSRef != nullptr)
    7402             :             {
    7403           2 :                 poNewSRS.reset(poNewSRSRef->Clone());
    7404           2 :                 if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) ==
    7405             :                     0)
    7406             :                 {
    7407           2 :                     if (poOldSRS)
    7408           1 :                         poNewSRS->SetCoordinateEpoch(
    7409             :                             poOldSRS->GetCoordinateEpoch());
    7410             :                 }
    7411             :             }
    7412             :         }
    7413           1 :         else if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
    7414             :         {
    7415           1 :             if (poOldSRS != nullptr)
    7416             :             {
    7417           1 :                 poNewSRS.reset(poOldSRS->Clone());
    7418           1 :                 if (poNewSRSRef)
    7419           1 :                     poNewSRS->SetCoordinateEpoch(
    7420             :                         poNewSRSRef->GetCoordinateEpoch());
    7421             :             }
    7422             :         }
    7423             : 
    7424           4 :         const char *const apszOptions[] = {
    7425             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    7426           4 :         if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
    7427          10 :             (poOldSRS != nullptr && poNewSRS == nullptr) ||
    7428           2 :             (poOldSRS != nullptr && poNewSRS != nullptr &&
    7429           2 :              !poOldSRS->IsSame(poNewSRS.get(), apszOptions)))
    7430             :         {
    7431             :             // Temporary remove foreign key checks
    7432             :             const GPKGTemporaryForeignKeyCheckDisabler
    7433           4 :                 oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
    7434             : 
    7435           4 :             if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    7436           0 :                 return OGRERR_FAILURE;
    7437             : 
    7438           4 :             const int nNewSRID = m_poDS->GetSrsId(poNewSRS.get());
    7439             : 
    7440             :             // Replace the old SRID by the new ones in geometry blobs
    7441           4 :             int32_t nNewSRID_LSB = nNewSRID;
    7442           4 :             CPL_LSBPTR32(&nNewSRID_LSB);
    7443           4 :             GByte abySRID_LSB[5] = {0, 0, 0, 0};
    7444           4 :             memcpy(abySRID_LSB, &nNewSRID_LSB, 4);
    7445           4 :             char *pszSRID_LSB_HEX = CPLBinaryToHex(4, abySRID_LSB);
    7446             : 
    7447           4 :             int32_t nNewSRID_MSB = nNewSRID;
    7448           4 :             CPL_MSBPTR32(&nNewSRID_MSB);
    7449           4 :             GByte abySRID_MSB[5] = {0, 0, 0, 0};
    7450           4 :             memcpy(abySRID_MSB, &nNewSRID_MSB, 4);
    7451           4 :             char *pszSRID_MSB_HEX = CPLBinaryToHex(4, abySRID_MSB);
    7452             : 
    7453             :             // Black magic below...
    7454             :             // the substr(hex(...) IN ('0','2',...'E') checks if bit 0 of the
    7455             :             // 4th byte is 0 and use that to decide how to replace the old SRID
    7456             :             // by the new one.
    7457           4 :             CPLString osSQL;
    7458             :             osSQL.Printf(
    7459             :                 "UPDATE \"%s\" SET \"%s\" = "
    7460             :                 "CAST(substr(\"%s\", 1, 4) || "
    7461             :                 "(CASE WHEN substr(hex(substr(\"%s\", 4, 1)),2) IN "
    7462             :                 "('0','2','4','6','8','A','C','E') "
    7463             :                 "THEN x'%s' ELSE x'%s' END) || substr(\"%s\", 9) AS BLOB) "
    7464             :                 "WHERE \"%s\" IS NOT NULL",
    7465           8 :                 SQLEscapeName(m_pszTableName).c_str(),
    7466           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7467           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7468           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7469             :                 pszSRID_MSB_HEX, pszSRID_LSB_HEX,
    7470           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7471          28 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str());
    7472           4 :             OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL.c_str());
    7473           4 :             CPLFree(pszSRID_MSB_HEX);
    7474           4 :             CPLFree(pszSRID_LSB_HEX);
    7475           4 :             if (eErr != OGRERR_NONE)
    7476             :             {
    7477           0 :                 m_poDS->SoftRollbackTransaction();
    7478           0 :                 return OGRERR_FAILURE;
    7479             :             }
    7480             : 
    7481           4 :             char *pszSQL = sqlite3_mprintf(
    7482             :                 "UPDATE gpkg_contents SET srs_id = %d WHERE table_name = '%q'",
    7483             :                 nNewSRID, m_pszTableName);
    7484           4 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    7485           4 :             sqlite3_free(pszSQL);
    7486           4 :             if (eErr != OGRERR_NONE)
    7487             :             {
    7488           0 :                 m_poDS->SoftRollbackTransaction();
    7489           0 :                 return OGRERR_FAILURE;
    7490             :             }
    7491             : 
    7492           4 :             pszSQL = sqlite3_mprintf(
    7493             :                 "UPDATE gpkg_geometry_columns SET srs_id = %d WHERE "
    7494             :                 "table_name = '%q' AND column_name = '%q'",
    7495             :                 nNewSRID, m_pszTableName, poGeomFieldDefn->GetNameRef());
    7496           4 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    7497           4 :             sqlite3_free(pszSQL);
    7498           4 :             if (eErr != OGRERR_NONE)
    7499             :             {
    7500           0 :                 m_poDS->SoftRollbackTransaction();
    7501           0 :                 return OGRERR_FAILURE;
    7502             :             }
    7503             : 
    7504           4 :             if (m_poDS->SoftCommitTransaction() != OGRERR_NONE)
    7505             :             {
    7506           0 :                 return OGRERR_FAILURE;
    7507             :             }
    7508             : 
    7509           4 :             m_iSrs = nNewSRID;
    7510           4 :             OGRSpatialReference *poSRS = poNewSRS.release();
    7511           4 :             poGeomFieldDefn->SetSpatialRef(poSRS);
    7512           4 :             if (poSRS)
    7513           3 :                 poSRS->Release();
    7514             :         }
    7515             :     }
    7516             : 
    7517           8 :     return OGRERR_NONE;
    7518             : }
    7519             : 
    7520             : /************************************************************************/
    7521             : /*                           ReorderFields()                            */
    7522             : /************************************************************************/
    7523             : 
    7524           4 : OGRErr OGRGeoPackageTableLayer::ReorderFields(int *panMap)
    7525             : {
    7526           4 :     if (!m_bFeatureDefnCompleted)
    7527           0 :         GetLayerDefn();
    7528           4 :     if (!CheckUpdatableTable("ReorderFields"))
    7529           2 :         return OGRERR_FAILURE;
    7530             : 
    7531           2 :     if (m_poFeatureDefn->GetFieldCount() == 0)
    7532           0 :         return OGRERR_NONE;
    7533             : 
    7534           2 :     OGRErr eErr = OGRCheckPermutation(panMap, m_poFeatureDefn->GetFieldCount());
    7535           2 :     if (eErr != OGRERR_NONE)
    7536           1 :         return eErr;
    7537             : 
    7538             :     /* -------------------------------------------------------------------- */
    7539             :     /*      Deferred actions, reset state.                                   */
    7540             :     /* -------------------------------------------------------------------- */
    7541           1 :     ResetReading();
    7542           1 :     RunDeferredCreationIfNecessary();
    7543           1 :     if (m_bThreadRTreeStarted)
    7544           0 :         CancelAsyncRTree();
    7545           1 :     if (!RunDeferredSpatialIndexUpdate())
    7546           0 :         return OGRERR_FAILURE;
    7547             : 
    7548             :     /* -------------------------------------------------------------------- */
    7549             :     /*      Drop any iterator since we change the DB structure              */
    7550             :     /* -------------------------------------------------------------------- */
    7551           1 :     m_poDS->ResetReadingAllLayers();
    7552             : 
    7553             :     /* -------------------------------------------------------------------- */
    7554             :     /*      Build list of old fields, and the list of new fields.           */
    7555             :     /* -------------------------------------------------------------------- */
    7556           2 :     std::vector<OGRFieldDefn *> apoFields;
    7557           3 :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    7558             :     {
    7559             :         OGRFieldDefn *poFieldDefn =
    7560           2 :             m_poFeatureDefn->GetFieldDefn(panMap[iField]);
    7561           2 :         apoFields.push_back(poFieldDefn);
    7562             :     }
    7563             : 
    7564           2 :     const CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
    7565           2 :     const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
    7566             : 
    7567             :     /* -------------------------------------------------------------------- */
    7568             :     /*      Recreate table in a transaction                                 */
    7569             :     /* -------------------------------------------------------------------- */
    7570           1 :     if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    7571           0 :         return OGRERR_FAILURE;
    7572             : 
    7573           1 :     eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
    7574             : 
    7575             :     /* -------------------------------------------------------------------- */
    7576             :     /*      Finish                                                          */
    7577             :     /* -------------------------------------------------------------------- */
    7578           1 :     if (eErr == OGRERR_NONE)
    7579             :     {
    7580           1 :         eErr = m_poDS->SoftCommitTransaction();
    7581             : 
    7582           1 :         if (eErr == OGRERR_NONE)
    7583             :         {
    7584           1 :             eErr = whileUnsealing(m_poFeatureDefn)->ReorderFieldDefns(panMap);
    7585             :         }
    7586             : 
    7587           1 :         ResetReading();
    7588             :     }
    7589             :     else
    7590             :     {
    7591           0 :         m_poDS->SoftRollbackTransaction();
    7592             :     }
    7593             : 
    7594           1 :     return eErr;
    7595             : }
    7596             : 
    7597             : /************************************************************************/
    7598             : /*                   OGR_GPKG_GeometryTypeAggregate()                   */
    7599             : /************************************************************************/
    7600             : 
    7601             : namespace
    7602             : {
    7603             : struct GeometryTypeAggregateContext
    7604             : {
    7605             :     sqlite3 *m_hDB = nullptr;
    7606             :     int m_nFlags = 0;
    7607             :     bool m_bIsGeometryTypeAggregateInterrupted = false;
    7608             :     std::map<OGRwkbGeometryType, int64_t> m_oMapCount{};
    7609             :     std::set<OGRwkbGeometryType> m_oSetNotNull{};
    7610             : 
    7611          14 :     explicit GeometryTypeAggregateContext(sqlite3 *hDB, int nFlags)
    7612          14 :         : m_hDB(hDB), m_nFlags(nFlags)
    7613             :     {
    7614          14 :     }
    7615             : 
    7616             :     GeometryTypeAggregateContext(const GeometryTypeAggregateContext &) = delete;
    7617             :     GeometryTypeAggregateContext &
    7618             :     operator=(const GeometryTypeAggregateContext &) = delete;
    7619             : 
    7620           2 :     void SetGeometryTypeAggregateInterrupted(bool b)
    7621             :     {
    7622           2 :         m_bIsGeometryTypeAggregateInterrupted = b;
    7623           2 :         if (b)
    7624           2 :             sqlite3_interrupt(m_hDB);
    7625           2 :     }
    7626             : };
    7627             : 
    7628             : }  // namespace
    7629             : 
    7630        1376 : static void OGR_GPKG_GeometryTypeAggregate_Step(sqlite3_context *pContext,
    7631             :                                                 int /*argc*/,
    7632             :                                                 sqlite3_value **argv)
    7633             : {
    7634             :     const GByte *pabyBLOB =
    7635        1376 :         reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
    7636             : 
    7637             :     auto poContext = static_cast<GeometryTypeAggregateContext *>(
    7638        1376 :         sqlite3_user_data(pContext));
    7639             : 
    7640        1376 :     OGRwkbGeometryType eGeometryType = wkbNone;
    7641        1376 :     OGRErr err = OGRERR_FAILURE;
    7642        1376 :     if (pabyBLOB != nullptr)
    7643             :     {
    7644             :         GPkgHeader sHeader;
    7645        1353 :         const int nBLOBLen = sqlite3_value_bytes(argv[0]);
    7646        2706 :         if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, &sHeader) == OGRERR_NONE &&
    7647        1353 :             static_cast<size_t>(nBLOBLen) >= sHeader.nHeaderLen + 5)
    7648             :         {
    7649        1353 :             err = OGRReadWKBGeometryType(pabyBLOB + sHeader.nHeaderLen,
    7650             :                                          wkbVariantIso, &eGeometryType);
    7651        1353 :             if (eGeometryType == wkbGeometryCollection25D &&
    7652           4 :                 (poContext->m_nFlags & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
    7653             :             {
    7654             :                 auto poGeom = std::unique_ptr<OGRGeometry>(
    7655           2 :                     GPkgGeometryToOGR(pabyBLOB, nBLOBLen, nullptr));
    7656           1 :                 if (poGeom)
    7657             :                 {
    7658           1 :                     const auto poGC = poGeom->toGeometryCollection();
    7659           1 :                     if (poGC->getNumGeometries() > 0)
    7660             :                     {
    7661             :                         auto eSubGeomType =
    7662           1 :                             poGC->getGeometryRef(0)->getGeometryType();
    7663           1 :                         if (eSubGeomType == wkbTINZ)
    7664           1 :                             eGeometryType = wkbTINZ;
    7665             :                     }
    7666             :                 }
    7667             :             }
    7668             :         }
    7669             :     }
    7670             :     else
    7671             :     {
    7672             :         // NULL geometry
    7673          23 :         err = OGRERR_NONE;
    7674             :     }
    7675        1376 :     if (err == OGRERR_NONE)
    7676             :     {
    7677        1376 :         ++poContext->m_oMapCount[eGeometryType];
    7678        1376 :         if (eGeometryType != wkbNone &&
    7679        1353 :             (poContext->m_nFlags & OGR_GGT_STOP_IF_MIXED) != 0)
    7680             :         {
    7681           4 :             poContext->m_oSetNotNull.insert(eGeometryType);
    7682           4 :             if (poContext->m_oSetNotNull.size() == 2)
    7683             :             {
    7684           2 :                 poContext->SetGeometryTypeAggregateInterrupted(true);
    7685             :             }
    7686             :         }
    7687             :     }
    7688        1376 : }
    7689             : 
    7690          11 : static void OGR_GPKG_GeometryTypeAggregate_Finalize(sqlite3_context *)
    7691             : {
    7692          11 : }
    7693             : 
    7694             : /************************************************************************/
    7695             : /*                         GetGeometryTypes()                           */
    7696             : /************************************************************************/
    7697             : 
    7698          15 : OGRGeometryTypeCounter *OGRGeoPackageTableLayer::GetGeometryTypes(
    7699             :     int iGeomField, int nFlagsGGT, int &nEntryCountOut,
    7700             :     GDALProgressFunc pfnProgress, void *pProgressData)
    7701             : {
    7702          15 :     const OGRFeatureDefn *poDefn = GetLayerDefn();
    7703             : 
    7704             :     /* -------------------------------------------------------------------- */
    7705             :     /*      Deferred actions, reset state.                                   */
    7706             :     /* -------------------------------------------------------------------- */
    7707          15 :     RunDeferredCreationIfNecessary();
    7708          15 :     if (!RunDeferredSpatialIndexUpdate())
    7709             :     {
    7710           0 :         nEntryCountOut = 0;
    7711           0 :         return nullptr;
    7712             :     }
    7713             : 
    7714          15 :     const int nGeomFieldCount = poDefn->GetGeomFieldCount();
    7715          15 :     if (iGeomField < 0 || iGeomField >= nGeomFieldCount)
    7716             :     {
    7717           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for iGeomField");
    7718           1 :         nEntryCountOut = 0;
    7719           1 :         return nullptr;
    7720             :     }
    7721             : 
    7722             : #ifdef SQLITE_HAS_PROGRESS_HANDLER
    7723             :     struct CancelCallback
    7724             :     {
    7725             :         sqlite3 *m_hDB = nullptr;
    7726             :         GDALProgressFunc m_pfnProgress = nullptr;
    7727             :         void *m_pProgressData = nullptr;
    7728             : 
    7729          14 :         CancelCallback(sqlite3 *hDB, GDALProgressFunc pfnProgressIn,
    7730             :                        void *pProgressDataIn)
    7731          14 :             : m_hDB(hDB),
    7732          14 :               m_pfnProgress(pfnProgressIn != GDALDummyProgress ? pfnProgressIn
    7733             :                                                                : nullptr),
    7734          14 :               m_pProgressData(pProgressDataIn)
    7735             :         {
    7736          14 :             if (m_pfnProgress)
    7737             :             {
    7738             :                 // If changing that value, update
    7739             :                 // ogr_gpkg.py::test_ogr_gpkg_get_geometry_types
    7740           2 :                 constexpr int COUNT_VM_INSTRUCTIONS = 1000;
    7741           2 :                 sqlite3_progress_handler(m_hDB, COUNT_VM_INSTRUCTIONS,
    7742             :                                          ProgressHandler, this);
    7743             :             }
    7744          14 :         }
    7745             : 
    7746          14 :         ~CancelCallback()
    7747          14 :         {
    7748          14 :             if (m_pfnProgress)
    7749             :             {
    7750           2 :                 sqlite3_progress_handler(m_hDB, 0, nullptr, nullptr);
    7751             :             }
    7752          14 :         }
    7753             : 
    7754             :         CancelCallback(const CancelCallback &) = delete;
    7755             :         CancelCallback &operator=(const CancelCallback &) = delete;
    7756             : 
    7757           1 :         static int ProgressHandler(void *pData)
    7758             :         {
    7759           1 :             CancelCallback *psCancelCallback =
    7760             :                 static_cast<CancelCallback *>(pData);
    7761           1 :             return psCancelCallback->m_pfnProgress != nullptr &&
    7762           1 :                            psCancelCallback->m_pfnProgress(
    7763             :                                0.0, "", psCancelCallback->m_pProgressData)
    7764           2 :                        ? 0
    7765           1 :                        : 1;
    7766             :         }
    7767             :     };
    7768             : 
    7769          28 :     CancelCallback oCancelCallback(m_poDS->hDB, pfnProgress, pProgressData);
    7770             : #else
    7771             :     CPL_IGNORE_RET_VAL(pfnProgress);
    7772             :     CPL_IGNORE_RET_VAL(pProgressData);
    7773             : #endif
    7774             : 
    7775             :     // For internal use only
    7776             : 
    7777          28 :     GeometryTypeAggregateContext sContext(m_poDS->hDB, nFlagsGGT);
    7778             : 
    7779          28 :     CPLString osFuncName;
    7780          14 :     osFuncName.Printf("OGR_GPKG_GeometryTypeAggregate_INTERNAL_%p", &sContext);
    7781             : 
    7782          14 :     sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
    7783             :                             &sContext, nullptr,
    7784             :                             OGR_GPKG_GeometryTypeAggregate_Step,
    7785             :                             OGR_GPKG_GeometryTypeAggregate_Finalize);
    7786             : 
    7787             :     // Using this aggregate function is slightly faster than using
    7788             :     // sqlite3_step() to loop over each geometry blob (650 ms vs 750ms on a 1.6
    7789             :     // GB db with 3.3 million features)
    7790          28 :     char *pszSQL = sqlite3_mprintf(
    7791             :         "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
    7792          14 :         poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
    7793          30 :         m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
    7794          14 :     char *pszErrMsg = nullptr;
    7795             :     const int rc =
    7796          14 :         sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
    7797             : 
    7798             :     // Delete function
    7799          14 :     sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
    7800             :                             nullptr, nullptr, nullptr, nullptr);
    7801             : 
    7802          14 :     if (rc != SQLITE_OK && !sContext.m_bIsGeometryTypeAggregateInterrupted)
    7803             :     {
    7804           1 :         if (rc != SQLITE_INTERRUPT)
    7805             :         {
    7806           0 :             CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
    7807             :                      pszSQL, pszErrMsg);
    7808             :         }
    7809           1 :         sqlite3_free(pszErrMsg);
    7810           1 :         sqlite3_free(pszSQL);
    7811           1 :         nEntryCountOut = 0;
    7812           1 :         return nullptr;
    7813             :     }
    7814          13 :     sqlite3_free(pszErrMsg);
    7815          13 :     sqlite3_free(pszSQL);
    7816             : 
    7817             :     // Format result
    7818          13 :     nEntryCountOut = static_cast<int>(sContext.m_oMapCount.size());
    7819             :     OGRGeometryTypeCounter *pasRet = static_cast<OGRGeometryTypeCounter *>(
    7820          13 :         CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
    7821          13 :     int i = 0;
    7822          47 :     for (const auto &sEntry : sContext.m_oMapCount)
    7823             :     {
    7824          34 :         pasRet[i].eGeomType = sEntry.first;
    7825          34 :         pasRet[i].nCount = sEntry.second;
    7826          34 :         ++i;
    7827             :     }
    7828          13 :     return pasRet;
    7829             : }
    7830             : 
    7831             : /************************************************************************/
    7832             : /*                    OGR_GPKG_FillArrowArray_Step()                    */
    7833             : /************************************************************************/
    7834             : 
    7835             : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
    7836             :                                   sqlite3_value **argv);
    7837             : 
    7838       65095 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
    7839             :                                   sqlite3_value **argv)
    7840             : {
    7841             :     auto psFillArrowArray = static_cast<OGRGPKGTableLayerFillArrowArray *>(
    7842       65095 :         sqlite3_user_data(pContext));
    7843             : 
    7844             :     // To be called under psFillArrowArray->oMutex
    7845             :     const auto SubmitBatchToCaller =
    7846         762 :         [psFillArrowArray](std::unique_lock<std::mutex> &oLock)
    7847             :     {
    7848         127 :         psFillArrowArray->psHelper->Shrink(psFillArrowArray->nCountRows);
    7849         127 :         psFillArrowArray->bThreadReady = true;
    7850         127 :         psFillArrowArray->oCV.notify_one();
    7851         254 :         while (psFillArrowArray->nCountRows > 0)
    7852             :         {
    7853         127 :             psFillArrowArray->oCV.wait(oLock);
    7854             :         }
    7855       65221 :     };
    7856             : 
    7857             :     const auto DealWithTooBigArray =
    7858         549 :         [&SubmitBatchToCaller, psFillArrowArray](int iFeat)
    7859             :     {
    7860         134 :         CPLDebug("GPKG",
    7861             :                  "OGR_GPKG_FillArrowArray_Step(): premature "
    7862             :                  "notification of %d features to consumer due "
    7863             :                  "to too big array",
    7864             :                  iFeat);
    7865         134 :         if (psFillArrowArray->bAsynchronousMode)
    7866             :         {
    7867         242 :             std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7868         121 :             psFillArrowArray->bMemoryLimitReached = true;
    7869         121 :             SubmitBatchToCaller(oLock);
    7870             :         }
    7871             :         else
    7872             :         {
    7873             :             {
    7874          13 :                 std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7875          13 :                 psFillArrowArray->bMemoryLimitReached = true;
    7876             :             }
    7877          13 :             sqlite3_interrupt(psFillArrowArray->hDB);
    7878             :         }
    7879         134 :     };
    7880             : 
    7881             :     {
    7882       65094 :         std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7883      130190 :         if (psFillArrowArray->nCountRows >=
    7884       65095 :             psFillArrowArray->psHelper->m_nMaxBatchSize)
    7885             :         {
    7886           6 :             if (psFillArrowArray->bAsynchronousMode)
    7887             :             {
    7888           6 :                 SubmitBatchToCaller(oLock);
    7889             :                 // Note that psFillArrowArray->psHelper.get() will generally now be
    7890             :                 // different from before the wait()
    7891             :             }
    7892             :             else
    7893             :             {
    7894             :                 // should not happen !
    7895             :                 psFillArrowArray->osErrorMsg = "OGR_GPKG_FillArrowArray_Step() "
    7896           0 :                                                "got more rows than expected!";
    7897           0 :                 sqlite3_interrupt(psFillArrowArray->hDB);
    7898           0 :                 psFillArrowArray->bErrorOccurred = true;
    7899           0 :                 return;
    7900             :             }
    7901             :         }
    7902       65095 :         if (psFillArrowArray->nCountRows < 0)
    7903           2 :             return;
    7904             :     }
    7905             : 
    7906       65091 :     if (psFillArrowArray->nMemLimit == 0)
    7907         133 :         psFillArrowArray->nMemLimit = OGRArrowArrayHelper::GetMemLimit();
    7908       65091 :     const auto nMemLimit = psFillArrowArray->nMemLimit;
    7909             :     const int SQLITE_MAX_FUNCTION_ARG =
    7910       65091 :         sqlite3_limit(psFillArrowArray->hDB, SQLITE_LIMIT_FUNCTION_ARG, -1);
    7911       65093 :     const bool bDateTimeAsString = psFillArrowArray->bDateTimeAsString;
    7912       65214 : begin:
    7913             :     int iFeat;
    7914             :     OGRArrowArrayHelper *psHelper;
    7915             :     {
    7916      130426 :         std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7917       65214 :         iFeat = psFillArrowArray->nCountRows;
    7918       65214 :         psHelper = psFillArrowArray->psHelper.get();
    7919             :     }
    7920       65214 :     int iCol = 0;
    7921       65214 :     const int iFieldStart = sqlite3_value_int(argv[iCol]);
    7922       65214 :     ++iCol;
    7923       65214 :     int iField = std::max(0, iFieldStart);
    7924             : 
    7925             :     GIntBig nFID;
    7926       65214 :     if (iFieldStart < 0)
    7927             :     {
    7928       65208 :         nFID = sqlite3_value_int64(argv[iCol]);
    7929       65208 :         iCol++;
    7930       65208 :         if (psHelper->m_panFIDValues)
    7931             :         {
    7932       65199 :             psHelper->m_panFIDValues[iFeat] = nFID;
    7933             :         }
    7934       65208 :         psFillArrowArray->nCurFID = nFID;
    7935             :     }
    7936             :     else
    7937             :     {
    7938           6 :         nFID = psFillArrowArray->nCurFID;
    7939             :     }
    7940             : 
    7941      130418 :     if (iFieldStart < 0 && !psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
    7942       65204 :         psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    7943             :     {
    7944       65201 :         const int iArrowField = psHelper->m_mapOGRGeomFieldToArrowField[0];
    7945       65201 :         auto psArray = psHelper->m_out_array->children[iArrowField];
    7946       65201 :         size_t nWKBSize = 0;
    7947       65201 :         const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
    7948       65201 :         if (nSqlite3ColType == SQLITE_BLOB)
    7949             :         {
    7950             :             GPkgHeader oHeader;
    7951       64817 :             memset(&oHeader, 0, sizeof(oHeader));
    7952             : 
    7953       64817 :             const GByte *pabyWkb = nullptr;
    7954       64817 :             const int nBlobSize = sqlite3_value_bytes(argv[iCol]);
    7955             :             // coverity[tainted_data_return]
    7956             :             const GByte *pabyBlob =
    7957       64816 :                 static_cast<const GByte *>(sqlite3_value_blob(argv[iCol]));
    7958       64817 :             std::vector<GByte> abyWkb;
    7959       64816 :             if (nBlobSize >= 8 && pabyBlob && pabyBlob[0] == 'G' &&
    7960       64816 :                 pabyBlob[1] == 'P')
    7961             :             {
    7962       64816 :                 if (psFillArrowArray->poLayer->m_bUndoDiscardCoordLSBOnReading)
    7963             :                 {
    7964             :                     OGRGeometry *poGeomPtr =
    7965           1 :                         GPkgGeometryToOGR(pabyBlob, nBlobSize, nullptr);
    7966           1 :                     if (poGeomPtr)
    7967             :                     {
    7968           1 :                         poGeomPtr->roundCoordinates(
    7969           1 :                             psFillArrowArray->poFeatureDefn->GetGeomFieldDefn(0)
    7970             :                                 ->GetCoordinatePrecision());
    7971           1 :                         nWKBSize = poGeomPtr->WkbSize();
    7972           1 :                         abyWkb.resize(nWKBSize);
    7973           1 :                         if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
    7974           1 :                                                    wkbVariantIso) !=
    7975             :                             OGRERR_NONE)
    7976             :                         {
    7977           0 :                             nWKBSize = 0;
    7978             :                         }
    7979             :                         else
    7980             :                         {
    7981           1 :                             pabyWkb = abyWkb.data();
    7982             :                         }
    7983           1 :                         delete poGeomPtr;
    7984             :                     }
    7985             :                 }
    7986             :                 else
    7987             :                 {
    7988             :                     /* Read header */
    7989             :                     OGRErr err =
    7990       64815 :                         GPkgHeaderFromWKB(pabyBlob, nBlobSize, &oHeader);
    7991       64815 :                     if (err == OGRERR_NONE)
    7992             :                     {
    7993             :                         /* WKB pointer */
    7994       64815 :                         pabyWkb = pabyBlob + oHeader.nHeaderLen;
    7995       64815 :                         nWKBSize = nBlobSize - oHeader.nHeaderLen;
    7996             :                     }
    7997       64816 :                 }
    7998             :             }
    7999           0 :             else if (nBlobSize > 0 && pabyBlob)
    8000             :             {
    8001             :                 // Try also spatialite geometry blobs, although that is
    8002             :                 // not really expected...
    8003           0 :                 OGRGeometry *poGeomPtr = nullptr;
    8004           0 :                 if (OGRSQLiteImportSpatiaLiteGeometry(
    8005           0 :                         pabyBlob, nBlobSize, &poGeomPtr) != OGRERR_NONE)
    8006             :                 {
    8007           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    8008             :                              "Unable to read geometry");
    8009             :                 }
    8010             :                 else
    8011             :                 {
    8012           0 :                     nWKBSize = poGeomPtr->WkbSize();
    8013           0 :                     abyWkb.resize(nWKBSize);
    8014           0 :                     if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
    8015           0 :                                                wkbVariantIso) != OGRERR_NONE)
    8016             :                     {
    8017           0 :                         nWKBSize = 0;
    8018             :                     }
    8019             :                     else
    8020             :                     {
    8021           0 :                         pabyWkb = abyWkb.data();
    8022             :                     }
    8023             :                 }
    8024           0 :                 delete poGeomPtr;
    8025             :             }
    8026             : 
    8027       64816 :             if (nWKBSize != 0)
    8028             :             {
    8029             :                 // Deal with spatial filter
    8030       64816 :                 if (psFillArrowArray->poLayerForFilterGeom)
    8031             :                 {
    8032          33 :                     OGREnvelope sEnvelope;
    8033          33 :                     bool bEnvelopeAlreadySet = false;
    8034          33 :                     if (oHeader.bEmpty)
    8035             :                     {
    8036           0 :                         bEnvelopeAlreadySet = true;
    8037             :                     }
    8038          33 :                     else if (oHeader.bExtentHasXY)
    8039             :                     {
    8040          31 :                         bEnvelopeAlreadySet = true;
    8041          31 :                         sEnvelope.MinX = oHeader.MinX;
    8042          31 :                         sEnvelope.MinY = oHeader.MinY;
    8043          31 :                         sEnvelope.MaxX = oHeader.MaxX;
    8044          31 :                         sEnvelope.MaxY = oHeader.MaxY;
    8045             :                     }
    8046             : 
    8047          66 :                     if (!psFillArrowArray->poLayerForFilterGeom
    8048          33 :                              ->FilterWKBGeometry(pabyWkb, nWKBSize,
    8049             :                                                  bEnvelopeAlreadySet,
    8050             :                                                  sEnvelope))
    8051             :                     {
    8052           2 :                         return;
    8053             :                     }
    8054             :                 }
    8055             : 
    8056       64814 :                 if (iFeat > 0)
    8057             :                 {
    8058       64663 :                     auto panOffsets = static_cast<int32_t *>(
    8059       64663 :                         const_cast<void *>(psArray->buffers[1]));
    8060       64663 :                     const uint32_t nCurLength =
    8061       64663 :                         static_cast<uint32_t>(panOffsets[iFeat]);
    8062       64663 :                     if (nWKBSize <= nMemLimit &&
    8063       64663 :                         nWKBSize > nMemLimit - nCurLength)
    8064             :                     {
    8065          52 :                         DealWithTooBigArray(iFeat);
    8066          52 :                         if (psFillArrowArray->bAsynchronousMode)
    8067             :                         {
    8068          47 :                             goto begin;
    8069             :                         }
    8070             :                         else
    8071             :                         {
    8072           5 :                             return;
    8073             :                         }
    8074             :                     }
    8075             :                 }
    8076             : 
    8077       64762 :                 GByte *outPtr = psHelper->GetPtrForStringOrBinary(
    8078             :                     iArrowField, iFeat, nWKBSize);
    8079       64759 :                 if (outPtr == nullptr)
    8080             :                 {
    8081           0 :                     goto error;
    8082             :                 }
    8083       64759 :                 memcpy(outPtr, pabyWkb, nWKBSize);
    8084             :             }
    8085             :             else
    8086             :             {
    8087           0 :                 psHelper->SetEmptyStringOrBinary(psArray, iFeat);
    8088             :             }
    8089             :         }
    8090             : 
    8091       65147 :         if (nWKBSize == 0)
    8092             :         {
    8093         384 :             if (!psHelper->SetNull(iArrowField, iFeat))
    8094             :             {
    8095           0 :                 goto error;
    8096             :             }
    8097             :         }
    8098       65147 :         iCol++;
    8099             :     }
    8100             : 
    8101     1268190 :     for (; iField < psHelper->m_nFieldCount; iField++)
    8102             :     {
    8103     1203120 :         const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
    8104     1203120 :         if (iArrowField < 0)
    8105          33 :             continue;
    8106     1203080 :         if (iCol == SQLITE_MAX_FUNCTION_ARG)
    8107           6 :             break;
    8108             : 
    8109             :         const OGRFieldDefn *poFieldDefn =
    8110     1203080 :             psFillArrowArray->poFeatureDefn->GetFieldDefnUnsafe(iField);
    8111             : 
    8112     1203080 :         auto psArray = psHelper->m_out_array->children[iArrowField];
    8113             : 
    8114     1203080 :         const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
    8115     1203080 :         if (nSqlite3ColType == SQLITE_NULL)
    8116             :         {
    8117         784 :             if (!psHelper->SetNull(iArrowField, iFeat))
    8118             :             {
    8119           0 :                 goto error;
    8120             :             }
    8121         784 :             iCol++;
    8122         784 :             continue;
    8123             :         }
    8124             : 
    8125     1202300 :         switch (poFieldDefn->GetType())
    8126             :         {
    8127        1526 :             case OFTInteger:
    8128             :             {
    8129        1526 :                 const int nVal = sqlite3_value_int(argv[iCol]);
    8130        1526 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    8131             :                 {
    8132         122 :                     if (nVal != 0)
    8133             :                     {
    8134          91 :                         psHelper->SetBoolOn(psArray, iFeat);
    8135             :                     }
    8136             :                 }
    8137        1404 :                 else if (poFieldDefn->GetSubType() == OFSTInt16)
    8138             :                 {
    8139          59 :                     psHelper->SetInt16(psArray, iFeat,
    8140             :                                        static_cast<int16_t>(nVal));
    8141             :                 }
    8142             :                 else
    8143             :                 {
    8144        1345 :                     psHelper->SetInt32(psArray, iFeat, nVal);
    8145             :                 }
    8146        1526 :                 break;
    8147             :             }
    8148             : 
    8149          71 :             case OFTInteger64:
    8150             :             {
    8151          71 :                 psHelper->SetInt64(psArray, iFeat,
    8152          71 :                                    sqlite3_value_int64(argv[iCol]));
    8153          71 :                 break;
    8154             :             }
    8155             : 
    8156         162 :             case OFTReal:
    8157             :             {
    8158         162 :                 const double dfVal = sqlite3_value_double(argv[iCol]);
    8159         162 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    8160             :                 {
    8161          51 :                     psHelper->SetFloat(psArray, iFeat,
    8162             :                                        static_cast<float>(dfVal));
    8163             :                 }
    8164             :                 else
    8165             :                 {
    8166         111 :                     psHelper->SetDouble(psArray, iFeat, dfVal);
    8167             :                 }
    8168         162 :                 break;
    8169             :             }
    8170             : 
    8171         176 :             case OFTBinary:
    8172             :             {
    8173             :                 const uint32_t nBytes =
    8174         176 :                     static_cast<uint32_t>(sqlite3_value_bytes(argv[iCol]));
    8175             :                 // coverity[tainted_data_return]
    8176         176 :                 const void *pabyData = sqlite3_value_blob(argv[iCol]);
    8177         176 :                 if (pabyData != nullptr || nBytes == 0)
    8178             :                 {
    8179         176 :                     if (iFeat > 0)
    8180             :                     {
    8181         119 :                         auto panOffsets = static_cast<int32_t *>(
    8182         119 :                             const_cast<void *>(psArray->buffers[1]));
    8183         119 :                         const uint32_t nCurLength =
    8184         119 :                             static_cast<uint32_t>(panOffsets[iFeat]);
    8185         119 :                         if (nBytes <= nMemLimit &&
    8186         119 :                             nBytes > nMemLimit - nCurLength)
    8187             :                         {
    8188          41 :                             DealWithTooBigArray(iFeat);
    8189          41 :                             if (psFillArrowArray->bAsynchronousMode)
    8190             :                             {
    8191          37 :                                 goto begin;
    8192             :                             }
    8193             :                             else
    8194             :                             {
    8195           4 :                                 return;
    8196             :                             }
    8197             :                         }
    8198             :                     }
    8199             : 
    8200         135 :                     GByte *outPtr = psHelper->GetPtrForStringOrBinary(
    8201             :                         iArrowField, iFeat, nBytes);
    8202         135 :                     if (outPtr == nullptr)
    8203             :                     {
    8204           0 :                         goto error;
    8205             :                     }
    8206         135 :                     if (nBytes)
    8207         135 :                         memcpy(outPtr, pabyData, nBytes);
    8208             :                 }
    8209             :                 else
    8210             :                 {
    8211           0 :                     psHelper->SetEmptyStringOrBinary(psArray, iFeat);
    8212             :                 }
    8213         135 :                 break;
    8214             :             }
    8215             : 
    8216          51 :             case OFTDate:
    8217             :             {
    8218             :                 OGRField ogrField;
    8219             :                 const auto pszTxt = reinterpret_cast<const char *>(
    8220          51 :                     sqlite3_value_text(argv[iCol]));
    8221         102 :                 if (pszTxt != nullptr &&
    8222          51 :                     psFillArrowArray->poLayer->ParseDateField(
    8223             :                         pszTxt, &ogrField, poFieldDefn, nFID))
    8224             :                 {
    8225          51 :                     psHelper->SetDate(psArray, iFeat,
    8226          51 :                                       psFillArrowArray->brokenDown, ogrField);
    8227             :                 }
    8228          51 :                 break;
    8229             :             }
    8230             : 
    8231          65 :             case OFTDateTime:
    8232             :             {
    8233          65 :                 if (!bDateTimeAsString)
    8234             :                 {
    8235             :                     OGRField ogrField;
    8236             :                     const auto pszTxt = reinterpret_cast<const char *>(
    8237          55 :                         sqlite3_value_text(argv[iCol]));
    8238         110 :                     if (pszTxt != nullptr &&
    8239          55 :                         psFillArrowArray->poLayer->ParseDateTimeField(
    8240             :                             pszTxt, &ogrField, poFieldDefn, nFID))
    8241             :                     {
    8242          55 :                         psHelper->SetDateTime(
    8243          55 :                             psArray, iFeat, psFillArrowArray->brokenDown,
    8244          55 :                             psHelper->m_anTZFlags[iField], ogrField);
    8245             :                     }
    8246          55 :                     break;
    8247             :                 }
    8248             :                 else
    8249             :                 {
    8250             :                     [[fallthrough]];
    8251             :                 }
    8252             :             }
    8253             : 
    8254             :             case OFTString:
    8255             :             {
    8256             :                 const auto pszTxt = reinterpret_cast<const char *>(
    8257     1200250 :                     sqlite3_value_text(argv[iCol]));
    8258     1200250 :                 if (pszTxt != nullptr)
    8259             :                 {
    8260     1200250 :                     const size_t nBytes = strlen(pszTxt);
    8261     1200250 :                     if (iFeat > 0)
    8262             :                     {
    8263     1200140 :                         auto panOffsets = static_cast<int32_t *>(
    8264     1200140 :                             const_cast<void *>(psArray->buffers[1]));
    8265     1200140 :                         const uint32_t nCurLength =
    8266     1200140 :                             static_cast<uint32_t>(panOffsets[iFeat]);
    8267     1200140 :                         if (nBytes <= nMemLimit &&
    8268     1200140 :                             nBytes > nMemLimit - nCurLength)
    8269             :                         {
    8270          41 :                             DealWithTooBigArray(iFeat);
    8271          41 :                             if (psFillArrowArray->bAsynchronousMode)
    8272             :                             {
    8273          37 :                                 goto begin;
    8274             :                             }
    8275             :                             else
    8276             :                             {
    8277           4 :                                 return;
    8278             :                             }
    8279             :                         }
    8280             :                     }
    8281             : 
    8282     1200210 :                     GByte *outPtr = psHelper->GetPtrForStringOrBinary(
    8283             :                         iArrowField, iFeat, nBytes);
    8284     1200210 :                     if (outPtr == nullptr)
    8285             :                     {
    8286           0 :                         goto error;
    8287             :                     }
    8288     1200210 :                     if (nBytes)
    8289     1200210 :                         memcpy(outPtr, pszTxt, nBytes);
    8290             :                 }
    8291             :                 else
    8292             :                 {
    8293           0 :                     psHelper->SetEmptyStringOrBinary(psArray, iFeat);
    8294             :                 }
    8295     1200210 :                 break;
    8296             :             }
    8297             : 
    8298           0 :             default:
    8299           0 :                 break;
    8300             :         }
    8301             : 
    8302     1202210 :         iCol++;
    8303             :     }
    8304             : 
    8305       65079 :     if (iField == psHelper->m_nFieldCount)
    8306             :     {
    8307       65073 :         std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    8308       65073 :         psFillArrowArray->nCountRows++;
    8309             :     }
    8310       65079 :     return;
    8311             : 
    8312           0 : error:
    8313           0 :     sqlite3_interrupt(psFillArrowArray->hDB);
    8314             : }
    8315             : 
    8316             : /************************************************************************/
    8317             : /*                   OGR_GPKG_FillArrowArray_Finalize()                 */
    8318             : /************************************************************************/
    8319             : 
    8320         143 : static void OGR_GPKG_FillArrowArray_Finalize(sqlite3_context * /*pContext*/)
    8321             : {
    8322         143 : }
    8323             : 
    8324             : /************************************************************************/
    8325             : /*                    GetNextArrowArrayAsynchronous()                   */
    8326             : /************************************************************************/
    8327             : 
    8328         201 : int OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronous(
    8329             :     struct ArrowArrayStream *stream, struct ArrowArray *out_array)
    8330             : {
    8331         201 :     memset(out_array, 0, sizeof(*out_array));
    8332             : 
    8333         201 :     m_bGetNextArrowArrayCalledSinceResetReading = true;
    8334             : 
    8335         201 :     if (m_poFillArrowArray)
    8336             :     {
    8337         150 :         std::lock_guard oLock(m_poFillArrowArray->oMutex);
    8338         150 :         if (m_poFillArrowArray->bIsFinished)
    8339             :         {
    8340          25 :             return 0;
    8341             :         }
    8342             :     }
    8343             : 
    8344             :     auto psHelper = std::make_unique<OGRArrowArrayHelper>(
    8345         352 :         m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
    8346         176 :     if (out_array->release == nullptr)
    8347             :     {
    8348           0 :         return ENOMEM;
    8349             :     }
    8350             : 
    8351         176 :     if (m_poFillArrowArray == nullptr)
    8352             :     {
    8353             :         // Check that the total number of arguments passed to
    8354             :         // OGR_GPKG_FillArrowArray_INTERNAL() doesn't exceed SQLITE_MAX_FUNCTION_ARG
    8355             :         // If it does, we cannot reliably use GetNextArrowArrayAsynchronous() in
    8356             :         // the situation where the ArrowArray would exceed the nMemLimit.
    8357             :         // So be on the safe side, and rely on the base OGRGeoPackageLayer
    8358             :         // implementation
    8359             :         const int SQLITE_MAX_FUNCTION_ARG =
    8360          51 :             sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
    8361          51 :         int nCountArgs = 1     // field index
    8362             :                          + 1;  // FID column
    8363          99 :         if (!psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
    8364          48 :             psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    8365             :         {
    8366          48 :             ++nCountArgs;
    8367             :         }
    8368         468 :         for (int iField = 0; iField < psHelper->m_nFieldCount; iField++)
    8369             :         {
    8370         419 :             const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
    8371         419 :             if (iArrowField >= 0)
    8372             :             {
    8373         419 :                 if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
    8374             :                 {
    8375           2 :                     psHelper.reset();
    8376           2 :                     if (out_array->release)
    8377           2 :                         out_array->release(out_array);
    8378           2 :                     return OGRGeoPackageLayer::GetNextArrowArray(stream,
    8379           2 :                                                                  out_array);
    8380             :                 }
    8381         417 :                 ++nCountArgs;
    8382             :             }
    8383             :         }
    8384             : 
    8385             :         m_poFillArrowArray =
    8386          49 :             std::make_unique<OGRGPKGTableLayerFillArrowArray>();
    8387          49 :         m_poFillArrowArray->psHelper = std::move(psHelper);
    8388          98 :         m_poFillArrowArray->bDateTimeAsString =
    8389          49 :             m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
    8390             :                                                    false);
    8391          49 :         m_poFillArrowArray->poFeatureDefn = m_poFeatureDefn;
    8392          49 :         m_poFillArrowArray->poLayer = this;
    8393          49 :         m_poFillArrowArray->hDB = m_poDS->GetDB();
    8394          49 :         memset(&m_poFillArrowArray->brokenDown, 0,
    8395             :                sizeof(m_poFillArrowArray->brokenDown));
    8396          49 :         m_poFillArrowArray->bAsynchronousMode = true;
    8397          49 :         if (m_poFilterGeom)
    8398          10 :             m_poFillArrowArray->poLayerForFilterGeom = this;
    8399             : 
    8400             :         try
    8401             :         {
    8402          98 :             m_oThreadNextArrowArray = std::thread(
    8403          98 :                 [this]() { GetNextArrowArrayAsynchronousWorker(); });
    8404             :         }
    8405           0 :         catch (const std::exception &e)
    8406             :         {
    8407           0 :             m_poFillArrowArray.reset();
    8408           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    8409           0 :                      "Cannot start worker thread: %s", e.what());
    8410           0 :             out_array->release(out_array);
    8411           0 :             return ENOMEM;
    8412             :         }
    8413             :     }
    8414             :     else
    8415             :     {
    8416         125 :         std::lock_guard oLock(m_poFillArrowArray->oMutex);
    8417         125 :         if (m_poFillArrowArray->bErrorOccurred)
    8418             :         {
    8419           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    8420           0 :                      m_poFillArrowArray->osErrorMsg.c_str());
    8421           0 :             out_array->release(out_array);
    8422           0 :             return EIO;
    8423             :         }
    8424             : 
    8425             :         // Resume worker thread
    8426         125 :         m_poFillArrowArray->psHelper = std::move(psHelper);
    8427         125 :         m_poFillArrowArray->nCountRows = 0;
    8428         125 :         m_poFillArrowArray->bThreadReady = false;
    8429         125 :         m_poFillArrowArray->bMemoryLimitReached = false;
    8430         125 :         m_poFillArrowArray->oCV.notify_one();
    8431             :     }
    8432             : 
    8433             :     // Wait for GetNextArrowArrayAsynchronousWorker() /
    8434             :     // OGR_GPKG_FillArrowArray_Step() to have generated a result set (or an
    8435             :     // error)
    8436             :     bool bIsFinished;
    8437         348 :     std::string osErrorMsg;
    8438             :     {
    8439         348 :         std::unique_lock<std::mutex> oLock(m_poFillArrowArray->oMutex);
    8440         348 :         while (!m_poFillArrowArray->bThreadReady)
    8441             :         {
    8442         174 :             m_poFillArrowArray->oCV.wait(oLock);
    8443             :         }
    8444         174 :         CPLAssert(m_poFillArrowArray->bErrorOccurred ||
    8445             :                   m_poFillArrowArray->bMemoryLimitReached ||
    8446             :                   m_poFillArrowArray->bIsFinished ||
    8447             :                   m_poFillArrowArray->nCountRows ==
    8448             :                       m_poFillArrowArray->psHelper->m_nMaxBatchSize);
    8449         174 :         bIsFinished = m_poFillArrowArray->bIsFinished;
    8450         174 :         osErrorMsg = m_poFillArrowArray->osErrorMsg;
    8451             :     }
    8452             : 
    8453         174 :     if (m_poFillArrowArray->bErrorOccurred)
    8454             :     {
    8455           1 :         m_oThreadNextArrowArray.join();
    8456           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
    8457           1 :         m_poFillArrowArray->psHelper->ClearArray();
    8458           1 :         return EIO;
    8459             :     }
    8460         173 :     else if (bIsFinished)
    8461             :     {
    8462          46 :         m_oThreadNextArrowArray.join();
    8463             :     }
    8464             : 
    8465         173 :     return 0;
    8466             : }
    8467             : 
    8468             : /************************************************************************/
    8469             : /*                  GetNextArrowArrayAsynchronousWorker()               */
    8470             : /************************************************************************/
    8471             : 
    8472          49 : void OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronousWorker()
    8473             : {
    8474          49 :     sqlite3_create_function(
    8475          49 :         m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
    8476          49 :         SQLITE_UTF8 | SQLITE_DETERMINISTIC, m_poFillArrowArray.get(), nullptr,
    8477             :         OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
    8478             : 
    8479          98 :     std::string osSQL;
    8480          49 :     osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
    8481             : 
    8482        1722 :     const auto AddFields = [this, &osSQL]()
    8483             :     {
    8484          53 :         if (m_pszFidColumn)
    8485             :         {
    8486          50 :             osSQL += "m.\"";
    8487          50 :             osSQL += SQLEscapeName(m_pszFidColumn);
    8488          50 :             osSQL += '"';
    8489             :         }
    8490             :         else
    8491             :         {
    8492           3 :             osSQL += "NULL";
    8493             :         }
    8494             : 
    8495          53 :         if (!m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField
    8496         103 :                  .empty() &&
    8497          50 :             m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    8498             :         {
    8499          50 :             osSQL += ",m.\"";
    8500          50 :             osSQL += SQLEscapeName(GetGeometryColumn());
    8501          50 :             osSQL += '"';
    8502             :         }
    8503         238 :         for (int iField = 0;
    8504         238 :              iField < m_poFillArrowArray->psHelper->m_nFieldCount; iField++)
    8505             :         {
    8506             :             const int iArrowField =
    8507         185 :                 m_poFillArrowArray->psHelper->m_mapOGRFieldToArrowField[iField];
    8508         185 :             if (iArrowField >= 0)
    8509             :             {
    8510             :                 const OGRFieldDefn *poFieldDefn =
    8511         185 :                     m_poFeatureDefn->GetFieldDefnUnsafe(iField);
    8512         185 :                 osSQL += ",m.\"";
    8513         185 :                 osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
    8514         185 :                 osSQL += '"';
    8515             :             }
    8516             :         }
    8517          53 :     };
    8518             : 
    8519          49 :     AddFields();
    8520             : 
    8521          49 :     osSQL += ") FROM ";
    8522          49 :     if (m_iNextShapeId > 0)
    8523             :     {
    8524           4 :         osSQL += "(SELECT ";
    8525           4 :         AddFields();
    8526           4 :         osSQL += " FROM ";
    8527             :     }
    8528          49 :     osSQL += '\"';
    8529          49 :     osSQL += SQLEscapeName(m_pszTableName);
    8530          49 :     osSQL += "\" m";
    8531          49 :     if (!m_soFilter.empty())
    8532             :     {
    8533          35 :         if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
    8534          10 :             HasSpatialIndex())
    8535             :         {
    8536          10 :             OGREnvelope sEnvelope;
    8537             : 
    8538          10 :             m_poFilterGeom->getEnvelope(&sEnvelope);
    8539             : 
    8540          10 :             bool bUseSpatialIndex = true;
    8541          20 :             if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
    8542           1 :                 sEnvelope.MinY <= m_poExtent->MinY &&
    8543          21 :                 sEnvelope.MaxX >= m_poExtent->MaxX &&
    8544           1 :                 sEnvelope.MaxY >= m_poExtent->MaxY)
    8545             :             {
    8546             :                 // Selecting from spatial filter on whole extent can be rather
    8547             :                 // slow. So use function based filtering, just in case the
    8548             :                 // advertized global extent might be wrong. Otherwise we might
    8549             :                 // just discard completely the spatial filter.
    8550           1 :                 bUseSpatialIndex = false;
    8551             :             }
    8552             : 
    8553           9 :             if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
    8554          28 :                 !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
    8555           9 :                 !std::isinf(sEnvelope.MaxY))
    8556             :             {
    8557             :                 osSQL +=
    8558             :                     CPLSPrintf(" JOIN \"%s\" r "
    8559             :                                "ON m.\"%s\" = r.id WHERE "
    8560             :                                "r.maxx >= %.12f AND r.minx <= %.12f AND "
    8561             :                                "r.maxy >= %.12f AND r.miny <= %.12f",
    8562          18 :                                SQLEscapeName(m_osRTreeName).c_str(),
    8563          18 :                                SQLEscapeName(m_osFIDForRTree).c_str(),
    8564           9 :                                sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    8565          27 :                                sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
    8566             :             }
    8567             :         }
    8568             :         else
    8569             :         {
    8570          15 :             osSQL += " WHERE ";
    8571          15 :             osSQL += m_soFilter;
    8572             :         }
    8573             :     }
    8574             : 
    8575          49 :     if (m_iNextShapeId > 0)
    8576             :         osSQL +=
    8577           4 :             CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB ") m", m_iNextShapeId);
    8578             : 
    8579             :     // CPLDebug("GPKG", "%s", osSQL.c_str());
    8580             : 
    8581          49 :     char *pszErrMsg = nullptr;
    8582          98 :     std::string osErrorMsg;
    8583          49 :     if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
    8584          49 :                      &pszErrMsg) != SQLITE_OK)
    8585             :     {
    8586           1 :         osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
    8587             :     }
    8588          49 :     sqlite3_free(pszErrMsg);
    8589             : 
    8590             :     // Delete function
    8591          49 :     sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
    8592             :                             -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
    8593             :                             nullptr, nullptr, nullptr);
    8594             : 
    8595          98 :     std::lock_guard oLock(m_poFillArrowArray->oMutex);
    8596          49 :     m_poFillArrowArray->bIsFinished = true;
    8597          49 :     m_poFillArrowArray->bErrorOccurred = !osErrorMsg.empty();
    8598          49 :     m_poFillArrowArray->osErrorMsg = std::move(osErrorMsg);
    8599             : 
    8600          49 :     if (m_poFillArrowArray->nCountRows >= 0)
    8601             :     {
    8602          47 :         m_poFillArrowArray->psHelper->Shrink(m_poFillArrowArray->nCountRows);
    8603          47 :         if (m_poFillArrowArray->nCountRows == 0)
    8604             :         {
    8605          22 :             m_poFillArrowArray->psHelper->ClearArray();
    8606             :         }
    8607             :     }
    8608          49 :     m_poFillArrowArray->bThreadReady = true;
    8609          49 :     m_poFillArrowArray->oCV.notify_one();
    8610          49 : }
    8611             : 
    8612             : /************************************************************************/
    8613             : /*                      GetNextArrowArray()                             */
    8614             : /************************************************************************/
    8615             : 
    8616         344 : int OGRGeoPackageTableLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
    8617             :                                                struct ArrowArray *out_array)
    8618             : {
    8619         344 :     if (!m_bFeatureDefnCompleted)
    8620           1 :         GetLayerDefn();
    8621         344 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    8622             :     {
    8623           0 :         memset(out_array, 0, sizeof(*out_array));
    8624           0 :         return EIO;
    8625             :     }
    8626             : 
    8627         344 :     if (m_poFilterGeom != nullptr)
    8628             :     {
    8629             :         // Both are exclusive
    8630          18 :         CreateSpatialIndexIfNecessary();
    8631          18 :         if (!RunDeferredSpatialIndexUpdate())
    8632             :         {
    8633           0 :             memset(out_array, 0, sizeof(*out_array));
    8634           0 :             return EIO;
    8635             :         }
    8636             :     }
    8637             : 
    8638         344 :     if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
    8639             :     {
    8640           6 :         return OGRGeoPackageLayer::GetNextArrowArray(stream, out_array);
    8641             :     }
    8642             : 
    8643         881 :     if (m_nIsCompatOfOptimizedGetNextArrowArray == FALSE ||
    8644         361 :         m_pszFidColumn == nullptr || !m_soFilter.empty() ||
    8645         699 :         m_poFillArrowArray ||
    8646         155 :         (!m_bGetNextArrowArrayCalledSinceResetReading && m_iNextShapeId > 0))
    8647             :     {
    8648         184 :         return GetNextArrowArrayAsynchronous(stream, out_array);
    8649             :     }
    8650             : 
    8651             :     // We can use this optimized version only if there is no hole in FID
    8652             :     // numbering. That is min(fid) == 1 and max(fid) == m_nTotalFeatureCount
    8653         154 :     if (m_nIsCompatOfOptimizedGetNextArrowArray < 0)
    8654             :     {
    8655          57 :         m_nIsCompatOfOptimizedGetNextArrowArray = FALSE;
    8656          57 :         const auto nTotalFeatureCount = GetTotalFeatureCount();
    8657          57 :         if (nTotalFeatureCount < 0)
    8658           3 :             return GetNextArrowArrayAsynchronous(stream, out_array);
    8659             :         {
    8660          54 :             char *pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
    8661             :                                            m_pszFidColumn, m_pszTableName);
    8662             :             OGRErr err;
    8663          54 :             const auto nMaxFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
    8664          54 :             sqlite3_free(pszSQL);
    8665          54 :             if (nMaxFID != nTotalFeatureCount)
    8666           2 :                 return GetNextArrowArrayAsynchronous(stream, out_array);
    8667             :         }
    8668             :         {
    8669          52 :             char *pszSQL = sqlite3_mprintf("SELECT MIN(\"%w\") FROM \"%w\"",
    8670             :                                            m_pszFidColumn, m_pszTableName);
    8671             :             OGRErr err;
    8672          52 :             const auto nMinFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
    8673          52 :             sqlite3_free(pszSQL);
    8674          52 :             if (nMinFID != 1)
    8675          12 :                 return GetNextArrowArrayAsynchronous(stream, out_array);
    8676             :         }
    8677          40 :         m_nIsCompatOfOptimizedGetNextArrowArray = TRUE;
    8678             :     }
    8679             : 
    8680         137 :     m_bGetNextArrowArrayCalledSinceResetReading = true;
    8681             : 
    8682             :     // CPLDebug("GPKG", "m_iNextShapeId = " CPL_FRMT_GIB, m_iNextShapeId);
    8683             : 
    8684         274 :     const int nMaxBatchSize = OGRArrowArrayHelper::GetMaxFeaturesInBatch(
    8685         137 :         m_aosArrowArrayStreamOptions);
    8686             : 
    8687             :     // Fetch the answer from a potentially queued asynchronous task
    8688         137 :     if (!m_oQueueArrowArrayPrefetchTasks.empty())
    8689             :     {
    8690          44 :         const size_t nTasks = m_oQueueArrowArrayPrefetchTasks.size();
    8691          44 :         auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
    8692          44 :         m_oQueueArrowArrayPrefetchTasks.pop();
    8693             : 
    8694             :         // Wait for thread to be ready
    8695             :         {
    8696          44 :             std::unique_lock<std::mutex> oLock(task->m_oMutex);
    8697          50 :             while (!task->m_bArrayReady)
    8698             :             {
    8699           6 :                 task->m_oCV.wait(oLock);
    8700             :             }
    8701          44 :             task->m_bArrayReady = false;
    8702             :         }
    8703          44 :         if (!task->m_osErrorMsg.empty())
    8704           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    8705           0 :                      task->m_osErrorMsg.c_str());
    8706             : 
    8707         145 :         const auto stopThread = [&task]()
    8708             :         {
    8709             :             {
    8710          58 :                 std::lock_guard oLock(task->m_oMutex);
    8711          29 :                 task->m_bStop = true;
    8712          29 :                 task->m_oCV.notify_one();
    8713             :             }
    8714          29 :             if (task->m_oThread.joinable())
    8715          29 :                 task->m_oThread.join();
    8716          29 :         };
    8717             : 
    8718          44 :         if (task->m_iStartShapeId != m_iNextShapeId)
    8719             :         {
    8720             :             // Should not normally happen, unless the user messes with
    8721             :             // GetNextFeature()
    8722           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    8723             :                      "Worker thread task has not expected m_iStartShapeId "
    8724             :                      "value. Got " CPL_FRMT_GIB ", expected " CPL_FRMT_GIB,
    8725           0 :                      task->m_iStartShapeId, m_iNextShapeId);
    8726           0 :             if (task->m_psArrowArray->release)
    8727           0 :                 task->m_psArrowArray->release(task->m_psArrowArray.get());
    8728             : 
    8729           0 :             stopThread();
    8730             :         }
    8731          44 :         else if (task->m_psArrowArray->release)
    8732             :         {
    8733          40 :             m_iNextShapeId += task->m_psArrowArray->length;
    8734             : 
    8735             :             // Transfer the task ArrowArray to the client array
    8736          40 :             memcpy(out_array, task->m_psArrowArray.get(),
    8737             :                    sizeof(struct ArrowArray));
    8738          40 :             memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
    8739             : 
    8740          80 :             const bool bMemoryLimitReached = [&task]()
    8741             :             {
    8742          40 :                 std::unique_lock oLock(task->m_oMutex);
    8743          80 :                 return task->m_bMemoryLimitReached;
    8744          40 :             }();
    8745             : 
    8746          40 :             if (bMemoryLimitReached)
    8747             :             {
    8748           2 :                 m_nIsCompatOfOptimizedGetNextArrowArray = false;
    8749           2 :                 stopThread();
    8750           2 :                 CancelAsyncNextArrowArray();
    8751           2 :                 return 0;
    8752             :             }
    8753             :             // Are the records still available for reading beyond the current
    8754             :             // queued tasks ? If so, recycle this task to read them
    8755          38 :             else if (task->m_iStartShapeId +
    8756          38 :                          static_cast<GIntBig>(nTasks) * nMaxBatchSize <=
    8757          38 :                      m_nTotalFeatureCount)
    8758             :             {
    8759          15 :                 task->m_iStartShapeId +=
    8760          15 :                     static_cast<GIntBig>(nTasks) * nMaxBatchSize;
    8761          15 :                 task->m_poLayer->m_iNextShapeId = task->m_iStartShapeId;
    8762             :                 try
    8763             :                 {
    8764             :                     // Wake-up thread with new task
    8765             :                     {
    8766          30 :                         std::lock_guard oLock(task->m_oMutex);
    8767          15 :                         task->m_bFetchRows = true;
    8768          15 :                         task->m_oCV.notify_one();
    8769             :                     }
    8770          15 :                     m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
    8771          15 :                     return 0;
    8772             :                 }
    8773           0 :                 catch (const std::exception &e)
    8774             :                 {
    8775           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    8776           0 :                              "Cannot start worker thread: %s", e.what());
    8777             :                 }
    8778             :             }
    8779             :             else
    8780             :             {
    8781          23 :                 stopThread();
    8782          23 :                 return 0;
    8783             :             }
    8784             :         }
    8785             : 
    8786           4 :         stopThread();
    8787             :     }
    8788             : 
    8789          32 :     const auto GetThreadsAvailable = []()
    8790             :     {
    8791             :         const char *pszMaxThreads =
    8792          32 :             CPLGetConfigOption("OGR_GPKG_NUM_THREADS", nullptr);
    8793          32 :         if (pszMaxThreads == nullptr)
    8794          32 :             return std::min(4, CPLGetNumCPUs());
    8795           0 :         else if (EQUAL(pszMaxThreads, "ALL_CPUS"))
    8796           0 :             return CPLGetNumCPUs();
    8797             :         else
    8798           0 :             return atoi(pszMaxThreads);
    8799             :     };
    8800             : 
    8801             :     // Start asynchronous tasks to prefetch the next ArrowArray
    8802         163 :     if (m_poDS->GetAccess() == GA_ReadOnly &&
    8803          66 :         m_oQueueArrowArrayPrefetchTasks.empty() &&
    8804          66 :         m_iNextShapeId + 2 * static_cast<GIntBig>(nMaxBatchSize) <=
    8805          66 :             m_nTotalFeatureCount &&
    8806         179 :         sqlite3_threadsafe() != 0 && GetThreadsAvailable() >= 2 &&
    8807          16 :         CPLGetUsablePhysicalRAM() > 1024 * 1024 * 1024)
    8808             :     {
    8809          16 :         const int nMaxTasks = static_cast<int>(std::min<GIntBig>(
    8810          16 :             DIV_ROUND_UP(m_nTotalFeatureCount - nMaxBatchSize - m_iNextShapeId,
    8811             :                          nMaxBatchSize),
    8812          32 :             GetThreadsAvailable()));
    8813          16 :         CPLDebug("GPKG", "Using %d threads", nMaxTasks);
    8814          32 :         GDALOpenInfo oOpenInfo(m_poDS->GetDescription(), GA_ReadOnly);
    8815          16 :         oOpenInfo.papszOpenOptions = m_poDS->GetOpenOptions();
    8816          16 :         oOpenInfo.nOpenFlags = GDAL_OF_VECTOR;
    8817          59 :         for (int iTask = 0; iTask < nMaxTasks; ++iTask)
    8818             :         {
    8819          43 :             auto task = std::make_unique<ArrowArrayPrefetchTask>();
    8820          86 :             task->m_iStartShapeId =
    8821          43 :                 m_iNextShapeId +
    8822          43 :                 static_cast<GIntBig>(iTask + 1) * nMaxBatchSize;
    8823          43 :             task->m_poDS = std::make_unique<GDALGeoPackageDataset>();
    8824          43 :             if (!task->m_poDS->Open(&oOpenInfo, m_poDS->m_osFilenameInZip))
    8825             :             {
    8826           0 :                 break;
    8827             :             }
    8828           0 :             auto poOtherLayer = dynamic_cast<OGRGeoPackageTableLayer *>(
    8829          43 :                 task->m_poDS->GetLayerByName(GetName()));
    8830          86 :             if (poOtherLayer == nullptr ||
    8831          43 :                 poOtherLayer->GetLayerDefn()->GetFieldCount() !=
    8832          43 :                     m_poFeatureDefn->GetFieldCount())
    8833             :             {
    8834           0 :                 break;
    8835             :             }
    8836             : 
    8837             :             // Install query logging callback
    8838          43 :             if (m_poDS->pfnQueryLoggerFunc)
    8839             :             {
    8840           0 :                 task->m_poDS->SetQueryLoggerFunc(m_poDS->pfnQueryLoggerFunc,
    8841           0 :                                                  m_poDS->poQueryLoggerArg);
    8842             :             }
    8843             : 
    8844          43 :             task->m_poLayer = poOtherLayer;
    8845          43 :             task->m_psArrowArray = std::make_unique<struct ArrowArray>();
    8846          43 :             memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
    8847             : 
    8848          43 :             poOtherLayer->m_nTotalFeatureCount = m_nTotalFeatureCount;
    8849             :             poOtherLayer->m_aosArrowArrayStreamOptions =
    8850          43 :                 m_aosArrowArrayStreamOptions;
    8851          43 :             OGRLayer *poOtherLayerAsLayer = poOtherLayer;
    8852          43 :             auto poOtherFDefn = poOtherLayerAsLayer->GetLayerDefn();
    8853          86 :             for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
    8854             :             {
    8855          86 :                 poOtherFDefn->GetGeomFieldDefn(i)->SetIgnored(
    8856          43 :                     m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored());
    8857             :             }
    8858         127 :             for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    8859             :             {
    8860         168 :                 poOtherFDefn->GetFieldDefn(i)->SetIgnored(
    8861          84 :                     m_poFeatureDefn->GetFieldDefn(i)->IsIgnored());
    8862             :             }
    8863             : 
    8864          43 :             poOtherLayer->m_iNextShapeId = task->m_iStartShapeId;
    8865             : 
    8866          43 :             auto taskPtr = task.get();
    8867         456 :             auto taskRunner = [taskPtr]()
    8868             :             {
    8869          86 :                 std::unique_lock oLock(taskPtr->m_oMutex);
    8870          15 :                 do
    8871             :                 {
    8872          58 :                     taskPtr->m_bFetchRows = false;
    8873          58 :                     taskPtr->m_poLayer->GetNextArrowArrayInternal(
    8874          58 :                         taskPtr->m_psArrowArray.get(), taskPtr->m_osErrorMsg,
    8875          58 :                         taskPtr->m_bMemoryLimitReached);
    8876          58 :                     taskPtr->m_bArrayReady = true;
    8877          58 :                     taskPtr->m_oCV.notify_one();
    8878          58 :                     if (taskPtr->m_bMemoryLimitReached)
    8879          12 :                         break;
    8880             :                     // cppcheck-suppress knownConditionTrueFalse
    8881             :                     // Coverity apparently is confused by the fact that we
    8882             :                     // use unique_lock here to guard access for m_bStop whereas
    8883             :                     // in other places we use a lock_guard, but there's nothing
    8884             :                     // wrong.
    8885             :                     // coverity[missing_lock:FALSE]
    8886          90 :                     while (!taskPtr->m_bStop && !taskPtr->m_bFetchRows)
    8887             :                     {
    8888          44 :                         taskPtr->m_oCV.wait(oLock);
    8889             :                     }
    8890          46 :                 } while (!taskPtr->m_bStop);
    8891          43 :             };
    8892             : 
    8893          43 :             task->m_bFetchRows = true;
    8894             :             try
    8895             :             {
    8896          43 :                 task->m_oThread = std::thread(taskRunner);
    8897             :             }
    8898           0 :             catch (const std::exception &e)
    8899             :             {
    8900           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    8901           0 :                          "Cannot start worker thread: %s", e.what());
    8902           0 :                 break;
    8903             :             }
    8904          43 :             m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
    8905             :         }
    8906             :     }
    8907             : 
    8908          97 :     std::string osErrorMsg;
    8909          97 :     bool bMemoryLimitReached = false;
    8910             :     int ret =
    8911          97 :         GetNextArrowArrayInternal(out_array, osErrorMsg, bMemoryLimitReached);
    8912          97 :     if (!osErrorMsg.empty())
    8913           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
    8914          97 :     if (bMemoryLimitReached)
    8915             :     {
    8916           1 :         CancelAsyncNextArrowArray();
    8917           1 :         m_nIsCompatOfOptimizedGetNextArrowArray = false;
    8918             :     }
    8919          97 :     return ret;
    8920             : }
    8921             : 
    8922             : /************************************************************************/
    8923             : /*                      GetNextArrowArrayInternal()                     */
    8924             : /************************************************************************/
    8925             : 
    8926         155 : int OGRGeoPackageTableLayer::GetNextArrowArrayInternal(
    8927             :     struct ArrowArray *out_array, std::string &osErrorMsg,
    8928             :     bool &bMemoryLimitReached)
    8929             : {
    8930         155 :     bMemoryLimitReached = false;
    8931         155 :     memset(out_array, 0, sizeof(*out_array));
    8932             : 
    8933         155 :     if (m_iNextShapeId >= m_nTotalFeatureCount)
    8934             :     {
    8935          50 :         return 0;
    8936             :     }
    8937             : 
    8938             :     auto psHelper = std::make_unique<OGRArrowArrayHelper>(
    8939         210 :         m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
    8940         105 :     if (out_array->release == nullptr)
    8941             :     {
    8942           0 :         return ENOMEM;
    8943             :     }
    8944             : 
    8945         210 :     OGRGPKGTableLayerFillArrowArray sFillArrowArray;
    8946         105 :     sFillArrowArray.psHelper = std::move(psHelper);
    8947         105 :     sFillArrowArray.bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
    8948             :         GAS_OPT_DATETIME_AS_STRING, false);
    8949         105 :     sFillArrowArray.poFeatureDefn = m_poFeatureDefn;
    8950         105 :     sFillArrowArray.poLayer = this;
    8951         105 :     sFillArrowArray.hDB = m_poDS->GetDB();
    8952         105 :     memset(&sFillArrowArray.brokenDown, 0, sizeof(sFillArrowArray.brokenDown));
    8953             : 
    8954         105 :     sqlite3_create_function(
    8955         105 :         m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
    8956             :         SQLITE_UTF8 | SQLITE_DETERMINISTIC, &sFillArrowArray, nullptr,
    8957             :         OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
    8958             : 
    8959         210 :     std::string osSQL;
    8960         105 :     osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
    8961         105 :     int nCountArgs = 1;
    8962             : 
    8963         105 :     osSQL += '"';
    8964         105 :     osSQL += SQLEscapeName(m_pszFidColumn);
    8965         105 :     osSQL += '"';
    8966         105 :     ++nCountArgs;
    8967             : 
    8968         208 :     if (!sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
    8969         103 :         sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    8970             :     {
    8971         102 :         osSQL += ',';
    8972         102 :         osSQL += '"';
    8973         102 :         osSQL += SQLEscapeName(GetGeometryColumn());
    8974         102 :         osSQL += '"';
    8975         102 :         ++nCountArgs;
    8976             :     }
    8977             :     const int SQLITE_MAX_FUNCTION_ARG =
    8978         105 :         sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
    8979         740 :     for (int iField = 0; iField < sFillArrowArray.psHelper->m_nFieldCount;
    8980             :          iField++)
    8981             :     {
    8982             :         const int iArrowField =
    8983         635 :             sFillArrowArray.psHelper->m_mapOGRFieldToArrowField[iField];
    8984         635 :         if (iArrowField >= 0)
    8985             :         {
    8986         624 :             if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
    8987             :             {
    8988             :                 // We cannot pass more than SQLITE_MAX_FUNCTION_ARG args
    8989             :                 // to a function... So we have to split in several calls...
    8990           3 :                 osSQL += "), OGR_GPKG_FillArrowArray_INTERNAL(";
    8991           3 :                 osSQL += CPLSPrintf("%d", iField);
    8992           3 :                 nCountArgs = 1;
    8993             :             }
    8994             :             const OGRFieldDefn *poFieldDefn =
    8995         624 :                 m_poFeatureDefn->GetFieldDefnUnsafe(iField);
    8996         624 :             osSQL += ',';
    8997         624 :             osSQL += '"';
    8998         624 :             osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
    8999         624 :             osSQL += '"';
    9000         624 :             ++nCountArgs;
    9001             :         }
    9002             :     }
    9003         105 :     osSQL += ") FROM \"";
    9004         105 :     osSQL += SQLEscapeName(m_pszTableName);
    9005         105 :     osSQL += "\" WHERE \"";
    9006         105 :     osSQL += SQLEscapeName(m_pszFidColumn);
    9007         105 :     osSQL += "\" BETWEEN ";
    9008         105 :     osSQL += std::to_string(m_iNextShapeId + 1);
    9009         105 :     osSQL += " AND ";
    9010         105 :     osSQL += std::to_string(m_iNextShapeId +
    9011         105 :                             sFillArrowArray.psHelper->m_nMaxBatchSize);
    9012             : 
    9013             :     // CPLDebug("GPKG", "%s", osSQL.c_str());
    9014             : 
    9015         105 :     char *pszErrMsg = nullptr;
    9016         105 :     if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
    9017         105 :                      &pszErrMsg) != SQLITE_OK)
    9018             :     {
    9019          13 :         if (!sFillArrowArray.bErrorOccurred &&
    9020          13 :             !sFillArrowArray.bMemoryLimitReached)
    9021             :         {
    9022           0 :             osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
    9023             :         }
    9024             :     }
    9025         105 :     sqlite3_free(pszErrMsg);
    9026             : 
    9027         105 :     bMemoryLimitReached = sFillArrowArray.bMemoryLimitReached;
    9028             : 
    9029             :     // Delete function
    9030         105 :     sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
    9031             :                             -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
    9032             :                             nullptr, nullptr, nullptr);
    9033             : 
    9034         105 :     if (sFillArrowArray.bErrorOccurred)
    9035             :     {
    9036           0 :         sFillArrowArray.psHelper->ClearArray();
    9037           0 :         return ENOMEM;
    9038             :     }
    9039             : 
    9040         105 :     sFillArrowArray.psHelper->Shrink(sFillArrowArray.nCountRows);
    9041         105 :     if (sFillArrowArray.nCountRows == 0)
    9042             :     {
    9043           0 :         sFillArrowArray.psHelper->ClearArray();
    9044             :     }
    9045             : 
    9046         105 :     m_iNextShapeId += sFillArrowArray.nCountRows;
    9047             : 
    9048         105 :     return 0;
    9049             : }
    9050             : 
    9051             : /************************************************************************/
    9052             : /*               OGR_GPKG_GeometryExtent3DAggregate()                   */
    9053             : /************************************************************************/
    9054             : 
    9055             : namespace
    9056             : {
    9057             : struct GeometryExtent3DAggregateContext
    9058             : {
    9059             :     sqlite3 *m_hDB = nullptr;
    9060             :     OGREnvelope3D m_oExtent3D;
    9061             : 
    9062          18 :     explicit GeometryExtent3DAggregateContext(sqlite3 *hDB)
    9063          18 :         : m_hDB(hDB), m_oExtent3D()
    9064             :     {
    9065          18 :     }
    9066             : 
    9067             :     GeometryExtent3DAggregateContext(const GeometryExtent3DAggregateContext &) =
    9068             :         delete;
    9069             :     GeometryExtent3DAggregateContext &
    9070             :     operator=(const GeometryExtent3DAggregateContext &) = delete;
    9071             : };
    9072             : 
    9073             : }  // namespace
    9074             : 
    9075          26 : static void OGR_GPKG_GeometryExtent3DAggregate_Step(sqlite3_context *pContext,
    9076             :                                                     int /*argc*/,
    9077             :                                                     sqlite3_value **argv)
    9078             : {
    9079             :     const GByte *pabyBLOB =
    9080          26 :         reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
    9081             : 
    9082             :     auto poContext = static_cast<GeometryExtent3DAggregateContext *>(
    9083          26 :         sqlite3_user_data(pContext));
    9084             : 
    9085          26 :     if (pabyBLOB != nullptr)
    9086             :     {
    9087             :         GPkgHeader sHeader;
    9088          26 :         if (OGRGeoPackageGetHeader(pContext, 0, argv, &sHeader, true, true))
    9089             :         {
    9090          26 :             OGREnvelope3D extent3D;
    9091          26 :             extent3D.MinX = sHeader.MinX;
    9092          26 :             extent3D.MaxX = sHeader.MaxX;
    9093          26 :             extent3D.MinY = sHeader.MinY;
    9094          26 :             extent3D.MaxY = sHeader.MaxY;
    9095          26 :             extent3D.MinZ = sHeader.MinZ;
    9096          26 :             extent3D.MaxZ = sHeader.MaxZ;
    9097          26 :             poContext->m_oExtent3D.Merge(extent3D);
    9098             :         }
    9099           0 :         else if (!sHeader.bEmpty)
    9100             :         {
    9101             :             // Try also spatialite geometry blobs
    9102           0 :             const int nBLOBLen = sqlite3_value_bytes(argv[0]);
    9103           0 :             OGRGeometry *poGeom = nullptr;
    9104           0 :             if (OGRSQLiteImportSpatiaLiteGeometry(pabyBLOB, nBLOBLen,
    9105           0 :                                                   &poGeom) == OGRERR_NONE &&
    9106           0 :                 poGeom && !poGeom->IsEmpty())
    9107             :             {
    9108           0 :                 OGREnvelope3D extent3D;
    9109           0 :                 poGeom->getEnvelope(&extent3D);
    9110           0 :                 poContext->m_oExtent3D.Merge(extent3D);
    9111             :             }
    9112           0 :             delete poGeom;
    9113             :         }
    9114             :     }
    9115          26 : }
    9116             : 
    9117          18 : static void OGR_GPKG_GeometryExtent3DAggregate_Finalize(sqlite3_context *)
    9118             : {
    9119          18 : }
    9120             : 
    9121             : /************************************************************************/
    9122             : /*                            IGetExtent3D                              */
    9123             : /************************************************************************/
    9124          20 : OGRErr OGRGeoPackageTableLayer::IGetExtent3D(int iGeomField,
    9125             :                                              OGREnvelope3D *psExtent3D,
    9126             :                                              bool bForce)
    9127             : {
    9128             : 
    9129          20 :     const OGRFeatureDefn *poDefn = GetLayerDefn();
    9130             : 
    9131             :     /* -------------------------------------------------------------------- */
    9132             :     /*      Deferred actions, reset state.                                   */
    9133             :     /* -------------------------------------------------------------------- */
    9134          20 :     RunDeferredCreationIfNecessary();
    9135          20 :     if (!RunDeferredSpatialIndexUpdate())
    9136             :     {
    9137           0 :         return OGRERR_FAILURE;
    9138             :     }
    9139             : 
    9140          20 :     if (m_nZFlag == 0 && m_soFilter.empty())
    9141             :     {
    9142             :         // If the layer doesn't contain any 3D geometry and no filter is set,
    9143             :         // we can fallback to the fast 2D GetExtent()
    9144           2 :         const OGRErr retVal{GetExtent(iGeomField, psExtent3D, bForce)};
    9145           2 :         psExtent3D->MinZ = std::numeric_limits<double>::infinity();
    9146           2 :         psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
    9147           2 :         return retVal;
    9148             :     }
    9149             :     else
    9150             :     {
    9151          18 :         *psExtent3D = OGREnvelope3D();
    9152             :     }
    9153             : 
    9154             :     // For internal use only
    9155             : 
    9156          18 :     GeometryExtent3DAggregateContext sContext(m_poDS->hDB);
    9157             : 
    9158          36 :     CPLString osFuncName;
    9159             :     osFuncName.Printf("OGR_GPKG_GeometryExtent3DAggregate_INTERNAL_%p",
    9160          18 :                       &sContext);
    9161             : 
    9162          18 :     sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
    9163             :                             &sContext, nullptr,
    9164             :                             OGR_GPKG_GeometryExtent3DAggregate_Step,
    9165             :                             OGR_GPKG_GeometryExtent3DAggregate_Finalize);
    9166             : 
    9167          36 :     char *pszSQL = sqlite3_mprintf(
    9168             :         "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
    9169          18 :         poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
    9170          45 :         m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
    9171          18 :     char *pszErrMsg = nullptr;
    9172             :     const int rc =
    9173          18 :         sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
    9174             : 
    9175             :     // Delete function
    9176          18 :     sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
    9177             :                             nullptr, nullptr, nullptr, nullptr);
    9178             : 
    9179          18 :     if (rc != SQLITE_OK)
    9180             :     {
    9181           0 :         if (rc != SQLITE_INTERRUPT)
    9182             :         {
    9183           0 :             CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
    9184             :                      pszSQL, pszErrMsg);
    9185             :         }
    9186           0 :         sqlite3_free(pszErrMsg);
    9187           0 :         sqlite3_free(pszSQL);
    9188           0 :         return OGRERR_FAILURE;
    9189             :     }
    9190          18 :     sqlite3_free(pszErrMsg);
    9191          18 :     sqlite3_free(pszSQL);
    9192             : 
    9193          18 :     *psExtent3D = sContext.m_oExtent3D;
    9194             : 
    9195          18 :     return OGRERR_NONE;
    9196             : }
    9197             : 
    9198             : /************************************************************************/
    9199             : /*                           Truncate()                                 */
    9200             : /************************************************************************/
    9201             : 
    9202             : /** Implements "DELETE FROM {table_name}" in an optimized way.
    9203             :  *
    9204             :  * Disable triggers if we detect that the only triggers on the table are ones
    9205             :  * under our control (i.e. the ones for the gpkg_ogr_contents table and the
    9206             :  * ones updating the RTree)
    9207             :  * And even if we cannot disable triggers, truncate the RTree before the main
    9208             :  * table, as this dramatically speeds up truncating the main table.
    9209             :  */
    9210           8 : OGRErr OGRGeoPackageTableLayer::Truncate()
    9211             : {
    9212           8 :     if (!m_poDS->GetUpdate())
    9213             :     {
    9214           2 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    9215             :                  "Truncate");
    9216           2 :         return OGRERR_FAILURE;
    9217             :     }
    9218             : 
    9219           6 :     ResetReading();
    9220           6 :     SyncToDisk();
    9221             : 
    9222           6 :     bool bOK = (m_poDS->SoftStartTransaction() == OGRERR_NONE);
    9223             : 
    9224             :     struct ReenableTriggers
    9225             :     {
    9226             :         sqlite3 *m_hDB = nullptr;
    9227             : 
    9228           4 :         explicit ReenableTriggers(sqlite3 *hDB) : m_hDB(hDB)
    9229             :         {
    9230           4 :         }
    9231             : 
    9232           4 :         ~ReenableTriggers()
    9233           4 :         {
    9234           4 :             sqlite3_db_config(m_hDB, SQLITE_DBCONFIG_ENABLE_TRIGGER, 1,
    9235             :                               nullptr);
    9236           4 :         }
    9237             :         CPL_DISALLOW_COPY_ASSIGN(ReenableTriggers)
    9238             :     };
    9239             : 
    9240             :     // to keep in top level scope!
    9241           0 :     std::unique_ptr<ReenableTriggers> reenableTriggers;
    9242             : 
    9243             :     // Temporarily disable triggers for greater speed if we detect that the
    9244             :     // only triggers on the table are the RTree ones and the ones for the
    9245             :     // gpkg_ogr_contents table
    9246           6 :     if (bOK && m_bIsTable)
    9247             :     {
    9248           6 :         char *pszSQL = sqlite3_mprintf(
    9249             :             "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' "
    9250             :             "AND tbl_name = '%q' "
    9251             :             "AND name NOT IN ('trigger_insert_feature_count_%q',"
    9252             :             "'trigger_delete_feature_count_%q') "
    9253             :             "AND name NOT LIKE 'rtree_%q_%%'",
    9254             :             m_pszTableName, m_pszTableName, m_pszTableName, m_pszTableName);
    9255           6 :         OGRErr eErr = OGRERR_NONE;
    9256          10 :         if (SQLGetInteger(m_poDS->GetDB(), pszSQL, &eErr) == 0 &&
    9257           4 :             eErr == OGRERR_NONE)
    9258             :         {
    9259           4 :             int nEnableTriggerOldVal = -1;
    9260           4 :             sqlite3_db_config(m_poDS->GetDB(), SQLITE_DBCONFIG_ENABLE_TRIGGER,
    9261             :                               -1, &nEnableTriggerOldVal);
    9262           4 :             if (nEnableTriggerOldVal == 1)
    9263             :             {
    9264           4 :                 int nNewVal = -1;
    9265           4 :                 sqlite3_db_config(m_poDS->GetDB(),
    9266             :                                   SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &nNewVal);
    9267           4 :                 if (nNewVal == 0)
    9268             :                 {
    9269           4 :                     CPLDebugOnly("GPKG",
    9270             :                                  "Disabling triggers during truncation of %s",
    9271             :                                  m_pszTableName);
    9272             :                     reenableTriggers =
    9273           4 :                         std::make_unique<ReenableTriggers>(m_poDS->GetDB());
    9274           4 :                     CPL_IGNORE_RET_VAL(reenableTriggers);  // to please cppcheck
    9275             :                 }
    9276             :             }
    9277             :         }
    9278           6 :         sqlite3_free(pszSQL);
    9279             :     }
    9280             : 
    9281           6 :     char *pszErrMsg = nullptr;
    9282           6 :     if (bOK && m_bIsTable && HasSpatialIndex())
    9283             :     {
    9284             :         // Manually clean the 3 tables that are used by the RTree:
    9285             :         // - rtree_{tablename}_{geom}_node: all rows, but nodeno = 1 for which
    9286             :         //   we reset the 'data' field to a zero blob of the same size
    9287             :         // - rtree_{tablename}_{geom}_parent: all rows
    9288             :         // - rtree_{tablename}_{geom}_rowid: all rows
    9289             : 
    9290           2 :         const char *pszT = m_pszTableName;
    9291           2 :         const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    9292             : 
    9293           2 :         m_osRTreeName = "rtree_";
    9294           2 :         m_osRTreeName += pszT;
    9295           2 :         m_osRTreeName += "_";
    9296           2 :         m_osRTreeName += pszC;
    9297             : 
    9298             :         {
    9299             :             char *pszSQL =
    9300           2 :                 sqlite3_mprintf("DELETE FROM \"%w_node\" WHERE nodeno > 1;"
    9301             :                                 "DELETE FROM \"%w_parent\"; "
    9302             :                                 "DELETE FROM \"%w_rowid\"",
    9303             :                                 m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    9304             :                                 m_osRTreeName.c_str());
    9305           2 :             bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
    9306             :                                &pszErrMsg) == SQLITE_OK;
    9307           2 :             sqlite3_free(pszSQL);
    9308             :         }
    9309             : 
    9310           2 :         if (bOK)
    9311             :         {
    9312           2 :             char *pszSQL = sqlite3_mprintf(
    9313             :                 "SELECT length(data) FROM \"%w_node\" WHERE nodeno = 1",
    9314             :                 m_osRTreeName.c_str());
    9315             :             const int nBlobSize =
    9316           2 :                 SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr);
    9317           2 :             sqlite3_free(pszSQL);
    9318             : 
    9319           2 :             pszSQL = sqlite3_mprintf(
    9320             :                 "UPDATE \"%w_node\" SET data = zeroblob(%d) WHERE nodeno = 1",
    9321             :                 m_osRTreeName.c_str(), nBlobSize);
    9322           2 :             bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
    9323             :                                &pszErrMsg) == SQLITE_OK;
    9324           2 :             sqlite3_free(pszSQL);
    9325             :         }
    9326             :     }
    9327             : 
    9328           6 :     if (bOK)
    9329             :     {
    9330             :         // Truncate main table
    9331           6 :         char *pszSQL = sqlite3_mprintf("DELETE FROM \"%w\"", m_pszTableName);
    9332           6 :         bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
    9333             :               SQLITE_OK;
    9334           6 :         sqlite3_free(pszSQL);
    9335             :     }
    9336             : 
    9337             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    9338             :     // Reset feature count
    9339           6 :     if (bOK && m_poDS->m_bHasGPKGOGRContents)
    9340             :     {
    9341             :         char *pszSQL =
    9342           5 :             sqlite3_mprintf("UPDATE gpkg_ogr_contents SET feature_count = 0 "
    9343             :                             "WHERE lower(table_name) = lower('%q')",
    9344             :                             m_pszTableName);
    9345           5 :         bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
    9346             :               SQLITE_OK;
    9347           5 :         sqlite3_free(pszSQL);
    9348             :     }
    9349             : 
    9350           6 :     if (bOK)
    9351             :     {
    9352           5 :         m_nTotalFeatureCount = 0;
    9353             :     }
    9354             : #endif
    9355             : 
    9356           6 :     if (bOK)
    9357             :     {
    9358           5 :         m_poDS->SoftCommitTransaction();
    9359             :     }
    9360             :     else
    9361             :     {
    9362           1 :         m_poDS->SoftRollbackTransaction();
    9363             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    9364           1 :         DisableFeatureCount();
    9365             : #endif
    9366           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Truncate(%s) failed: %s",
    9367           1 :                  m_pszTableName, pszErrMsg ? pszErrMsg : "(unknown reason)");
    9368             :     }
    9369           6 :     sqlite3_free(pszErrMsg);
    9370             : 
    9371           6 :     return bOK ? OGRERR_NONE : OGRERR_FAILURE;
    9372             : }

Generated by: LCOV version 1.14