LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagetablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3805 4205 90.5 %
Date: 2025-06-19 12:30:01 Functions: 132 132 100.0 %

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

Generated by: LCOV version 1.14