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

Generated by: LCOV version 1.14