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

Generated by: LCOV version 1.14