LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagetablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3807 4203 90.6 %
Date: 2026-04-20 19:56:30 Functions: 134 134 100.0 %

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

Generated by: LCOV version 1.14