LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagetablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3841 4236 90.7 %
Date: 2026-10-22 01:45:52 Functions: 135 135 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 "ogrlibjsonutils.h"
      18             : #include "ogrsqliteutility.h"
      19             : #include "cpl_md5.h"
      20             : #include "cpl_multiproc.h"  // CPLSleep()
      21             : #include "cpl_time.h"
      22             : #include "ogr_p.h"
      23             : #include "sqlite_rtree_bulk_load/wrapper.h"
      24             : #include "gdal_priv_templates.hpp"
      25             : 
      26             : #include <algorithm>
      27             : #include <cassert>
      28             : #include <cerrno>
      29             : #include <cinttypes>
      30             : #include <climits>
      31             : #include <cmath>
      32             : #include <limits>
      33             : #include <mutex>
      34             : 
      35             : #undef SQLITE_STATIC
      36             : #define SQLITE_STATIC static_cast<sqlite3_destructor_type>(nullptr)
      37             : #undef SQLITE_TRANSIENT
      38             : #define SQLITE_TRANSIENT reinterpret_cast<sqlite3_destructor_type>(-1)
      39             : 
      40             : static const char UNSUPPORTED_OP_READ_ONLY[] =
      41             :     "%s : unsupported operation on a read-only datasource.";
      42             : 
      43             : //----------------------------------------------------------------------
      44             : // SaveExtent()
      45             : //
      46             : // Write the current contents of the layer envelope down to the
      47             : // gpkg_contents metadata table.
      48             : //
      49        5956 : OGRErr OGRGeoPackageTableLayer::SaveExtent()
      50             : {
      51        5956 :     if (!m_poDS->GetUpdate() || !m_bExtentChanged || !m_poExtent)
      52        5300 :         return OGRERR_NONE;
      53             : 
      54         656 :     sqlite3 *poDb = m_poDS->GetDB();
      55             : 
      56         656 :     if (!poDb)
      57           0 :         return OGRERR_FAILURE;
      58             : 
      59             :     char *pszSQL =
      60         656 :         sqlite3_mprintf("UPDATE gpkg_contents SET "
      61             :                         "min_x = %.17g, min_y = %.17g, "
      62             :                         "max_x = %.17g, max_y = %.17g "
      63             :                         "WHERE lower(table_name) = lower('%q') AND "
      64             :                         "Lower(data_type) = 'features'",
      65         656 :                         m_poExtent->MinX, m_poExtent->MinY, m_poExtent->MaxX,
      66         656 :                         m_poExtent->MaxY, m_pszTableName);
      67             : 
      68         656 :     OGRErr err = SQLCommand(poDb, pszSQL);
      69         656 :     sqlite3_free(pszSQL);
      70         656 :     m_bExtentChanged = false;
      71             : 
      72         656 :     return err;
      73             : }
      74             : 
      75             : //----------------------------------------------------------------------
      76             : // SaveTimestamp()
      77             : //
      78             : // Update the last_change column of the gpkg_contents metadata table.
      79             : //
      80        5951 : OGRErr OGRGeoPackageTableLayer::SaveTimestamp()
      81             : {
      82        5951 :     if (!m_poDS->GetUpdate() || !m_bContentChanged)
      83        5072 :         return OGRERR_NONE;
      84             : 
      85         879 :     m_bContentChanged = false;
      86             : 
      87         879 :     OGRErr err = m_poDS->UpdateGpkgContentsLastChange(m_pszTableName);
      88             : 
      89             : #ifdef ENABLE_GPKG_OGR_CONTENTS
      90         879 :     if (m_bIsTable && err == OGRERR_NONE && m_poDS->m_bHasGPKGOGRContents &&
      91         870 :         !m_bOGRFeatureCountTriggersEnabled && m_nTotalFeatureCount >= 0)
      92             :     {
      93        1636 :         CPLString osFeatureCount;
      94         818 :         osFeatureCount.Printf(CPL_FRMT_GIB, m_nTotalFeatureCount);
      95         818 :         char *pszSQL = sqlite3_mprintf("UPDATE gpkg_ogr_contents SET "
      96             :                                        "feature_count = %s "
      97             :                                        "WHERE lower(table_name) = lower('%q')",
      98             :                                        osFeatureCount.c_str(), m_pszTableName);
      99         818 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
     100         818 :         sqlite3_free(pszSQL);
     101             :     }
     102             : #endif
     103             : 
     104         879 :     return err;
     105             : }
     106             : 
     107             : //----------------------------------------------------------------------
     108             : // UpdateExtent()
     109             : //
     110             : // Expand the layer envelope if necessary to reflect the bounds
     111             : // of new features being added to the layer.
     112             : //
     113      262971 : OGRErr OGRGeoPackageTableLayer::UpdateExtent(const OGREnvelope *poExtent)
     114             : {
     115      262971 :     if (!m_poExtent)
     116             :     {
     117         617 :         m_poExtent = std::make_unique<OGREnvelope>(*poExtent);
     118             :     }
     119      262971 :     m_poExtent->Merge(*poExtent);
     120      262971 :     m_bExtentChanged = true;
     121      262971 :     return OGRERR_NONE;
     122             : }
     123             : 
     124             : //----------------------------------------------------------------------
     125             : // BuildColumns()
     126             : //
     127             : // Save a list of columns (fid, geometry, attributes) suitable
     128             : // for use in a SELECT query that retrieves all fields.
     129             : //
     130       26887 : OGRErr OGRGeoPackageTableLayer::BuildColumns()
     131             : {
     132       26887 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
     133           0 :         return OGRERR_FAILURE;
     134             : 
     135       26887 :     if (!m_bFeatureDefnCompleted)
     136          27 :         GetLayerDefn();
     137             : 
     138       26887 :     m_anFieldOrdinals.resize(m_poFeatureDefn->GetFieldCount());
     139       26887 :     int iCurCol = 0;
     140             : 
     141             :     /* Always start with a primary key */
     142       26887 :     CPLString soColumns;
     143       26887 :     if (m_bIsTable || m_pszFidColumn != nullptr)
     144             :     {
     145       26873 :         soColumns += "m.";
     146       26873 :         soColumns += m_pszFidColumn
     147       53746 :                          ? "\"" + SQLEscapeName(m_pszFidColumn) + "\""
     148       26873 :                          : "_rowid_";
     149       26873 :         m_iFIDCol = iCurCol;
     150       26873 :         iCurCol++;
     151             :     }
     152             : 
     153             :     /* Add a geometry column if there is one (just one) */
     154       26887 :     if (m_poFeatureDefn->GetGeomFieldCount())
     155             :     {
     156       26663 :         const auto poFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
     157       26663 :         if (poFieldDefn->IsIgnored())
     158             :         {
     159          10 :             m_iGeomCol = -1;
     160             :         }
     161             :         else
     162             :         {
     163       26653 :             if (!soColumns.empty())
     164       26642 :                 soColumns += ", ";
     165       26653 :             soColumns += "m.\"";
     166       26653 :             soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
     167       26653 :             soColumns += "\"";
     168       26653 :             m_iGeomCol = iCurCol;
     169       26653 :             iCurCol++;
     170             :         }
     171             :     }
     172             : 
     173             :     /* Add all the attribute columns */
     174       38930 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     175             :     {
     176       12043 :         const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
     177       12043 :         if (poFieldDefn->IsIgnored())
     178             :         {
     179          40 :             m_anFieldOrdinals[i] = -1;
     180             :         }
     181             :         else
     182             :         {
     183       12003 :             if (!soColumns.empty())
     184       12002 :                 soColumns += ", ";
     185       12003 :             soColumns += "m.\"";
     186       12003 :             soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
     187       12003 :             soColumns += "\"";
     188       12003 :             m_anFieldOrdinals[i] = iCurCol;
     189       12003 :             iCurCol++;
     190             :         }
     191             :     }
     192             : 
     193       26887 :     if (soColumns.empty())
     194             :     {
     195             :         // Can happen if ignoring all fields on a view...
     196           2 :         soColumns = "NULL";
     197             :     }
     198       26887 :     m_soColumns = std::move(soColumns);
     199       26887 :     return OGRERR_NONE;
     200             : }
     201             : 
     202             : //----------------------------------------------------------------------
     203             : // IsGeomFieldSet()
     204             : //
     205             : // Utility method to determine if there is a non-Null geometry
     206             : // in an OGRGeometry.
     207             : //
     208      268817 : bool OGRGeoPackageTableLayer::IsGeomFieldSet(OGRFeature *poFeature)
     209             : {
     210      534251 :     return poFeature->GetDefnRef()->GetGeomFieldCount() &&
     211      534251 :            poFeature->GetGeomFieldRef(0);
     212             : }
     213             : 
     214      268840 : OGRErr OGRGeoPackageTableLayer::FeatureBindParameters(
     215             :     OGRFeature *poFeature, sqlite3_stmt *poStmt, int *pnColCount, bool bAddFID,
     216             :     bool bBindUnsetFields, int nUpdatedFieldsCount,
     217             :     const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
     218             :     const int * /*panUpdatedGeomFieldsIdx*/)
     219             : {
     220      268840 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     221             : 
     222      268840 :     int nColCount = 1;
     223      268840 :     if (bAddFID)
     224             :     {
     225       67529 :         int err = sqlite3_bind_int64(poStmt, nColCount++, poFeature->GetFID());
     226       67529 :         if (err != SQLITE_OK)
     227             :         {
     228           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     229             :                      "sqlite3_bind_int64() failed");
     230           0 :             return OGRERR_FAILURE;
     231             :         }
     232             :     }
     233             : 
     234             :     // Bind data values to the statement, here bind the blob for geometry.
     235             :     // We bind only if there's a geometry column (poFeatureDefn->GetGeomFieldCount() > 0)
     236             :     // and if we are:
     237             :     // - either in CreateFeature/SetFeature mode: nUpdatedGeomFieldsCount < 0
     238             :     // - or in UpdateFeature mode with nUpdatedGeomFieldsCount == 1, which
     239             :     //   implicitly involves that panUpdatedGeomFieldsIdx[0] == 0, so we don't
     240             :     //   need to test this condition.
     241      537672 :     if ((nUpdatedGeomFieldsCount < 0 || nUpdatedGeomFieldsCount == 1) &&
     242      268832 :         poFeatureDefn->GetGeomFieldCount())
     243             :     {
     244             :         // Non-NULL geometry.
     245      265447 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
     246      265447 :         if (poGeom)
     247             :         {
     248      264018 :             size_t szWkb = 0;
     249      528036 :             GByte *pabyWkb = GPkgGeometryFromOGR(poGeom, m_iSrs,
     250      264018 :                                                  &m_sBinaryPrecision, &szWkb);
     251      264018 :             if (!pabyWkb)
     252           0 :                 return OGRERR_FAILURE;
     253      264018 :             int err = sqlite3_bind_blob(poStmt, nColCount++, pabyWkb,
     254             :                                         static_cast<int>(szWkb), CPLFree);
     255      264018 :             if (err != SQLITE_OK)
     256             :             {
     257           0 :                 if (err == SQLITE_TOOBIG)
     258             :                 {
     259           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     260             :                              "sqlite3_bind_blob() failed: too big");
     261             :                 }
     262             :                 else
     263             :                 {
     264           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     265             :                              "sqlite3_bind_blob() failed");
     266             :                 }
     267           0 :                 return OGRERR_FAILURE;
     268             :             }
     269      264018 :             CreateGeometryExtensionIfNecessary(poGeom);
     270             :         }
     271             :         /* NULL geometry */
     272             :         else
     273             :         {
     274        1429 :             int err = sqlite3_bind_null(poStmt, nColCount++);
     275        1429 :             if (err != SQLITE_OK)
     276             :             {
     277           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     278             :                          "sqlite3_bind_null() failed");
     279           0 :                 return OGRERR_FAILURE;
     280             :             }
     281             :         }
     282             :     }
     283             : 
     284             :     /* Bind the attributes using appropriate SQLite data types */
     285      268840 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
     286             : 
     287      268840 :     size_t nInsertionBufferPos = 0;
     288      268840 :     if (m_osInsertionBuffer.empty())
     289       29502 :         m_osInsertionBuffer.resize(OGR_SIZEOF_ISO8601_DATETIME_BUFFER *
     290             :                                    nFieldCount);
     291             : 
     292     4737050 :     for (int idx = 0;
     293     4737050 :          idx < (nUpdatedFieldsCount < 0 ? nFieldCount : nUpdatedFieldsCount);
     294             :          idx++)
     295             :     {
     296     4468210 :         const int iField =
     297     4468210 :             nUpdatedFieldsCount < 0 ? idx : panUpdatedFieldsIdx[idx];
     298     4468210 :         assert(iField >= 0);
     299     4468210 :         const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(iField);
     300     4468210 :         if (iField == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
     301        1287 :             continue;
     302     4468180 :         if (!poFeature->IsFieldSetUnsafe(iField))
     303             :         {
     304        1258 :             if (bBindUnsetFields)
     305             :             {
     306        1244 :                 int err = sqlite3_bind_null(poStmt, nColCount++);
     307        1244 :                 if (err != SQLITE_OK)
     308             :                 {
     309           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     310             :                              "sqlite3_bind_null() failed");
     311           0 :                     return OGRERR_FAILURE;
     312             :                 }
     313             :             }
     314        1258 :             continue;
     315             :         }
     316             : 
     317             :         const OGRFieldDefn *poFieldDefn =
     318     4466930 :             poFeatureDefn->GetFieldDefnUnsafe(iField);
     319     4466930 :         int err = SQLITE_OK;
     320     4466930 :         bool bBound = false;
     321     4466930 :         char *pszValToFree = nullptr;
     322             : 
     323     4466930 :         if (!poFeature->IsFieldNullUnsafe(iField))
     324             :         {
     325     4466890 :             const auto eType = poFieldDefn->GetType();
     326     4466890 :             switch (eType)
     327             :             {
     328       10532 :                 case OFTInteger:
     329             :                 {
     330       10532 :                     err = sqlite3_bind_int(
     331             :                         poStmt, nColCount++,
     332             :                         poFeature->GetFieldAsIntegerUnsafe(iField));
     333       10532 :                     break;
     334             :                 }
     335        6069 :                 case OFTInteger64:
     336             :                 {
     337        6069 :                     err = sqlite3_bind_int64(
     338             :                         poStmt, nColCount++,
     339             :                         poFeature->GetFieldAsInteger64Unsafe(iField));
     340        6069 :                     break;
     341             :                 }
     342       20382 :                 case OFTReal:
     343             :                 {
     344       20382 :                     err = sqlite3_bind_double(
     345             :                         poStmt, nColCount++,
     346             :                         poFeature->GetFieldAsDoubleUnsafe(iField));
     347       20382 :                     break;
     348             :                 }
     349        7311 :                 case OFTBinary:
     350             :                 {
     351        7311 :                     int szBlob = 0;
     352             :                     GByte *pabyBlob =
     353        7311 :                         poFeature->GetFieldAsBinary(iField, &szBlob);
     354        7311 :                     err = sqlite3_bind_blob(poStmt, nColCount++, pabyBlob,
     355             :                                             szBlob, SQLITE_STATIC);
     356        7311 :                     break;
     357             :                 }
     358     4422600 :                 default:
     359             :                 {
     360     4422600 :                     const char *pszVal = "";
     361     4422600 :                     CPL_IGNORE_RET_VAL(pszVal);  // Make CSA happy
     362     4422600 :                     int nValLengthBytes = -1;
     363     4422600 :                     sqlite3_destructor_type destructorType = SQLITE_TRANSIENT;
     364     4422600 :                     if (eType == OFTDate)
     365             :                     {
     366        5141 :                         destructorType = SQLITE_STATIC;
     367             :                         const auto psFieldRaw =
     368        5141 :                             poFeature->GetRawFieldRef(iField);
     369             :                         char *pszValEdit =
     370        5141 :                             &m_osInsertionBuffer[nInsertionBufferPos];
     371        5141 :                         pszVal = pszValEdit;
     372        5141 :                         if (psFieldRaw->Date.Year < 0 ||
     373        5141 :                             psFieldRaw->Date.Year >= 10000)
     374             :                         {
     375           0 :                             CPLError(
     376             :                                 CE_Failure, CPLE_AppDefined,
     377             :                                 "OGRGetISO8601DateTime(): year %d unsupported ",
     378           0 :                                 psFieldRaw->Date.Year);
     379           0 :                             nValLengthBytes = 0;
     380             :                         }
     381             :                         else
     382             :                         {
     383        5141 :                             int nYear = psFieldRaw->Date.Year;
     384        5141 :                             pszValEdit[3] = (nYear % 10) + '0';
     385        5141 :                             nYear /= 10;
     386        5141 :                             pszValEdit[2] = (nYear % 10) + '0';
     387        5141 :                             nYear /= 10;
     388        5141 :                             pszValEdit[1] = (nYear % 10) + '0';
     389        5141 :                             nYear /= 10;
     390        5141 :                             pszValEdit[0] =
     391        5141 :                                 static_cast<char>(nYear /*% 10*/ + '0');
     392        5141 :                             pszValEdit[4] = '-';
     393        5141 :                             pszValEdit[5] =
     394        5141 :                                 ((psFieldRaw->Date.Month / 10) % 10) + '0';
     395        5141 :                             pszValEdit[6] = (psFieldRaw->Date.Month % 10) + '0';
     396        5141 :                             pszValEdit[7] = '-';
     397        5141 :                             pszValEdit[8] =
     398        5141 :                                 ((psFieldRaw->Date.Day / 10) % 10) + '0';
     399        5141 :                             pszValEdit[9] = (psFieldRaw->Date.Day % 10) + '0';
     400        5141 :                             nValLengthBytes = 10;
     401        5141 :                             nInsertionBufferPos += 10;
     402             :                         }
     403             :                     }
     404     4417450 :                     else if (eType == OFTDateTime)
     405             :                     {
     406        5169 :                         destructorType = SQLITE_STATIC;
     407             :                         const auto psFieldRaw =
     408        5169 :                             poFeature->GetRawFieldRef(iField);
     409             :                         char *pszValEdit =
     410        5169 :                             &m_osInsertionBuffer[nInsertionBufferPos];
     411        5169 :                         pszVal = pszValEdit;
     412        5169 :                         if (m_poDS->m_bDateTimeWithTZ ||
     413           6 :                             psFieldRaw->Date.TZFlag == 100)
     414             :                         {
     415        5165 :                             nValLengthBytes = OGRGetISO8601DateTime(
     416        5165 :                                 psFieldRaw, m_sDateTimeFormat, pszValEdit);
     417             :                         }
     418             :                         else
     419             :                         {
     420           4 :                             OGRField sField(*psFieldRaw);
     421           4 :                             if (sField.Date.TZFlag == 0 ||
     422           2 :                                 sField.Date.TZFlag == 1)
     423             :                             {
     424           2 :                                 sField.Date.TZFlag = 100;
     425             :                             }
     426             :                             else
     427             :                             {
     428             :                                 struct tm brokendowntime;
     429           2 :                                 brokendowntime.tm_year =
     430           2 :                                     sField.Date.Year - 1900;
     431           2 :                                 brokendowntime.tm_mon = sField.Date.Month - 1;
     432           2 :                                 brokendowntime.tm_mday = sField.Date.Day;
     433           2 :                                 brokendowntime.tm_hour = sField.Date.Hour;
     434           2 :                                 brokendowntime.tm_min = sField.Date.Minute;
     435           2 :                                 brokendowntime.tm_sec = 0;
     436             :                                 GIntBig nDT =
     437           2 :                                     CPLYMDHMSToUnixTime(&brokendowntime);
     438           2 :                                 const int TZOffset =
     439           2 :                                     std::abs(sField.Date.TZFlag - 100) * 15;
     440           2 :                                 nDT -= TZOffset * 60;
     441           2 :                                 CPLUnixTimeToYMDHMS(nDT, &brokendowntime);
     442           2 :                                 sField.Date.Year = static_cast<GInt16>(
     443           2 :                                     brokendowntime.tm_year + 1900);
     444           2 :                                 sField.Date.Month = static_cast<GByte>(
     445           2 :                                     brokendowntime.tm_mon + 1);
     446           2 :                                 sField.Date.Day =
     447           2 :                                     static_cast<GByte>(brokendowntime.tm_mday);
     448           2 :                                 sField.Date.Hour =
     449           2 :                                     static_cast<GByte>(brokendowntime.tm_hour);
     450           2 :                                 sField.Date.Minute =
     451           2 :                                     static_cast<GByte>(brokendowntime.tm_min);
     452           2 :                                 sField.Date.TZFlag = 100;
     453             :                             }
     454             : 
     455           4 :                             nValLengthBytes = OGRGetISO8601DateTime(
     456           4 :                                 &sField, m_sDateTimeFormat, pszValEdit);
     457             :                         }
     458        5169 :                         nInsertionBufferPos += nValLengthBytes;
     459             :                     }
     460     4412280 :                     else if (eType == OFTString)
     461             :                     {
     462     4407280 :                         pszVal = poFeature->GetFieldAsStringUnsafe(iField);
     463     4407280 :                         if (poFieldDefn->GetWidth() > 0)
     464             :                         {
     465         970 :                             if (!CPLIsUTF8(pszVal, -1))
     466             :                             {
     467           4 :                                 CPLError(CE_Warning, CPLE_AppDefined,
     468             :                                          "Value of field '%s' is not a valid "
     469             :                                          "UTF-8 string.%s",
     470           2 :                                          poFeatureDefn->GetFieldDefn(iField)
     471             :                                              ->GetNameRef(),
     472           2 :                                          m_bTruncateFields
     473             :                                              ? " Value will be laundered."
     474             :                                              : "");
     475           2 :                                 if (m_bTruncateFields)
     476             :                                 {
     477           1 :                                     pszValToFree =
     478           1 :                                         CPLForceToASCII(pszVal, -1, '_');
     479           1 :                                     pszVal = pszValToFree;
     480           1 :                                     destructorType = CPLFree;
     481             :                                 }
     482             :                             }
     483             : 
     484         970 :                             if (CPLStrlenUTF8Ex(pszVal) >
     485         970 :                                 static_cast<size_t>(poFieldDefn->GetWidth()))
     486             :                             {
     487           4 :                                 CPLError(CE_Warning, CPLE_AppDefined,
     488             :                                          "Value of field '%s' has %" PRIu64
     489             :                                          " characters, "
     490             :                                          "whereas maximum allowed is %d.%s",
     491           2 :                                          poFeatureDefn->GetFieldDefn(iField)
     492             :                                              ->GetNameRef(),
     493             :                                          static_cast<uint64_t>(
     494           2 :                                              CPLStrlenUTF8Ex(pszVal)),
     495             :                                          poFieldDefn->GetWidth(),
     496           2 :                                          m_bTruncateFields
     497             :                                              ? " Value will be truncated."
     498             :                                              : "");
     499           2 :                                 if (m_bTruncateFields)
     500             :                                 {
     501           1 :                                     size_t countUTF8Chars = 0;
     502           1 :                                     nValLengthBytes = 0;
     503           3 :                                     while (pszVal[nValLengthBytes])
     504             :                                     {
     505           3 :                                         if ((pszVal[nValLengthBytes] & 0xc0) !=
     506             :                                             0x80)
     507             :                                         {
     508             :                                             // Stop at the start of the
     509             :                                             // character just beyond the maximum
     510             :                                             // accepted
     511           3 :                                             if (countUTF8Chars ==
     512           3 :                                                 static_cast<size_t>(
     513           3 :                                                     poFieldDefn->GetWidth()))
     514             :                                             {
     515           1 :                                                 break;
     516             :                                             }
     517           2 :                                             countUTF8Chars++;
     518             :                                         }
     519           2 :                                         nValLengthBytes++;
     520             :                                     }
     521             :                                 }
     522             :                             }
     523             :                         }
     524             :                         else
     525             :                         {
     526     4406320 :                             destructorType = SQLITE_STATIC;
     527             :                         }
     528             : 
     529             :                         // If the field subtype is JSON and the current value cannot
     530             :                         // be parsed as a valid JSON, encode it as a JSON string.
     531     4407280 :                         if (poFieldDefn->GetSubType() == OFSTJSON)
     532             :                         {
     533          15 :                             json_object *poObjProp = nullptr;
     534          15 :                             std::string osValue(pszVal);
     535          15 :                             if (!OGRJSonParse(
     536             :                                     osValue.c_str(), &poObjProp, false,
     537          15 :                                     static_cast<int>(osValue.length() + 1)))
     538             :                             {
     539           1 :                                 CPLErrorOnce(
     540             :                                     CE_Warning, CPLE_AppDefined,
     541             :                                     "Field %s is declared as JSON but the "
     542             :                                     "value is not a valid JSON. Storing as "
     543             :                                     "string.",
     544             :                                     poFieldDefn->GetNameRef());
     545             :                                 // Escape and quote
     546           2 :                                 osValue = CPLJSONObject(pszVal).Format(
     547           1 :                                     CPLJSONObject::PrettyFormat::Plain);
     548             :                             }
     549             :                             else
     550             :                             {
     551          14 :                                 osValue = json_object_to_json_string(poObjProp);
     552             :                             }
     553          15 :                             err = sqlite3_bind_text(
     554             :                                 poStmt, nColCount++, osValue.c_str(),
     555          15 :                                 static_cast<int>(osValue.size()),
     556             :                                 SQLITE_TRANSIENT);
     557          15 :                             bBound = true;
     558          15 :                             json_object_put(poObjProp);
     559          15 :                             CPLFree(pszValToFree);
     560          15 :                             pszValToFree = nullptr;
     561          15 :                             CPL_IGNORE_RET_VAL(pszValToFree);
     562          15 :                             destructorType = SQLITE_TRANSIENT;
     563          15 :                             pszVal = "";
     564          15 :                             nValLengthBytes = 0;
     565             :                         }
     566             :                     }
     567             :                     else
     568             :                     {
     569        5000 :                         pszVal = poFeature->GetFieldAsString(iField);
     570             :                     }
     571             : 
     572     4422600 :                     if (!bBound)
     573             :                     {
     574             :                         err =
     575     4422580 :                             sqlite3_bind_text(poStmt, nColCount++, pszVal,
     576             :                                               nValLengthBytes, destructorType);
     577             :                     }
     578     4422600 :                     break;
     579             :                 }
     580             :             }
     581             :         }
     582             :         else
     583             :         {
     584          38 :             err = sqlite3_bind_null(poStmt, nColCount++);
     585             :         }
     586     4466930 :         if (err != SQLITE_OK)
     587             :         {
     588           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     589             :                      "sqlite3_bind_() for column %s failed: %s",
     590             :                      poFieldDefn->GetNameRef(),
     591           0 :                      sqlite3_errmsg(m_poDS->GetDB()));
     592           0 :             return OGRERR_FAILURE;
     593             :         }
     594             :     }
     595             : 
     596      268840 :     if (pnColCount != nullptr)
     597          66 :         *pnColCount = nColCount;
     598      268840 :     return OGRERR_NONE;
     599             : }
     600             : 
     601             : //----------------------------------------------------------------------
     602             : // FeatureBindUpdateParameters()
     603             : //
     604             : // Selectively bind the values of an OGRFeature to a prepared
     605             : // statement, prior to execution. Carefully binds exactly the
     606             : // same parameters that have been set up by FeatureGenerateUpdateSQL()
     607             : // as bindable.
     608             : //
     609             : OGRErr
     610          56 : OGRGeoPackageTableLayer::FeatureBindUpdateParameters(OGRFeature *poFeature,
     611             :                                                      sqlite3_stmt *poStmt)
     612             : {
     613             : 
     614          56 :     int nColCount = 0;
     615          56 :     const OGRErr err = FeatureBindParameters(
     616             :         poFeature, poStmt, &nColCount, false, false, -1, nullptr, -1, nullptr);
     617          56 :     if (err != OGRERR_NONE)
     618           0 :         return err;
     619             : 
     620             :     // Bind the FID to the "WHERE" clause.
     621             :     const int sqlite_err =
     622          56 :         sqlite3_bind_int64(poStmt, nColCount, poFeature->GetFID());
     623          56 :     if (sqlite_err != SQLITE_OK)
     624             :     {
     625           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     626             :                  "failed to bind FID '" CPL_FRMT_GIB "' to statement: %s",
     627           0 :                  poFeature->GetFID(), sqlite3_errmsg(m_poDS->GetDB()));
     628           0 :         return OGRERR_FAILURE;
     629             :     }
     630             : 
     631          56 :     return OGRERR_NONE;
     632             : }
     633             : 
     634             : //----------------------------------------------------------------------
     635             : // FeatureBindInsertParameters()
     636             : //
     637             : // Selectively bind the values of an OGRFeature to a prepared
     638             : // statement, prior to execution. Carefully binds exactly the
     639             : // same parameters that have been set up by FeatureGenerateInsertSQL()
     640             : // as bindable.
     641             : //
     642      268774 : OGRErr OGRGeoPackageTableLayer::FeatureBindInsertParameters(
     643             :     OGRFeature *poFeature, sqlite3_stmt *poStmt, bool bAddFID,
     644             :     bool bBindUnsetFields)
     645             : {
     646      268774 :     return FeatureBindParameters(poFeature, poStmt, nullptr, bAddFID,
     647      268774 :                                  bBindUnsetFields, -1, nullptr, -1, nullptr);
     648             : }
     649             : 
     650             : //----------------------------------------------------------------------
     651             : // FeatureGenerateInsertSQL()
     652             : //
     653             : // Build a SQL INSERT statement that references all the columns in
     654             : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
     655             : // statement. All statements start off with geometry (if it exists)
     656             : // then reference each column in the order it appears in the OGRFeatureDefn.
     657             : // FeatureBindParameters operates on the expectation of this
     658             : // column ordering.
     659             : //
     660         857 : CPLString OGRGeoPackageTableLayer::FeatureGenerateInsertSQL(
     661             :     OGRFeature *poFeature, bool bAddFID, bool bBindUnsetFields, bool bUpsert,
     662             :     const std::string &osUpsertUniqueColumnName)
     663             : {
     664         857 :     bool bNeedComma = false;
     665         857 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     666             : 
     667         857 :     if (poFeatureDefn->GetFieldCount() ==
     668         857 :             ((m_iFIDAsRegularColumnIndex >= 0) ? 1 : 0) &&
     669         857 :         poFeatureDefn->GetGeomFieldCount() == 0 && !bAddFID)
     670             :         return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
     671           8 :                           SQLEscapeName(m_pszTableName).c_str());
     672             : 
     673             :     /* Set up our SQL string basics */
     674        1706 :     CPLString osSQLFront("INSERT");
     675         853 :     if (bUpsert && osUpsertUniqueColumnName.empty())
     676           8 :         osSQLFront += " OR REPLACE";
     677             :     osSQLFront +=
     678         853 :         CPLSPrintf(" INTO \"%s\" ( ", SQLEscapeName(m_pszTableName).c_str());
     679             : 
     680        1706 :     CPLString osSQLBack;
     681         853 :     osSQLBack = ") VALUES (";
     682             : 
     683        1706 :     CPLString osSQLColumn;
     684             : 
     685         853 :     if (bAddFID)
     686             :     {
     687         125 :         osSQLColumn.Printf("\"%s\"", SQLEscapeName(GetFIDColumn()).c_str());
     688         125 :         osSQLFront += osSQLColumn;
     689         125 :         osSQLBack += "?";
     690         125 :         bNeedComma = true;
     691             :     }
     692             : 
     693         853 :     if (poFeatureDefn->GetGeomFieldCount())
     694             :     {
     695         794 :         if (bNeedComma)
     696             :         {
     697         105 :             osSQLFront += ", ";
     698         105 :             osSQLBack += ", ";
     699             :         }
     700             : 
     701             :         osSQLColumn.Printf(
     702             :             "\"%s\"",
     703        1588 :             SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
     704         794 :                 .c_str());
     705         794 :         osSQLFront += osSQLColumn;
     706         794 :         osSQLBack += "?";
     707         794 :         bNeedComma = true;
     708             :     }
     709             : 
     710             :     /* Add attribute column names (except FID) to the SQL */
     711        3343 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     712             :     {
     713        2490 :         const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(i);
     714        2490 :         if (i == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
     715          13 :             continue;
     716        2477 :         if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
     717           2 :             continue;
     718             : 
     719        2475 :         if (!bNeedComma)
     720             :         {
     721          39 :             bNeedComma = true;
     722             :         }
     723             :         else
     724             :         {
     725        2436 :             osSQLFront += ", ";
     726        2436 :             osSQLBack += ", ";
     727             :         }
     728             : 
     729             :         osSQLColumn.Printf(
     730             :             "\"%s\"",
     731        4950 :             SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
     732        2475 :                 .c_str());
     733        2475 :         osSQLFront += osSQLColumn;
     734        2475 :         osSQLBack += "?";
     735             :     }
     736             : 
     737         853 :     osSQLBack += ")";
     738             : 
     739         853 :     if (!bNeedComma)
     740             :         return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
     741           0 :                           SQLEscapeName(m_pszTableName).c_str());
     742             : 
     743         853 :     if (bUpsert && !osUpsertUniqueColumnName.empty())
     744             :     {
     745           7 :         osSQLBack += " ON CONFLICT ";
     746             : #if SQLITE_VERSION_NUMBER < 3035000L
     747             :         osSQLBack += "(\"";
     748             :         osSQLBack += SQLEscapeName(osUpsertUniqueColumnName.c_str());
     749             :         osSQLBack += "\") ";
     750             : #endif
     751           7 :         osSQLBack += "DO UPDATE SET ";
     752           7 :         bNeedComma = false;
     753           7 :         if (poFeatureDefn->GetGeomFieldCount())
     754             :         {
     755             :             osSQLBack += CPLSPrintf(
     756             :                 "\"%s\" = excluded.\"%s\"",
     757           8 :                 SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
     758             :                     .c_str(),
     759           8 :                 SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
     760           8 :                     .c_str());
     761           4 :             bNeedComma = true;
     762             :         }
     763          23 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
     764             :         {
     765          16 :             const auto *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
     766          16 :             if (i == m_iFIDAsRegularColumnIndex || poFieldDefn->IsGenerated())
     767           2 :                 continue;
     768          14 :             if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
     769           0 :                 continue;
     770             : 
     771          14 :             if (!bNeedComma)
     772             :             {
     773           3 :                 bNeedComma = true;
     774             :             }
     775             :             else
     776             :             {
     777          11 :                 osSQLBack += ", ";
     778             :             }
     779             : 
     780             :             const std::string osEscapedColName =
     781          28 :                 SQLEscapeName(poFieldDefn->GetNameRef());
     782             :             osSQLBack +=
     783             :                 CPLSPrintf("\"%s\" = excluded.\"%s\"", osEscapedColName.c_str(),
     784          14 :                            osEscapedColName.c_str());
     785             :         }
     786             : #if SQLITE_VERSION_NUMBER >= 3035000L
     787           7 :         osSQLBack += " RETURNING \"";
     788           7 :         osSQLBack += SQLEscapeName(GetFIDColumn()).c_str();
     789           7 :         osSQLBack += "\"";
     790             : #endif
     791             :     }
     792             : 
     793        1706 :     return osSQLFront + osSQLBack;
     794             : }
     795             : 
     796             : //----------------------------------------------------------------------
     797             : // FeatureGenerateUpdateSQL()
     798             : //
     799             : // Build a SQL UPDATE statement that references all the columns in
     800             : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
     801             : // statement. All statements start off with geometry (if it exists)
     802             : // then reference each column in the order it appears in the OGRFeatureDefn.
     803             : // FeatureBindParameters operates on the expectation of this
     804             : // column ordering.
     805             : 
     806             : //
     807          46 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
     808             :     const OGRFeature *poFeature) const
     809             : {
     810          46 :     bool bNeedComma = false;
     811          46 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     812             : 
     813             :     /* Set up our SQL string basics */
     814          92 :     std::string osUpdate("UPDATE \"");
     815          46 :     osUpdate += SQLEscapeName(m_pszTableName);
     816          46 :     osUpdate += "\" SET ";
     817             : 
     818          46 :     if (poFeatureDefn->GetGeomFieldCount() > 0)
     819             :     {
     820          39 :         osUpdate += '"';
     821             :         osUpdate +=
     822          39 :             SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
     823          39 :         osUpdate += "\"=?";
     824          39 :         bNeedComma = true;
     825             :     }
     826             : 
     827             :     /* Add attribute column names (except FID) to the SQL */
     828          46 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
     829         114 :     for (int i = 0; i < nFieldCount; i++)
     830             :     {
     831          68 :         const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(i);
     832          68 :         if (i == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
     833           8 :             continue;
     834          60 :         if (!poFeature->IsFieldSet(i))
     835          11 :             continue;
     836          49 :         if (!bNeedComma)
     837           6 :             bNeedComma = true;
     838             :         else
     839          43 :             osUpdate += ", ";
     840             : 
     841          49 :         osUpdate += '"';
     842          49 :         osUpdate += SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     843          49 :         osUpdate += "\"=?";
     844             :     }
     845          46 :     if (!bNeedComma)
     846           1 :         return CPLString();
     847             : 
     848          45 :     osUpdate += " WHERE \"";
     849          45 :     osUpdate += SQLEscapeName(m_pszFidColumn);
     850          45 :     osUpdate += "\" = ?";
     851             : 
     852          45 :     return osUpdate;
     853             : }
     854             : 
     855             : /************************************************************************/
     856             : /*                            GetLayerDefn()                            */
     857             : /************************************************************************/
     858             : 
     859       67644 : const OGRFeatureDefn *OGRGeoPackageTableLayer::GetLayerDefn() const
     860             : {
     861       67644 :     if (!m_bFeatureDefnCompleted)
     862             :     {
     863        1074 :         m_bFeatureDefnCompleted = true;
     864        1074 :         const_cast<OGRGeoPackageTableLayer *>(this)->ReadTableDefinition();
     865        1074 :         m_poFeatureDefn->Seal(/* bSealFields = */ true);
     866             :     }
     867       67644 :     return m_poFeatureDefn;
     868             : }
     869             : 
     870             : /************************************************************************/
     871             : /*                            GetFIDColumn()                            */
     872             : /************************************************************************/
     873             : 
     874        3478 : const char *OGRGeoPackageTableLayer::GetFIDColumn() const
     875             : {
     876        3478 :     if (!m_bFeatureDefnCompleted)
     877          25 :         GetLayerDefn();
     878        3478 :     return OGRGeoPackageLayer::GetFIDColumn();
     879             : }
     880             : 
     881             : /************************************************************************/
     882             : /*                            GetGeomType()                             */
     883             : /************************************************************************/
     884             : 
     885      273091 : OGRwkbGeometryType OGRGeoPackageTableLayer::GetGeomType() const
     886             : {
     887      273091 :     return m_poFeatureDefn->GetGeomType();
     888             : }
     889             : 
     890             : /************************************************************************/
     891             : /*                         GetGeometryColumn()                          */
     892             : /************************************************************************/
     893             : 
     894        7133 : const char *OGRGeoPackageTableLayer::GetGeometryColumn() const
     895             : 
     896             : {
     897        7133 :     if (m_poFeatureDefn->GetGeomFieldCount() > 0)
     898        7119 :         return m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
     899             :     else
     900          14 :         return "";
     901             : }
     902             : 
     903             : //----------------------------------------------------------------------
     904             : // ReadTableDefinition()
     905             : //
     906             : // Initialization routine. Read all the metadata about a table,
     907             : // starting from just the table name. Reads information from GPKG
     908             : // metadata tables and from SQLite table metadata. Uses it to
     909             : // populate OGRSpatialReference information and OGRFeatureDefn objects,
     910             : // among others.
     911             : //
     912        1074 : OGRErr OGRGeoPackageTableLayer::ReadTableDefinition()
     913             : {
     914        1074 :     m_poDS->IncrementReadTableDefCounter();
     915             : 
     916        1074 :     bool bReadExtent = false;
     917        1074 :     sqlite3 *poDb = m_poDS->GetDB();
     918        1074 :     OGREnvelope oExtent;
     919        2148 :     CPLString osGeomColumnName;
     920        2148 :     CPLString osGeomColsType;
     921        1074 :     bool bHasZ = false;
     922        1074 :     bool bHasM = false;
     923             : 
     924             : #ifdef ENABLE_GPKG_OGR_CONTENTS
     925        1074 :     if (m_poDS->m_bHasGPKGOGRContents)
     926             :     {
     927             :         CPLString osTrigger1Name(
     928        2140 :             CPLSPrintf("trigger_insert_feature_count_%s", m_pszTableName));
     929             :         CPLString osTrigger2Name(
     930        2140 :             CPLSPrintf("trigger_delete_feature_count_%s", m_pszTableName));
     931             :         const std::map<CPLString, CPLString> &oMap =
     932        1070 :             m_poDS->GetNameTypeMapFromSQliteMaster();
     933        2093 :         if (cpl::contains(oMap, osTrigger1Name.toupper()) &&
     934        1023 :             cpl::contains(oMap, osTrigger2Name.toupper()))
     935             :         {
     936        1023 :             m_bOGRFeatureCountTriggersEnabled = true;
     937             :         }
     938          47 :         else if (m_bIsTable)
     939             :         {
     940          39 :             CPLDebug("GPKG",
     941             :                      "Insert/delete feature_count triggers "
     942             :                      "missing on %s",
     943             :                      m_pszTableName);
     944             :         }
     945             :     }
     946             : #endif
     947             : 
     948             : #ifdef ENABLE_GPKG_OGR_CONTENTS
     949        1074 :     if (m_poDS->m_bHasGPKGOGRContents)
     950             :     {
     951        1070 :         char *pszSQL = sqlite3_mprintf("SELECT feature_count "
     952             :                                        "FROM gpkg_ogr_contents "
     953             :                                        "WHERE table_name = '%q'"
     954             : #ifdef WORKAROUND_SQLITE3_BUGS
     955             :                                        " OR 0"
     956             : #endif
     957             :                                        " LIMIT 2",
     958             :                                        m_pszTableName);
     959        2140 :         auto oResultFeatureCount = SQLQuery(poDb, pszSQL);
     960        1070 :         sqlite3_free(pszSQL);
     961        1070 :         if (oResultFeatureCount && oResultFeatureCount->RowCount() == 0)
     962             :         {
     963          35 :             pszSQL = sqlite3_mprintf("SELECT feature_count "
     964             :                                      "FROM gpkg_ogr_contents "
     965             :                                      "WHERE lower(table_name) = lower('%q')"
     966             : #ifdef WORKAROUND_SQLITE3_BUGS
     967             :                                      " OR 0"
     968             : #endif
     969             :                                      " LIMIT 2",
     970             :                                      m_pszTableName);
     971          35 :             oResultFeatureCount = SQLQuery(poDb, pszSQL);
     972          35 :             sqlite3_free(pszSQL);
     973             :         }
     974             : 
     975        1070 :         if (oResultFeatureCount && oResultFeatureCount->RowCount() == 1)
     976             :         {
     977        1035 :             const char *pszFeatureCount = oResultFeatureCount->GetValue(0, 0);
     978        1035 :             if (pszFeatureCount)
     979             :             {
     980        1018 :                 m_nTotalFeatureCount =
     981        1018 :                     std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
     982             :             }
     983             :         }
     984             :     }
     985             : #endif
     986             : 
     987             :     bool bHasPreexistingSingleGeomColumn =
     988        1074 :         m_poFeatureDefn->GetGeomFieldCount() == 1;
     989        1074 :     bool bHasMultipleGeomColsInGpkgGeometryColumns = false;
     990             : 
     991        1074 :     if (m_bIsInGpkgContents)
     992             :     {
     993             :         /* Check that the table name is registered in gpkg_contents */
     994             :         const std::map<CPLString, GPKGContentsDesc> &oMapContents =
     995        1055 :             m_poDS->GetContents();
     996             :         const auto oIterContents =
     997        1055 :             oMapContents.find(CPLString(m_pszTableName).toupper());
     998        1055 :         if (oIterContents == oMapContents.end())
     999             :         {
    1000           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1001             :                      "layer '%s' is not registered in gpkg_contents",
    1002             :                      m_pszTableName);
    1003           0 :             return OGRERR_FAILURE;
    1004             :         }
    1005             : 
    1006        1055 :         const GPKGContentsDesc &oContents = oIterContents->second;
    1007             : 
    1008        1055 :         const char *pszIdentifier = oContents.osIdentifier.c_str();
    1009        1055 :         if (pszIdentifier[0] != 0 && strcmp(pszIdentifier, m_pszTableName) != 0)
    1010           6 :             OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
    1011        1055 :         const char *pszDescription = oContents.osDescription.c_str();
    1012        1055 :         if (pszDescription[0])
    1013          12 :             OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
    1014             : 
    1015        1055 :         if (m_bIsSpatial)
    1016             :         {
    1017             :             /* All the extrema have to be non-NULL for this to make sense */
    1018        1736 :             if (!oContents.osMinX.empty() && !oContents.osMinY.empty() &&
    1019        1736 :                 !oContents.osMaxX.empty() && !oContents.osMaxY.empty())
    1020             :             {
    1021         741 :                 oExtent.MinX = CPLAtof(oContents.osMinX);
    1022         741 :                 oExtent.MinY = CPLAtof(oContents.osMinY);
    1023         741 :                 oExtent.MaxX = CPLAtof(oContents.osMaxX);
    1024         741 :                 oExtent.MaxY = CPLAtof(oContents.osMaxY);
    1025        1482 :                 bReadExtent = oExtent.MinX <= oExtent.MaxX &&
    1026         741 :                               oExtent.MinY <= oExtent.MaxY;
    1027             :             }
    1028             : 
    1029             :             /* Check that the table name is registered in gpkg_geometry_columns
    1030             :              */
    1031         995 :             char *pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
    1032             :                                            "geometry_type_name, srs_id, z, m "
    1033             :                                            "FROM gpkg_geometry_columns "
    1034             :                                            "WHERE table_name = '%q'"
    1035             : #ifdef WORKAROUND_SQLITE3_BUGS
    1036             :                                            " OR 0"
    1037             : #endif
    1038             :                                            " LIMIT 2000",
    1039             :                                            m_pszTableName);
    1040             : 
    1041        1990 :             auto oResultGeomCols = SQLQuery(poDb, pszSQL);
    1042         995 :             sqlite3_free(pszSQL);
    1043         995 :             if (oResultGeomCols && oResultGeomCols->RowCount() == 0)
    1044             :             {
    1045           0 :                 pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
    1046             :                                          "geometry_type_name, srs_id, z, m "
    1047             :                                          "FROM gpkg_geometry_columns "
    1048             :                                          "WHERE lower(table_name) = lower('%q')"
    1049             : #ifdef WORKAROUND_SQLITE3_BUGS
    1050             :                                          " OR 0"
    1051             : #endif
    1052             :                                          " LIMIT 2000",
    1053             :                                          m_pszTableName);
    1054             : 
    1055           0 :                 oResultGeomCols = SQLQuery(poDb, pszSQL);
    1056           0 :                 sqlite3_free(pszSQL);
    1057             :             }
    1058             : 
    1059             :             /* gpkg_geometry_columns query has to work */
    1060         995 :             if (!(oResultGeomCols && oResultGeomCols->RowCount() > 0))
    1061             :             {
    1062           0 :                 CPLError(
    1063             :                     CE_Warning, CPLE_AppDefined,
    1064             :                     "layer '%s' is not registered in gpkg_geometry_columns",
    1065             :                     m_pszTableName);
    1066             :             }
    1067             :             else
    1068             :             {
    1069         995 :                 int iRow = -1;
    1070         995 :                 bHasMultipleGeomColsInGpkgGeometryColumns =
    1071         995 :                     oResultGeomCols->RowCount() > 1;
    1072         996 :                 for (int i = 0; i < oResultGeomCols->RowCount(); ++i)
    1073             :                 {
    1074             :                     const char *pszGeomColName =
    1075         996 :                         oResultGeomCols->GetValue(1, i);
    1076         996 :                     if (!pszGeomColName)
    1077           0 :                         continue;
    1078        1992 :                     if (!bHasPreexistingSingleGeomColumn ||
    1079         996 :                         strcmp(pszGeomColName,
    1080         996 :                                m_poFeatureDefn->GetGeomFieldDefn(0)
    1081             :                                    ->GetNameRef()) == 0)
    1082             :                     {
    1083         995 :                         iRow = i;
    1084         995 :                         break;
    1085             :                     }
    1086             :                 }
    1087             : 
    1088         995 :                 if (iRow >= 0)
    1089             :                 {
    1090             :                     const char *pszGeomColName =
    1091         995 :                         oResultGeomCols->GetValue(1, iRow);
    1092         995 :                     if (pszGeomColName != nullptr)
    1093         995 :                         osGeomColumnName = pszGeomColName;
    1094             :                     const char *pszGeomColsType =
    1095         995 :                         oResultGeomCols->GetValue(2, iRow);
    1096         995 :                     if (pszGeomColsType != nullptr)
    1097         995 :                         osGeomColsType = pszGeomColsType;
    1098         995 :                     m_iSrs = oResultGeomCols->GetValueAsInteger(3, iRow);
    1099         995 :                     m_nZFlag = oResultGeomCols->GetValueAsInteger(4, iRow);
    1100         995 :                     m_nMFlag = oResultGeomCols->GetValueAsInteger(5, iRow);
    1101         995 :                     if (!(EQUAL(osGeomColsType, "GEOMETRY") && m_nZFlag == 2))
    1102             :                     {
    1103         989 :                         bHasZ = CPL_TO_BOOL(m_nZFlag);
    1104         989 :                         bHasM = CPL_TO_BOOL(m_nMFlag);
    1105             :                     }
    1106             :                 }
    1107             :                 else
    1108             :                 {
    1109           0 :                     CPLError(
    1110             :                         CE_Warning, CPLE_AppDefined,
    1111             :                         "Cannot find record for layer '%s' and geometry column "
    1112             :                         "'%s' in gpkg_geometry_columns",
    1113             :                         m_pszTableName,
    1114             :                         bHasPreexistingSingleGeomColumn
    1115           0 :                             ? m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()
    1116             :                             : "unknown");
    1117             :                 }
    1118             :             }
    1119             :         }
    1120             :     }
    1121             : 
    1122             :     // set names (in upper case) of fields with unique constraint
    1123        2148 :     std::set<std::string> uniqueFieldsUC;
    1124        1074 :     if (m_bIsTable)
    1125             :     {
    1126             :         // If resolving the layer definition of a substantial number of tables,
    1127             :         // fetch in a single time the content of the sqlite_master to increase
    1128             :         // performance
    1129             :         // Threshold somewhat arbitrary. If changing it, change
    1130             :         // ogr_gpkg.py::test_ogr_gpkg_unique_many_layers as well
    1131        1066 :         constexpr int THRESHOLD_GET_SQLITE_MASTER = 10;
    1132        1066 :         if (m_poDS->GetReadTableDefCounter() >= THRESHOLD_GET_SQLITE_MASTER)
    1133             :         {
    1134           2 :             uniqueFieldsUC = SQLGetUniqueFieldUCConstraints(
    1135           2 :                 poDb, m_pszTableName, m_poDS->GetSqliteMasterContent());
    1136             :         }
    1137             :         else
    1138             :         {
    1139             :             uniqueFieldsUC =
    1140        1064 :                 SQLGetUniqueFieldUCConstraints(poDb, m_pszTableName);
    1141             :         }
    1142             :     }
    1143             : 
    1144             :     /* Use the "PRAGMA TABLE_INFO()" call to get table definition */
    1145             :     /*  #|name|type|notnull|default|pk */
    1146             :     /*  0|id|integer|0||1 */
    1147             :     /*  1|name|varchar|0||0 */
    1148        1074 :     char *pszSQL = sqlite3_mprintf("pragma table_xinfo('%q')", m_pszTableName);
    1149        2148 :     auto oResultTable = SQLQuery(poDb, pszSQL);
    1150        1074 :     sqlite3_free(pszSQL);
    1151             : 
    1152        1074 :     if (!oResultTable || oResultTable->RowCount() == 0)
    1153             :     {
    1154           0 :         if (oResultTable)
    1155           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
    1156             :                      m_pszTableName);
    1157           0 :         return OGRERR_FAILURE;
    1158             :     }
    1159             : 
    1160             :     /* Populate feature definition from table description */
    1161             : 
    1162             :     // First pass to determine if we have a single PKID column
    1163        1074 :     int nCountPKIDColumns = 0;
    1164        4986 :     for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
    1165             :     {
    1166        3912 :         int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
    1167        3912 :         if (nPKIDIndex > 0)
    1168        1059 :             nCountPKIDColumns++;
    1169             :     }
    1170        1074 :     if (nCountPKIDColumns > 1)
    1171             :     {
    1172           1 :         CPLDebug("GPKG",
    1173             :                  "For table %s, multiple columns make "
    1174             :                  "the primary key. Ignoring them",
    1175             :                  m_pszTableName);
    1176             :     }
    1177             : 
    1178        4986 :     for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
    1179             :     {
    1180        3912 :         const char *pszName = oResultTable->GetValue(1, iRecord);
    1181        7824 :         std::string osType = oResultTable->GetValue(2, iRecord);
    1182        3912 :         int bNotNull = oResultTable->GetValueAsInteger(3, iRecord);
    1183        3912 :         const char *pszDefault = oResultTable->GetValue(4, iRecord);
    1184        3912 :         int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
    1185        3912 :         int nHiddenValue = oResultTable->GetValueAsInteger(6, iRecord);
    1186             : 
    1187        3912 :         OGRFieldSubType eSubType = OFSTNone;
    1188        3912 :         int nMaxWidth = 0;
    1189        3912 :         int nType = OFTMaxType + 1;
    1190             : 
    1191             :         // SQLite 3.31 has a " GENERATED ALWAYS" suffix in the type column,
    1192             :         // but more recent versions no longer have it.
    1193        3912 :         bool bIsGenerated = false;
    1194        3912 :         constexpr const char *GENERATED_ALWAYS_SUFFIX = " GENERATED ALWAYS";
    1195        3912 :         if (osType.size() > strlen(GENERATED_ALWAYS_SUFFIX) &&
    1196        3912 :             CPLString(osType).toupper().compare(
    1197           0 :                 osType.size() - strlen(GENERATED_ALWAYS_SUFFIX),
    1198             :                 strlen(GENERATED_ALWAYS_SUFFIX), GENERATED_ALWAYS_SUFFIX) == 0)
    1199             :         {
    1200           0 :             bIsGenerated = true;
    1201           0 :             osType.resize(osType.size() - strlen(GENERATED_ALWAYS_SUFFIX));
    1202             :         }
    1203        3912 :         constexpr int GENERATED_VIRTUAL = 2;
    1204        3912 :         constexpr int GENERATED_STORED = 3;
    1205        3912 :         if (nHiddenValue == GENERATED_VIRTUAL ||
    1206             :             nHiddenValue == GENERATED_STORED)
    1207             :         {
    1208           2 :             bIsGenerated = true;
    1209             :         }
    1210             : 
    1211        3912 :         if (!osType.empty() || m_bIsTable)
    1212             :         {
    1213        3909 :             nType = GPkgFieldToOGR(osType.c_str(), eSubType, nMaxWidth);
    1214             :         }
    1215             :         else
    1216             :         {
    1217             :             // For a view, if the geometry column is computed, we don't
    1218             :             // get a type, so trust the one from gpkg_geometry_columns
    1219           3 :             if (EQUAL(osGeomColumnName, pszName))
    1220             :             {
    1221           1 :                 osType = osGeomColsType;
    1222             :             }
    1223             :         }
    1224             : 
    1225             :         /* Not a standard field type... */
    1226        7818 :         if (!osType.empty() && !EQUAL(pszName, "OGC_FID") &&
    1227         999 :             ((nType > OFTMaxType && !osGeomColsType.empty()) ||
    1228        2909 :              EQUAL(osGeomColumnName, pszName)))
    1229             :         {
    1230             :             /* Maybe it is a geometry type? */
    1231             :             OGRwkbGeometryType oGeomType;
    1232         997 :             if (nType > OFTMaxType)
    1233         997 :                 oGeomType = GPkgGeometryTypeToWKB(osType.c_str(), bHasZ, bHasM);
    1234             :             else
    1235           0 :                 oGeomType = wkbUnknown;
    1236         997 :             if (oGeomType != wkbNone)
    1237             :             {
    1238        1993 :                 if ((bHasPreexistingSingleGeomColumn &&
    1239         996 :                      (!bHasMultipleGeomColsInGpkgGeometryColumns ||
    1240           3 :                       strcmp(pszName, m_poFeatureDefn->GetGeomFieldDefn(0)
    1241        1994 :                                           ->GetNameRef()) == 0)) ||
    1242           2 :                     m_poFeatureDefn->GetGeomFieldCount() == 0)
    1243             :                 {
    1244             :                     OGRwkbGeometryType oGeomTypeGeomCols =
    1245         995 :                         GPkgGeometryTypeToWKB(osGeomColsType.c_str(), bHasZ,
    1246             :                                               bHasM);
    1247             :                     /* Enforce consistency between table and metadata */
    1248         995 :                     if (wkbFlatten(oGeomType) == wkbUnknown)
    1249         577 :                         oGeomType = oGeomTypeGeomCols;
    1250         995 :                     if (oGeomType != oGeomTypeGeomCols)
    1251             :                     {
    1252           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1253             :                                  "geometry column type for layer '%s' in "
    1254             :                                  "'%s.%s' (%s) is not "
    1255             :                                  "consistent with type in "
    1256             :                                  "gpkg_geometry_columns (%s)",
    1257             :                                  GetName(), m_pszTableName, pszName,
    1258             :                                  osType.c_str(), osGeomColsType.c_str());
    1259             :                     }
    1260             : 
    1261         995 :                     if (!bHasPreexistingSingleGeomColumn)
    1262             :                     {
    1263           0 :                         OGRGeomFieldDefn oGeomField(pszName, oGeomType);
    1264           0 :                         m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
    1265             :                     }
    1266         995 :                     bHasPreexistingSingleGeomColumn = false;
    1267         995 :                     if (bNotNull)
    1268           3 :                         m_poFeatureDefn->GetGeomFieldDefn(0)->SetNullable(
    1269             :                             FALSE);
    1270             : 
    1271             :                     /* Read the SRS */
    1272        1990 :                     auto poSRS = m_poDS->GetSpatialRef(m_iSrs);
    1273         995 :                     if (poSRS)
    1274             :                     {
    1275         890 :                         m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
    1276         445 :                             poSRS.get());
    1277             :                     }
    1278             :                 }
    1279           2 :                 else if (!STARTS_WITH(
    1280             :                              GetName(),
    1281             :                              (std::string(m_pszTableName) + " (").c_str()))
    1282             :                 {
    1283           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1284             :                              "table '%s' has multiple geometry fields. "
    1285             :                              "Ignoring field '%s' for this layer",
    1286             :                              m_pszTableName, pszName);
    1287             :                 }
    1288             :             }
    1289             :             else
    1290             :             {
    1291           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1292             :                          "geometry column '%s' of type '%s' ignored", pszName,
    1293             :                          osType.c_str());
    1294             :             }
    1295             :         }
    1296             :         else
    1297             :         {
    1298        2915 :             if (nType > OFTMaxType)
    1299             :             {
    1300           5 :                 CPLDebug("GPKG",
    1301             :                          "For table %s, unrecognized type name %s for "
    1302             :                          "column %s. Using string type",
    1303             :                          m_pszTableName, osType.c_str(), pszName);
    1304           5 :                 nType = OFTString;
    1305             :             }
    1306             : 
    1307             :             /* Is this the FID column? */
    1308        2915 :             if (nPKIDIndex > 0 && nCountPKIDColumns == 1 &&
    1309        1057 :                 (nType == OFTInteger || nType == OFTInteger64))
    1310             :             {
    1311        1057 :                 m_pszFidColumn = CPLStrdup(pszName);
    1312             :             }
    1313             :             else
    1314             :             {
    1315        3716 :                 OGRFieldDefn oField(pszName, static_cast<OGRFieldType>(nType));
    1316        1858 :                 oField.SetSubType(eSubType);
    1317        1858 :                 oField.SetWidth(nMaxWidth);
    1318        1858 :                 if (bNotNull)
    1319          14 :                     oField.SetNullable(FALSE);
    1320             : 
    1321        1858 :                 if (cpl::contains(uniqueFieldsUC, CPLString(pszName).toupper()))
    1322             :                 {
    1323          48 :                     oField.SetUnique(TRUE);
    1324             :                 }
    1325             : 
    1326        1858 :                 if (pszDefault != nullptr)
    1327             :                 {
    1328          15 :                     int nYear = 0;
    1329          15 :                     int nMonth = 0;
    1330          15 :                     int nDay = 0;
    1331          15 :                     int nHour = 0;
    1332          15 :                     int nMinute = 0;
    1333          15 :                     float fSecond = 0.0f;
    1334          15 :                     if (oField.GetType() == OFTString &&
    1335           3 :                         !EQUAL(pszDefault, "NULL") &&
    1336           3 :                         !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
    1337          18 :                         pszDefault[0] != '(' && pszDefault[0] != '\'' &&
    1338           0 :                         CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
    1339             :                     {
    1340           0 :                         CPLString osDefault("'");
    1341             :                         char *pszTmp =
    1342           0 :                             CPLEscapeString(pszDefault, -1, CPLES_SQL);
    1343           0 :                         osDefault += pszTmp;
    1344           0 :                         CPLFree(pszTmp);
    1345           0 :                         osDefault += "'";
    1346           0 :                         oField.SetDefault(osDefault);
    1347             :                     }
    1348          23 :                     else if (nType == OFTDateTime &&
    1349           8 :                              sscanf(pszDefault, "'%d-%d-%dT%d:%d:%fZ'", &nYear,
    1350             :                                     &nMonth, &nDay, &nHour, &nMinute,
    1351             :                                     &fSecond) == 6)
    1352             :                     {
    1353           2 :                         if (strchr(pszDefault, '.') == nullptr)
    1354           1 :                             oField.SetDefault(
    1355             :                                 CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
    1356             :                                            nYear, nMonth, nDay, nHour, nMinute,
    1357           1 :                                            static_cast<int>(fSecond + 0.5)));
    1358             :                         else
    1359           1 :                             oField.SetDefault(CPLSPrintf(
    1360             :                                 "'%04d/%02d/%02d %02d:%02d:%06.3f'", nYear,
    1361             :                                 nMonth, nDay, nHour, nMinute, fSecond));
    1362             :                     }
    1363          25 :                     else if ((oField.GetType() == OFTDate ||
    1364          12 :                               oField.GetType() == OFTDateTime) &&
    1365           7 :                              !EQUAL(pszDefault, "NULL") &&
    1366           7 :                              !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
    1367           6 :                              pszDefault[0] != '(' && pszDefault[0] != '\'' &&
    1368          30 :                              !(pszDefault[0] >= '0' && pszDefault[0] <= '9') &&
    1369           4 :                              CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
    1370             :                     {
    1371           8 :                         CPLString osDefault("(");
    1372           4 :                         osDefault += pszDefault;
    1373           4 :                         osDefault += ")";
    1374           4 :                         if (EQUAL(osDefault,
    1375             :                                   "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))"))
    1376           4 :                             oField.SetDefault("CURRENT_TIMESTAMP");
    1377             :                         else
    1378           0 :                             oField.SetDefault(osDefault);
    1379             :                     }
    1380             :                     else
    1381             :                     {
    1382           9 :                         oField.SetDefault(pszDefault);
    1383             :                     }
    1384             :                 }
    1385        1858 :                 oField.SetGenerated(bIsGenerated);
    1386        1858 :                 m_poFeatureDefn->AddFieldDefn(&oField);
    1387             :             }
    1388             :         }
    1389             :     }
    1390             : 
    1391             :     /* Wait, we didn't find a FID? Some operations will not be possible */
    1392        1074 :     if (m_bIsTable && m_pszFidColumn == nullptr)
    1393             :     {
    1394           9 :         CPLDebug("GPKG", "no integer primary key defined for table '%s'",
    1395             :                  m_pszTableName);
    1396             :     }
    1397             : 
    1398        1074 :     if (bReadExtent)
    1399             :     {
    1400         741 :         m_poExtent = std::make_unique<OGREnvelope>(oExtent);
    1401             :     }
    1402             : 
    1403             :     // Look for sub-types such as JSON
    1404        1074 :     if (m_poDS->HasDataColumnsTable())
    1405             :     {
    1406          46 :         pszSQL = sqlite3_mprintf(
    1407             :             "SELECT column_name, name, mime_type, "
    1408             :             "constraint_name, description FROM gpkg_data_columns "
    1409             :             "WHERE table_name = '%q'",
    1410             :             m_pszTableName);
    1411          46 :         oResultTable = SQLQuery(poDb, pszSQL);
    1412          46 :         sqlite3_free(pszSQL);
    1413          46 :         if (oResultTable)
    1414             :         {
    1415         154 :             for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
    1416             :             {
    1417         108 :                 const char *pszColumn = oResultTable->GetValue(0, iRecord);
    1418         108 :                 if (pszColumn == nullptr)
    1419           0 :                     continue;
    1420         108 :                 const char *pszName = oResultTable->GetValue(1, iRecord);
    1421             : 
    1422             :                 // We use the "name" attribute from gpkg_data_columns as the
    1423             :                 // field alternative name, so long as it isn't just a copy
    1424             :                 // of the column name
    1425         108 :                 const char *pszAlias = nullptr;
    1426         108 :                 if (pszName && !EQUAL(pszName, pszColumn))
    1427          10 :                     pszAlias = pszName;
    1428             : 
    1429         108 :                 if (pszAlias)
    1430             :                 {
    1431          10 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1432          10 :                     if (iIdx >= 0)
    1433             :                     {
    1434          10 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetAlternativeName(
    1435             :                             pszAlias);
    1436             :                     }
    1437             :                 }
    1438             : 
    1439         108 :                 if (const char *pszDescription =
    1440         108 :                         oResultTable->GetValue(4, iRecord))
    1441             :                 {
    1442           7 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1443           7 :                     if (iIdx >= 0)
    1444             :                     {
    1445           7 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetComment(
    1446             :                             pszDescription);
    1447             :                     }
    1448             :                 }
    1449             : 
    1450         108 :                 const char *pszMimeType = oResultTable->GetValue(2, iRecord);
    1451             :                 const char *pszConstraintName =
    1452         108 :                     oResultTable->GetValue(3, iRecord);
    1453         108 :                 if (pszMimeType && EQUAL(pszMimeType, "application/json"))
    1454             :                 {
    1455           7 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1456          14 :                     if (iIdx >= 0 &&
    1457           7 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->GetType() ==
    1458             :                             OFTString)
    1459             :                     {
    1460           7 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetSubType(
    1461             :                             OFSTJSON);
    1462           7 :                     }
    1463             :                 }
    1464         101 :                 else if (pszConstraintName)
    1465             :                 {
    1466          89 :                     const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
    1467          89 :                     if (iIdx >= 0)
    1468             :                     {
    1469          89 :                         m_poFeatureDefn->GetFieldDefn(iIdx)->SetDomainName(
    1470             :                             pszConstraintName);
    1471             :                     }
    1472             :                 }
    1473             :             }
    1474             :         }
    1475             :     }
    1476             : 
    1477             :     // Look for geometry column coordinate precision in gpkg_metadata
    1478        1074 :     if (m_poDS->HasMetadataTables() && m_poFeatureDefn->GetGeomFieldCount() > 0)
    1479             :     {
    1480         419 :         pszSQL = sqlite3_mprintf(
    1481             :             "SELECT md.metadata, mdr.column_name "
    1482             :             "FROM gpkg_metadata md "
    1483             :             "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id) "
    1484             :             "WHERE lower(mdr.table_name) = lower('%q') "
    1485             :             "AND md.md_standard_uri = 'http://gdal.org' "
    1486             :             "AND md.mime_type = 'text/xml' "
    1487             :             "AND mdr.reference_scope = 'column' "
    1488             :             "AND md.metadata LIKE '<CoordinatePrecision%%' "
    1489             :             "ORDER BY md.id LIMIT 1000",  // to avoid denial of service
    1490             :             m_pszTableName);
    1491             : 
    1492         838 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    1493         419 :         sqlite3_free(pszSQL);
    1494             : 
    1495         435 :         for (int i = 0; oResult && i < oResult->RowCount(); i++)
    1496             :         {
    1497          16 :             const char *pszMetadata = oResult->GetValue(0, i);
    1498          16 :             const char *pszColumn = oResult->GetValue(1, i);
    1499          16 :             if (pszMetadata && pszColumn)
    1500             :             {
    1501             :                 const int iGeomCol =
    1502          16 :                     m_poFeatureDefn->GetGeomFieldIndex(pszColumn);
    1503          16 :                 if (iGeomCol >= 0)
    1504             :                 {
    1505             :                     auto psXMLNode =
    1506          32 :                         CPLXMLTreeCloser(CPLParseXMLString(pszMetadata));
    1507          16 :                     if (psXMLNode)
    1508             :                     {
    1509          32 :                         OGRGeomCoordinatePrecision sCoordPrec;
    1510          16 :                         if (const char *pszVal = CPLGetXMLValue(
    1511          16 :                                 psXMLNode.get(), "xy_resolution", nullptr))
    1512             :                         {
    1513          16 :                             sCoordPrec.dfXYResolution = CPLAtof(pszVal);
    1514             :                         }
    1515          16 :                         if (const char *pszVal = CPLGetXMLValue(
    1516          16 :                                 psXMLNode.get(), "z_resolution", nullptr))
    1517             :                         {
    1518          12 :                             sCoordPrec.dfZResolution = CPLAtof(pszVal);
    1519             :                         }
    1520          16 :                         if (const char *pszVal = CPLGetXMLValue(
    1521          16 :                                 psXMLNode.get(), "m_resolution", nullptr))
    1522             :                         {
    1523          12 :                             sCoordPrec.dfMResolution = CPLAtof(pszVal);
    1524             :                         }
    1525          16 :                         m_poFeatureDefn->GetGeomFieldDefn(iGeomCol)
    1526          16 :                             ->SetCoordinatePrecision(sCoordPrec);
    1527          16 :                         if (CPLTestBool(CPLGetXMLValue(
    1528          16 :                                 psXMLNode.get(), "discard_coord_lsb", "false")))
    1529             :                         {
    1530           4 :                             m_sBinaryPrecision.SetFrom(sCoordPrec);
    1531           4 :                             m_bUndoDiscardCoordLSBOnReading =
    1532           4 :                                 CPLTestBool(CPLGetXMLValue(
    1533           4 :                                     psXMLNode.get(),
    1534             :                                     "undo_discard_coord_lsb_on_reading",
    1535             :                                     "false"));
    1536             :                         }
    1537             :                     }
    1538             :                 }
    1539             :             }
    1540             :         }
    1541             :     }
    1542             : 
    1543             :     /* Update the columns string */
    1544        1074 :     BuildColumns();
    1545             : 
    1546        1074 :     CheckUnknownExtensions();
    1547             : 
    1548        1074 :     InitView();
    1549             : 
    1550        1074 :     return OGRERR_NONE;
    1551             : }
    1552             : 
    1553             : /************************************************************************/
    1554             : /*                      OGRGeoPackageTableLayer()                       */
    1555             : /************************************************************************/
    1556             : 
    1557        4432 : OGRGeoPackageTableLayer::OGRGeoPackageTableLayer(GDALGeoPackageDataset *poDS,
    1558        4432 :                                                  const char *pszTableName)
    1559        4432 :     : OGRGeoPackageLayer(poDS), m_pszTableName(CPLStrdup(pszTableName))
    1560             : {
    1561        4432 :     memset(m_abHasGeometryExtension, 0, sizeof(m_abHasGeometryExtension));
    1562             : 
    1563        4432 :     m_poFeatureDefn = new OGRFeatureDefn(m_pszTableName);
    1564        4432 :     SetDescription(m_poFeatureDefn->GetName());
    1565        4432 :     m_poFeatureDefn->SetGeomType(wkbNone);
    1566        4432 :     m_poFeatureDefn->Reference();
    1567        4432 : }
    1568             : 
    1569             : /************************************************************************/
    1570             : /*                      ~OGRGeoPackageTableLayer()                      */
    1571             : /************************************************************************/
    1572             : 
    1573        8864 : OGRGeoPackageTableLayer::~OGRGeoPackageTableLayer()
    1574             : {
    1575        4432 :     OGRGeoPackageTableLayer::SyncToDisk();
    1576             : 
    1577             :     /* Clean up resources in memory */
    1578        4432 :     if (m_pszTableName)
    1579        4432 :         CPLFree(m_pszTableName);
    1580             : 
    1581        4432 :     if (m_poUpdateStatement)
    1582          23 :         sqlite3_finalize(m_poUpdateStatement);
    1583             : 
    1584        4432 :     if (m_poInsertStatement)
    1585         686 :         sqlite3_finalize(m_poInsertStatement);
    1586             : 
    1587        4432 :     if (m_poGetFeatureStatement)
    1588          34 :         sqlite3_finalize(m_poGetFeatureStatement);
    1589             : 
    1590        4432 :     CancelAsyncNextArrowArray();
    1591        8864 : }
    1592             : 
    1593             : /************************************************************************/
    1594             : /*                     CancelAsyncNextArrowArray()                      */
    1595             : /************************************************************************/
    1596             : 
    1597      336577 : void OGRGeoPackageTableLayer::CancelAsyncNextArrowArray()
    1598             : {
    1599      336577 :     if (m_poFillArrowArray)
    1600             :     {
    1601         100 :         std::lock_guard oLock(m_poFillArrowArray->oMutex);
    1602          50 :         m_poFillArrowArray->nCountRows = -1;
    1603          50 :         m_poFillArrowArray->oCV.notify_one();
    1604             :     }
    1605             : 
    1606      336577 :     if (m_oThreadNextArrowArray.joinable())
    1607             :     {
    1608           2 :         m_oThreadNextArrowArray.join();
    1609             :     }
    1610             : 
    1611      336577 :     m_poFillArrowArray.reset();
    1612             : 
    1613      336591 :     while (!m_oQueueArrowArrayPrefetchTasks.empty())
    1614             :     {
    1615          28 :         auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
    1616          14 :         m_oQueueArrowArrayPrefetchTasks.pop();
    1617             : 
    1618             :         {
    1619          28 :             std::lock_guard oLock(task->m_oMutex);
    1620          14 :             task->m_bStop = true;
    1621          14 :             task->m_oCV.notify_one();
    1622             :         }
    1623          14 :         if (task->m_oThread.joinable())
    1624          14 :             task->m_oThread.join();
    1625             : 
    1626          14 :         if (task->m_psArrowArray)
    1627             :         {
    1628          14 :             if (task->m_psArrowArray->release)
    1629          14 :                 task->m_psArrowArray->release(task->m_psArrowArray.get());
    1630             :         }
    1631             :     }
    1632      336577 : }
    1633             : 
    1634             : /************************************************************************/
    1635             : /*                              InitView()                              */
    1636             : /************************************************************************/
    1637             : 
    1638        1074 : void OGRGeoPackageTableLayer::InitView()
    1639             : {
    1640             : #ifdef SQLITE_HAS_COLUMN_METADATA
    1641        1074 :     if (!m_bIsTable)
    1642             :     {
    1643             :         /* Detect if the view columns have the FID and geom columns of a */
    1644             :         /* table that has itself a spatial index */
    1645           8 :         sqlite3_stmt *hStmt = nullptr;
    1646           8 :         char *pszSQL = sqlite3_mprintf("SELECT * FROM \"%w\"", m_pszTableName);
    1647           8 :         CPL_IGNORE_RET_VAL(
    1648           8 :             sqlite3_prepare_v2(m_poDS->GetDB(), pszSQL, -1, &hStmt, nullptr));
    1649           8 :         sqlite3_free(pszSQL);
    1650           8 :         if (hStmt)
    1651             :         {
    1652           8 :             if (sqlite3_step(hStmt) == SQLITE_ROW)
    1653             :             {
    1654           8 :                 OGRGeoPackageTableLayer *poLayerGeom = nullptr;
    1655           8 :                 const int nRawColumns = sqlite3_column_count(hStmt);
    1656          33 :                 for (int iCol = 0; iCol < nRawColumns; iCol++)
    1657             :                 {
    1658             :                     CPLString osColName(
    1659          50 :                         SQLUnescape(sqlite3_column_name(hStmt, iCol)));
    1660             :                     const char *pszTableName =
    1661          25 :                         sqlite3_column_table_name(hStmt, iCol);
    1662             :                     const char *pszOriginName =
    1663          25 :                         sqlite3_column_origin_name(hStmt, iCol);
    1664          26 :                     if (EQUAL(osColName, "OGC_FID") &&
    1665           1 :                         (pszOriginName == nullptr ||
    1666           1 :                          osColName != pszOriginName))
    1667             :                     {
    1668             :                         // in the case we have a OGC_FID column, and that
    1669             :                         // is not the name of the original column, then
    1670             :                         // interpret this as an explicit intent to be a
    1671             :                         // PKID.
    1672             :                         // We cannot just take the FID of a source table as
    1673             :                         // a FID because of potential joins that would result
    1674             :                         // in multiple records with same source FID.
    1675           1 :                         CPLFree(m_pszFidColumn);
    1676           1 :                         m_pszFidColumn = CPLStrdup(osColName);
    1677           1 :                         m_poFeatureDefn->DeleteFieldDefn(
    1678           1 :                             m_poFeatureDefn->GetFieldIndex(osColName));
    1679             :                     }
    1680          32 :                     else if (iCol == 0 &&
    1681           8 :                              sqlite3_column_type(hStmt, iCol) == SQLITE_INTEGER)
    1682             :                     {
    1683             :                         // Assume the first column of integer type is the FID
    1684             :                         // column per the latest requirements of the GPKG spec
    1685           6 :                         CPLFree(m_pszFidColumn);
    1686           6 :                         m_pszFidColumn = CPLStrdup(osColName);
    1687           6 :                         m_poFeatureDefn->DeleteFieldDefn(
    1688           6 :                             m_poFeatureDefn->GetFieldIndex(osColName));
    1689             :                     }
    1690          18 :                     else if (pszTableName != nullptr &&
    1691             :                              pszOriginName != nullptr)
    1692             :                     {
    1693             :                         OGRGeoPackageTableLayer *poLayer =
    1694           0 :                             dynamic_cast<OGRGeoPackageTableLayer *>(
    1695          16 :                                 m_poDS->GetLayerByName(pszTableName));
    1696          16 :                         if (poLayer != nullptr &&
    1697          32 :                             osColName == GetGeometryColumn() &&
    1698           6 :                             strcmp(pszOriginName,
    1699             :                                    poLayer->GetGeometryColumn()) == 0)
    1700             :                         {
    1701           6 :                             poLayerGeom = poLayer;
    1702             :                         }
    1703             :                     }
    1704             :                 }
    1705             : 
    1706           8 :                 if (poLayerGeom != nullptr && poLayerGeom->HasSpatialIndex())
    1707             :                 {
    1708           9 :                     for (int iCol = 0; iCol < nRawColumns; iCol++)
    1709             :                     {
    1710             :                         const std::string osColName(
    1711           9 :                             SQLUnescape(sqlite3_column_name(hStmt, iCol)));
    1712             :                         const char *pszTableName =
    1713           9 :                             sqlite3_column_table_name(hStmt, iCol);
    1714             :                         const char *pszOriginName =
    1715           9 :                             sqlite3_column_origin_name(hStmt, iCol);
    1716           9 :                         if (pszTableName != nullptr && pszOriginName != nullptr)
    1717             :                         {
    1718             :                             OGRGeoPackageTableLayer *poLayer =
    1719           0 :                                 dynamic_cast<OGRGeoPackageTableLayer *>(
    1720           8 :                                     m_poDS->GetLayerByName(pszTableName));
    1721          16 :                             if (poLayer != nullptr && poLayer == poLayerGeom &&
    1722           8 :                                 strcmp(pszOriginName,
    1723             :                                        poLayer->GetFIDColumn()) == 0)
    1724             :                             {
    1725           6 :                                 m_bHasSpatialIndex = true;
    1726           6 :                                 m_osRTreeName = poLayerGeom->m_osRTreeName;
    1727           6 :                                 m_osFIDForRTree = osColName;
    1728           6 :                                 break;
    1729             :                             }
    1730             :                         }
    1731             :                     }
    1732             :                 }
    1733             :             }
    1734           8 :             sqlite3_finalize(hStmt);
    1735             :         }
    1736             : 
    1737             :         /* Update the columns string */
    1738           8 :         BuildColumns();
    1739             :     }
    1740             : #endif
    1741        1074 : }
    1742             : 
    1743             : /************************************************************************/
    1744             : /*                        CheckUpdatableTable()                         */
    1745             : /************************************************************************/
    1746             : 
    1747        5726 : bool OGRGeoPackageTableLayer::CheckUpdatableTable(const char *pszOperation)
    1748             : {
    1749        5726 :     if (!m_poDS->GetUpdate())
    1750             :     {
    1751           4 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1752             :                  pszOperation);
    1753           4 :         return false;
    1754             :     }
    1755             :     /* -------------------------------------------------------------------- */
    1756             :     /*      Check that is a table and not a view                            */
    1757             :     /* -------------------------------------------------------------------- */
    1758        5722 :     if (!m_bIsTable)
    1759             :     {
    1760           6 :         CPLError(CE_Failure, CPLE_AppDefined, "Layer %s is not a table",
    1761             :                  m_pszTableName);
    1762           6 :         return false;
    1763             :     }
    1764        5716 :     return true;
    1765             : }
    1766             : 
    1767             : /************************************************************************/
    1768             : /*                            CreateField()                             */
    1769             : /************************************************************************/
    1770             : 
    1771        4520 : OGRErr OGRGeoPackageTableLayer::CreateField(const OGRFieldDefn *poField,
    1772             :                                             int /* bApproxOK */)
    1773             : {
    1774        4520 :     if (!m_bFeatureDefnCompleted)
    1775           0 :         GetLayerDefn();
    1776        4520 :     if (!CheckUpdatableTable("CreateField"))
    1777           1 :         return OGRERR_FAILURE;
    1778             : 
    1779        9038 :     OGRFieldDefn oFieldDefn(poField);
    1780        4519 :     int nMaxWidth = 0;
    1781        4519 :     if (m_bPreservePrecision && poField->GetType() == OFTString)
    1782        2962 :         nMaxWidth = poField->GetWidth();
    1783             :     else
    1784        1557 :         oFieldDefn.SetWidth(0);
    1785        4519 :     oFieldDefn.SetPrecision(0);
    1786             : 
    1787        4519 :     if (m_bLaunder)
    1788           1 :         oFieldDefn.SetName(
    1789           2 :             GDALGeoPackageDataset::LaunderName(oFieldDefn.GetNameRef())
    1790             :                 .c_str());
    1791             : 
    1792        4519 :     if (m_poFeatureDefn->GetFieldIndex(oFieldDefn.GetNameRef()) >= 0)
    1793             :     {
    1794           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    1795             :                  "Cannot create field %s. "
    1796             :                  "A field with the same name already exists.",
    1797             :                  oFieldDefn.GetNameRef());
    1798           3 :         return OGRERR_FAILURE;
    1799             :     }
    1800             : 
    1801        4516 :     if (m_poFeatureDefn->GetGeomFieldIndex(oFieldDefn.GetNameRef()) >= 0)
    1802             :     {
    1803           7 :         CPLError(CE_Failure, CPLE_AppDefined,
    1804             :                  "Cannot create field %s. "
    1805             :                  "It has the same name as the geometry field.",
    1806             :                  oFieldDefn.GetNameRef());
    1807           7 :         return OGRERR_FAILURE;
    1808             :     }
    1809             : 
    1810       13527 :     if (m_pszFidColumn != nullptr &&
    1811        4516 :         EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn) &&
    1812          12 :         poField->GetType() != OFTInteger &&
    1813        9024 :         poField->GetType() != OFTInteger64 &&
    1814             :         // typically a GeoPackage exported with QGIS as a shapefile and
    1815             :         // re-imported See https://github.com/qgis/QGIS/pull/43118
    1816           6 :         !(poField->GetType() == OFTReal && poField->GetWidth() == 20 &&
    1817           1 :           poField->GetPrecision() == 0))
    1818             :     {
    1819           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1820             :                  oFieldDefn.GetNameRef());
    1821           3 :         return OGRERR_FAILURE;
    1822             :     }
    1823             : 
    1824             :     const int nMaxColumns =
    1825        4506 :         sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_COLUMN, -1);
    1826             :     // + 1 for the FID column
    1827        4506 :     if (m_poFeatureDefn->GetFieldCount() +
    1828        4506 :             m_poFeatureDefn->GetGeomFieldCount() + 1 >=
    1829             :         nMaxColumns)
    1830             :     {
    1831           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1832             :                  "Cannot add field %s. Limit of %d columns reached",
    1833             :                  oFieldDefn.GetNameRef(), nMaxColumns);
    1834           1 :         return OGRERR_FAILURE;
    1835             :     }
    1836             : 
    1837        4505 :     if (!m_bDeferredCreation)
    1838             :     {
    1839          15 :         CPLString osCommand;
    1840             : 
    1841             :         // ADD COLUMN has several restrictions
    1842             :         // See https://www.sqlite.org/lang_altertable.html#altertabaddcol
    1843             : 
    1844             :         osCommand.Printf("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s",
    1845          30 :                          SQLEscapeName(m_pszTableName).c_str(),
    1846          30 :                          SQLEscapeName(oFieldDefn.GetNameRef()).c_str(),
    1847             :                          GPkgFieldFromOGR(poField->GetType(),
    1848          45 :                                           poField->GetSubType(), nMaxWidth));
    1849          15 :         if (!poField->IsNullable())
    1850           1 :             osCommand += " NOT NULL";
    1851          15 :         if (poField->IsUnique())
    1852             :         {
    1853             :             // this will fail when SQLCommand() is run, as it is not allowed
    1854             :             // by SQLite. This is a bit of an artificial restriction.
    1855             :             // We could override it by rewriting the table.
    1856           1 :             osCommand += " UNIQUE";
    1857             :         }
    1858          18 :         if (poField->GetDefault() != nullptr &&
    1859           3 :             !poField->IsDefaultDriverSpecific())
    1860             :         {
    1861           3 :             osCommand += " DEFAULT ";
    1862           3 :             int nYear = 0;
    1863           3 :             int nMonth = 0;
    1864           3 :             int nDay = 0;
    1865           3 :             int nHour = 0;
    1866           3 :             int nMinute = 0;
    1867           3 :             float fSecond = 0.0f;
    1868           5 :             if (poField->GetType() == OFTDateTime &&
    1869           2 :                 sscanf(poField->GetDefault(), "'%d/%d/%d %d:%d:%f'", &nYear,
    1870             :                        &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6)
    1871             :             {
    1872           2 :                 if (strchr(poField->GetDefault(), '.') == nullptr)
    1873             :                     osCommand += CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%02dZ'",
    1874             :                                             nYear, nMonth, nDay, nHour, nMinute,
    1875           1 :                                             static_cast<int>(fSecond + 0.5));
    1876             :                 else
    1877             :                     osCommand +=
    1878             :                         CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%06.3fZ'", nYear,
    1879           1 :                                    nMonth, nDay, nHour, nMinute, fSecond);
    1880             :             }
    1881             :             else
    1882             :             {
    1883             :                 // This could fail if it is CURRENT_TIMESTAMP, etc.
    1884           1 :                 osCommand += poField->GetDefault();
    1885             :             }
    1886             :         }
    1887          12 :         else if (!poField->IsNullable())
    1888             :         {
    1889             :             // SQLite mandates a DEFAULT value when adding a NOT NULL column in
    1890             :             // an ALTER TABLE ADD COLUMN.
    1891           1 :             osCommand += " DEFAULT ''";
    1892             :         }
    1893             : 
    1894          15 :         OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
    1895             : 
    1896          15 :         if (err != OGRERR_NONE)
    1897           1 :             return err;
    1898             : 
    1899          14 :         if (!DoSpecialProcessingForColumnCreation(poField))
    1900             :         {
    1901           0 :             return OGRERR_FAILURE;
    1902             :         }
    1903             :     }
    1904             : 
    1905        4504 :     whileUnsealing(m_poFeatureDefn)->AddFieldDefn(&oFieldDefn);
    1906             : 
    1907        4504 :     if (m_poDS->IsInTransaction())
    1908             :     {
    1909             :         m_apoFieldDefnChanges.emplace_back(
    1910         503 :             std::make_unique<OGRFieldDefn>(oFieldDefn),
    1911         503 :             m_poFeatureDefn->GetFieldCount() - 1, FieldChangeType::ADD_FIELD,
    1912        1509 :             m_poDS->GetCurrentSavepoint());
    1913             :     }
    1914             : 
    1915        9008 :     if (m_pszFidColumn != nullptr &&
    1916        4504 :         EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn))
    1917             :     {
    1918           4 :         m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount() - 1;
    1919             :     }
    1920             : 
    1921        4504 :     if (!m_bDeferredCreation)
    1922             :     {
    1923          14 :         ResetReading();
    1924             :     }
    1925             : 
    1926        4504 :     return OGRERR_NONE;
    1927             : }
    1928             : 
    1929             : /************************************************************************/
    1930             : /*                DoSpecialProcessingForColumnCreation()                */
    1931             : /************************************************************************/
    1932             : 
    1933        4512 : bool OGRGeoPackageTableLayer::DoSpecialProcessingForColumnCreation(
    1934             :     const OGRFieldDefn *poField)
    1935             : {
    1936        4512 :     const std::string &osConstraintName(poField->GetDomainName());
    1937        9024 :     const std::string osName(poField->GetAlternativeNameRef());
    1938        4512 :     const std::string &osDescription(poField->GetComment());
    1939             : 
    1940        9024 :     std::string osMimeType;
    1941        4512 :     if (poField->GetType() == OFTString && poField->GetSubType() == OFSTJSON)
    1942             :     {
    1943          16 :         osMimeType = "application/json";
    1944             :     }
    1945             : 
    1946        9004 :     if (osConstraintName.empty() && osName.empty() && osDescription.empty() &&
    1947        4492 :         osMimeType.empty())
    1948             :     {
    1949             :         // no record required
    1950        4476 :         return true;
    1951             :     }
    1952             : 
    1953          36 :     if (!m_poDS->CreateColumnsTableAndColumnConstraintsTablesIfNecessary())
    1954           0 :         return false;
    1955             : 
    1956             :     /* Now let's register our column. */
    1957          72 :     std::string osNameSqlValue;
    1958          36 :     if (osName.empty())
    1959             :     {
    1960          26 :         osNameSqlValue = "NULL";
    1961             :     }
    1962             :     else
    1963             :     {
    1964          10 :         char *pszName = sqlite3_mprintf("'%q'", osName.c_str());
    1965          10 :         osNameSqlValue = std::string(pszName);
    1966          10 :         sqlite3_free(pszName);
    1967             :     }
    1968             : 
    1969          72 :     std::string osDescriptionSqlValue;
    1970          36 :     if (osDescription.empty())
    1971             :     {
    1972          31 :         osDescriptionSqlValue = "NULL";
    1973             :     }
    1974             :     else
    1975             :     {
    1976           5 :         char *pszDescription = sqlite3_mprintf("'%q'", osDescription.c_str());
    1977           5 :         osDescriptionSqlValue = std::string(pszDescription);
    1978           5 :         sqlite3_free(pszDescription);
    1979             :     }
    1980             : 
    1981          72 :     std::string osMimeTypeSqlValue;
    1982          36 :     if (osMimeType.empty())
    1983             :     {
    1984          20 :         osMimeTypeSqlValue = "NULL";
    1985             :     }
    1986             :     else
    1987             :     {
    1988          16 :         char *pszMimeType = sqlite3_mprintf("'%q'", osMimeType.c_str());
    1989          16 :         osMimeTypeSqlValue = std::string(pszMimeType);
    1990          16 :         sqlite3_free(pszMimeType);
    1991             :     }
    1992             : 
    1993          36 :     std::string osConstraintNameValue;
    1994          36 :     if (osConstraintName.empty())
    1995             :     {
    1996          26 :         osConstraintNameValue = "NULL";
    1997             :     }
    1998             :     else
    1999             :     {
    2000             :         char *pszConstraintName =
    2001          10 :             sqlite3_mprintf("'%q'", osConstraintName.c_str());
    2002          10 :         osConstraintNameValue = std::string(pszConstraintName);
    2003          10 :         sqlite3_free(pszConstraintName);
    2004             :     }
    2005             : 
    2006          36 :     char *pszSQL = sqlite3_mprintf(
    2007             :         "INSERT INTO gpkg_data_columns (table_name, column_name, name, "
    2008             :         "title, description, mime_type, constraint_name) VALUES ("
    2009             :         "'%q', '%q', %s, NULL, %s, %s, %s)",
    2010             :         m_pszTableName, poField->GetNameRef(), osNameSqlValue.c_str(),
    2011             :         osDescriptionSqlValue.c_str(), osMimeTypeSqlValue.c_str(),
    2012             :         osConstraintNameValue.c_str());
    2013             : 
    2014          36 :     bool ok = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
    2015          36 :     sqlite3_free(pszSQL);
    2016          36 :     return ok;
    2017             : }
    2018             : 
    2019             : /************************************************************************/
    2020             : /*                          CreateGeomField()                           */
    2021             : /************************************************************************/
    2022             : 
    2023             : OGRErr
    2024           6 : OGRGeoPackageTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
    2025             :                                          int /* bApproxOK */)
    2026             : {
    2027           6 :     if (!m_bFeatureDefnCompleted)
    2028           1 :         GetLayerDefn();
    2029           6 :     if (!CheckUpdatableTable("CreateGeomField"))
    2030           1 :         return OGRERR_FAILURE;
    2031             : 
    2032           5 :     if (m_poFeatureDefn->GetGeomFieldCount() == 1)
    2033             :     {
    2034           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2035             :                  "Cannot create more than one geometry field in GeoPackage");
    2036           2 :         return OGRERR_FAILURE;
    2037             :     }
    2038             : 
    2039           3 :     OGRwkbGeometryType eType = poGeomFieldIn->GetType();
    2040           3 :     if (eType == wkbNone)
    2041             :     {
    2042           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2043             :                  "Cannot create geometry field of type wkbNone");
    2044           0 :         return OGRERR_FAILURE;
    2045             :     }
    2046             : 
    2047           6 :     OGRGeomFieldDefn oGeomField(poGeomFieldIn);
    2048           3 :     const auto poSRSOri = poGeomFieldIn->GetSpatialRef();
    2049           3 :     if (poSRSOri)
    2050             :     {
    2051           0 :         auto poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(poSRSOri);
    2052           0 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2053           0 :         oGeomField.SetSpatialRef(poSRS.get());
    2054             :     }
    2055           3 :     if (EQUAL(oGeomField.GetNameRef(), ""))
    2056             :     {
    2057           1 :         oGeomField.SetName("geom");
    2058             :     }
    2059             : 
    2060           3 :     const OGRSpatialReference *poSRS = oGeomField.GetSpatialRef();
    2061           3 :     m_iSrs = m_poDS->GetSrsId(poSRS);
    2062             : 
    2063             :     /* -------------------------------------------------------------------- */
    2064             :     /*      Create the new field.                                           */
    2065             :     /* -------------------------------------------------------------------- */
    2066           3 :     if (!m_bDeferredCreation)
    2067             :     {
    2068           4 :         char *pszSQL = sqlite3_mprintf(
    2069             :             "ALTER TABLE \"%w\" ADD COLUMN \"%w\" %s%s"
    2070             :             ";"
    2071             :             "UPDATE gpkg_contents SET data_type = 'features' "
    2072             :             "WHERE lower(table_name) = lower('%q')",
    2073             :             m_pszTableName, oGeomField.GetNameRef(),
    2074           2 :             m_poDS->GetGeometryTypeString(oGeomField.GetType()),
    2075           2 :             !oGeomField.IsNullable() ? " NOT NULL DEFAULT ''" : "",
    2076             :             m_pszTableName);
    2077           2 :         CPLString osSQL(pszSQL);
    2078           2 :         sqlite3_free(pszSQL);
    2079             : 
    2080           2 :         OGRErr err = SQLCommand(m_poDS->GetDB(), osSQL);
    2081           2 :         if (err != OGRERR_NONE)
    2082           0 :             return err;
    2083             :     }
    2084             : 
    2085           3 :     if (m_poDS->IsInTransaction())
    2086             :     {
    2087             :         m_apoGeomFieldDefnChanges.emplace_back(
    2088           0 :             std::make_unique<OGRGeomFieldDefn>(oGeomField),
    2089           0 :             m_poFeatureDefn->GetGeomFieldCount(), FieldChangeType::ADD_FIELD);
    2090             :     }
    2091             : 
    2092           3 :     whileUnsealing(m_poFeatureDefn)->AddGeomFieldDefn(&oGeomField);
    2093             : 
    2094           3 :     if (!m_bDeferredCreation)
    2095             :     {
    2096           2 :         OGRErr err = RegisterGeometryColumn();
    2097           2 :         if (err != OGRERR_NONE)
    2098           0 :             return err;
    2099             : 
    2100           2 :         ResetReading();
    2101             :     }
    2102             : 
    2103           3 :     return OGRERR_NONE;
    2104             : }
    2105             : 
    2106             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    2107             : 
    2108             : /************************************************************************/
    2109             : /*                        DisableFeatureCount()                         */
    2110             : /************************************************************************/
    2111             : 
    2112         242 : void OGRGeoPackageTableLayer::DisableFeatureCount()
    2113             : {
    2114         242 :     m_nTotalFeatureCount = -1;
    2115         242 : }
    2116             : 
    2117             : /************************************************************************/
    2118             : /*                     CreateFeatureCountTriggers()                     */
    2119             : /************************************************************************/
    2120             : 
    2121        5961 : void OGRGeoPackageTableLayer::CreateFeatureCountTriggers(
    2122             :     const char *pszTableName)
    2123             : {
    2124        5961 :     if (m_bAddOGRFeatureCountTriggers)
    2125             :     {
    2126        1087 :         if (pszTableName == nullptr)
    2127        1078 :             pszTableName = m_pszTableName;
    2128             : 
    2129        1087 :         m_bOGRFeatureCountTriggersEnabled = true;
    2130        1087 :         m_bAddOGRFeatureCountTriggers = false;
    2131        1087 :         m_bFeatureCountTriggersDeletedInTransaction = false;
    2132             : 
    2133        1087 :         CPLDebug("GPKG", "Creating insert/delete feature_count triggers");
    2134        1087 :         char *pszSQL = sqlite3_mprintf(
    2135             :             "CREATE TRIGGER \"trigger_insert_feature_count_%w\" "
    2136             :             "AFTER INSERT ON \"%w\" "
    2137             :             "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
    2138             :             "feature_count + 1 WHERE lower(table_name) = lower('%q'); END;",
    2139             :             pszTableName, pszTableName, pszTableName);
    2140        1087 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2141        1087 :         sqlite3_free(pszSQL);
    2142             : 
    2143        1087 :         pszSQL = sqlite3_mprintf(
    2144             :             "CREATE TRIGGER \"trigger_delete_feature_count_%w\" "
    2145             :             "AFTER DELETE ON \"%w\" "
    2146             :             "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
    2147             :             "feature_count - 1 WHERE lower(table_name) = lower('%q'); END;",
    2148             :             pszTableName, pszTableName, pszTableName);
    2149        1087 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2150        1087 :         sqlite3_free(pszSQL);
    2151             :     }
    2152        5961 : }
    2153             : 
    2154             : /************************************************************************/
    2155             : /*                    DisableFeatureCountTriggers()                     */
    2156             : /************************************************************************/
    2157             : 
    2158          70 : void OGRGeoPackageTableLayer::DisableFeatureCountTriggers(
    2159             :     bool bNullifyFeatureCount)
    2160             : {
    2161          70 :     if (m_bOGRFeatureCountTriggersEnabled)
    2162             :     {
    2163          70 :         m_bOGRFeatureCountTriggersEnabled = false;
    2164          70 :         m_bAddOGRFeatureCountTriggers = true;
    2165          70 :         m_bFeatureCountTriggersDeletedInTransaction = m_poDS->IsInTransaction();
    2166             : 
    2167          70 :         CPLDebug("GPKG", "Deleting insert/delete feature_count triggers");
    2168             : 
    2169          70 :         char *pszSQL = sqlite3_mprintf(
    2170             :             "DROP TRIGGER \"trigger_insert_feature_count_%w\"", m_pszTableName);
    2171          70 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2172          70 :         sqlite3_free(pszSQL);
    2173             : 
    2174          70 :         pszSQL = sqlite3_mprintf(
    2175             :             "DROP TRIGGER \"trigger_delete_feature_count_%w\"", m_pszTableName);
    2176          70 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    2177          70 :         sqlite3_free(pszSQL);
    2178             : 
    2179          70 :         if (m_poDS->m_bHasGPKGOGRContents && bNullifyFeatureCount)
    2180             :         {
    2181          61 :             pszSQL = sqlite3_mprintf(
    2182             :                 "UPDATE gpkg_ogr_contents SET feature_count = NULL WHERE "
    2183             :                 "lower(table_name )= lower('%q')",
    2184             :                 m_pszTableName);
    2185          61 :             SQLCommand(m_poDS->GetDB(), pszSQL);
    2186          61 :             sqlite3_free(pszSQL);
    2187             :         }
    2188             :     }
    2189          70 : }
    2190             : 
    2191             : #endif  // #ifdef ENABLE_GPKG_OGR_CONTENTS
    2192             : 
    2193             : /************************************************************************/
    2194             : /*                         CheckGeometryType()                          */
    2195             : /************************************************************************/
    2196             : 
    2197             : /** Check that the feature geometry type is consistent with the layer geometry
    2198             :  * type.
    2199             :  *
    2200             :  * And potentially update the Z and M flags of gpkg_geometry_columns to
    2201             :  * reflect the dimensionality of feature geometries.
    2202             :  */
    2203      268847 : void OGRGeoPackageTableLayer::CheckGeometryType(const OGRFeature *poFeature)
    2204             : {
    2205      268847 :     const OGRwkbGeometryType eLayerGeomType = GetGeomType();
    2206      268847 :     const OGRwkbGeometryType eFlattenLayerGeomType = wkbFlatten(eLayerGeomType);
    2207      268847 :     const OGRGeometry *poGeom = poFeature->GetGeometryRef();
    2208      268847 :     if (eFlattenLayerGeomType != wkbNone && eFlattenLayerGeomType != wkbUnknown)
    2209             :     {
    2210       17709 :         if (poGeom != nullptr)
    2211             :         {
    2212             :             OGRwkbGeometryType eGeomType =
    2213       17532 :                 wkbFlatten(poGeom->getGeometryType());
    2214       17548 :             if (!OGR_GT_IsSubClassOf(eGeomType, eFlattenLayerGeomType) &&
    2215          16 :                 !cpl::contains(m_eSetBadGeomTypeWarned, eGeomType))
    2216             :             {
    2217          15 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2218             :                          "A geometry of type %s is inserted into layer %s "
    2219             :                          "of geometry type %s, which is not normally allowed "
    2220             :                          "by the GeoPackage specification, but the driver will "
    2221             :                          "however do it. "
    2222             :                          "To create a conformant GeoPackage, if using ogr2ogr, "
    2223             :                          "the -nlt option can be used to override the layer "
    2224             :                          "geometry type. "
    2225             :                          "This warning will no longer be emitted for this "
    2226             :                          "combination of layer and feature geometry type.",
    2227             :                          OGRToOGCGeomType(eGeomType), GetName(),
    2228             :                          OGRToOGCGeomType(eFlattenLayerGeomType));
    2229          15 :                 m_eSetBadGeomTypeWarned.insert(eGeomType);
    2230             :             }
    2231             :         }
    2232             :     }
    2233             : 
    2234             :     // Make sure to update the z and m columns of gpkg_geometry_columns to 2
    2235             :     // if we have geometries with Z and M components
    2236      268847 :     if (m_nZFlag == 0 || m_nMFlag == 0)
    2237             :     {
    2238      268842 :         if (poGeom != nullptr)
    2239             :         {
    2240      264018 :             bool bUpdateGpkgGeometryColumnsTable = false;
    2241      264018 :             const OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
    2242      264018 :             if (m_nZFlag == 0 && wkbHasZ(eGeomType))
    2243             :             {
    2244          18 :                 if (eLayerGeomType != wkbUnknown && !wkbHasZ(eLayerGeomType))
    2245             :                 {
    2246           2 :                     CPLError(
    2247             :                         CE_Warning, CPLE_AppDefined,
    2248             :                         "Layer '%s' has been declared with non-Z geometry type "
    2249             :                         "%s, but it does contain geometries with Z. Setting "
    2250             :                         "the Z=2 hint into gpkg_geometry_columns. "
    2251             :                         "If using ogr2ogr, specify the -nlt option to avoid "
    2252             :                         "ambiguity.",
    2253             :                         GetName(),
    2254             :                         OGRToOGCGeomType(eLayerGeomType, true, true, true));
    2255             :                 }
    2256          18 :                 m_nZFlag = 2;
    2257          18 :                 bUpdateGpkgGeometryColumnsTable = true;
    2258             :             }
    2259      264018 :             if (m_nMFlag == 0 && wkbHasM(eGeomType))
    2260             :             {
    2261           8 :                 if (eLayerGeomType != wkbUnknown && !wkbHasM(eLayerGeomType))
    2262             :                 {
    2263           1 :                     CPLError(
    2264             :                         CE_Warning, CPLE_AppDefined,
    2265             :                         "Layer '%s' has been declared with non-M geometry type "
    2266             :                         "%s, but it does contain geometries with M. Setting "
    2267             :                         "the M=2 hint into gpkg_geometry_columns. "
    2268             :                         "If using ogr2ogr, specify the -nlt option to avoid "
    2269             :                         "ambiguity.",
    2270             :                         GetName(),
    2271             :                         OGRToOGCGeomType(eLayerGeomType, true, true, true));
    2272             :                 }
    2273           8 :                 m_nMFlag = 2;
    2274           8 :                 bUpdateGpkgGeometryColumnsTable = true;
    2275             :             }
    2276      264018 :             if (bUpdateGpkgGeometryColumnsTable)
    2277             :             {
    2278             :                 /* Update gpkg_geometry_columns */
    2279          21 :                 char *pszSQL = sqlite3_mprintf(
    2280             :                     "UPDATE gpkg_geometry_columns SET z = %d, m = %d WHERE "
    2281             :                     "table_name = '%q' AND column_name = '%q'",
    2282             :                     m_nZFlag, m_nMFlag, GetName(), GetGeometryColumn());
    2283          21 :                 CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
    2284          21 :                 sqlite3_free(pszSQL);
    2285             :             }
    2286             :         }
    2287             :     }
    2288      268847 : }
    2289             : 
    2290             : /************************************************************************/
    2291             : /*                  CheckFIDAndFIDColumnConsistency()                   */
    2292             : /************************************************************************/
    2293             : 
    2294          16 : static bool CheckFIDAndFIDColumnConsistency(const OGRFeature *poFeature,
    2295             :                                             int iFIDAsRegularColumnIndex)
    2296             : {
    2297          16 :     bool ok = true;
    2298          16 :     if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
    2299             :     {
    2300             :         // nothing to do
    2301             :     }
    2302          14 :     else if (poFeature->GetDefnRef()
    2303          14 :                  ->GetFieldDefn(iFIDAsRegularColumnIndex)
    2304          14 :                  ->GetType() == OFTReal)
    2305             :     {
    2306             :         const double dfFID =
    2307           4 :             poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex);
    2308           4 :         if (GDALIsValueInRange<int64_t>(dfFID))
    2309             :         {
    2310           4 :             const auto nFID = static_cast<GIntBig>(dfFID);
    2311           4 :             if (nFID != poFeature->GetFID())
    2312             :             {
    2313           1 :                 ok = false;
    2314           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2315             :                          "Inconsistent values of FID (" CPL_FRMT_GIB
    2316             :                          ") and field of same name (%g)",
    2317             :                          poFeature->GetFID(),
    2318             :                          poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex));
    2319             :             }
    2320             :         }
    2321             :     }
    2322          20 :     else if (poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
    2323          10 :              poFeature->GetFID())
    2324             :     {
    2325           3 :         ok = false;
    2326           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    2327             :                  "Inconsistent values of FID (" CPL_FRMT_GIB
    2328             :                  ") and field of same name (" CPL_FRMT_GIB ")",
    2329             :                  poFeature->GetFID(),
    2330             :                  poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
    2331             :     }
    2332          16 :     return ok;
    2333             : }
    2334             : 
    2335             : /************************************************************************/
    2336             : /*                           ICreateFeature()                           */
    2337             : /************************************************************************/
    2338             : 
    2339             : // rtreeValueDown() / rtreeValueUp() come from SQLite3 source code
    2340             : // SQLite3 RTree stores min/max values as float. So do the same for our
    2341             : // GPKGRTreeEntry
    2342             : 
    2343             : /*
    2344             : ** Rounding constants for float->double conversion.
    2345             : */
    2346             : #define RNDTOWARDS (1.0 - 1.0 / 8388608.0) /* Round towards zero */
    2347             : #define RNDAWAY (1.0 + 1.0 / 8388608.0)    /* Round away from zero */
    2348             : 
    2349             : /*
    2350             : ** Convert an sqlite3_value into an RtreeValue (presumably a float)
    2351             : ** while taking care to round toward negative or positive, respectively.
    2352             : */
    2353      495644 : static float rtreeValueDown(double d)
    2354             : {
    2355      495644 :     float f = static_cast<float>(d);
    2356      495644 :     if (f > d)
    2357             :     {
    2358       13089 :         f = static_cast<float>(d * (d < 0 ? RNDAWAY : RNDTOWARDS));
    2359             :     }
    2360      495644 :     return f;
    2361             : }
    2362             : 
    2363      495644 : static float rtreeValueUp(double d)
    2364             : {
    2365      495644 :     float f = static_cast<float>(d);
    2366      495644 :     if (f < d)
    2367             :     {
    2368       13191 :         f = static_cast<float>(d * (d < 0 ? RNDTOWARDS : RNDAWAY));
    2369             :     }
    2370      495644 :     return f;
    2371             : }
    2372             : 
    2373      268778 : OGRErr OGRGeoPackageTableLayer::CreateOrUpsertFeature(OGRFeature *poFeature,
    2374             :                                                       bool bUpsert)
    2375             : {
    2376      268778 :     if (!m_bFeatureDefnCompleted)
    2377           0 :         GetLayerDefn();
    2378      268778 :     if (!m_poDS->GetUpdate())
    2379             :     {
    2380           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2381             :                  "CreateFeature");
    2382           0 :         return OGRERR_FAILURE;
    2383             :     }
    2384             : 
    2385      268778 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    2386           0 :         return OGRERR_FAILURE;
    2387             : 
    2388      268778 :     CancelAsyncNextArrowArray();
    2389             : 
    2390      537556 :     std::string osUpsertUniqueColumnName;
    2391      268778 :     if (bUpsert && poFeature->GetFID() == OGRNullFID)
    2392             :     {
    2393          15 :         int nUniqueColumns = 0;
    2394          15 :         const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    2395          47 :         for (int i = 0; i < nFieldCount; ++i)
    2396             :         {
    2397          32 :             const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    2398          32 :             if (poFieldDefn->IsUnique())
    2399             :             {
    2400          15 :                 if (osUpsertUniqueColumnName.empty())
    2401          15 :                     osUpsertUniqueColumnName = poFieldDefn->GetNameRef();
    2402          15 :                 nUniqueColumns++;
    2403             :             }
    2404             :         }
    2405          15 :         if (nUniqueColumns == 0)
    2406             :         {
    2407             :             // This is just a regular INSERT
    2408           0 :             bUpsert = false;
    2409             :         }
    2410             :     }
    2411             : 
    2412      268778 :     if (bUpsert)
    2413             :     {
    2414          27 :         if (m_bThreadRTreeStarted)
    2415           0 :             CancelAsyncRTree();
    2416          27 :         if (!RunDeferredSpatialIndexUpdate())
    2417           0 :             return OGRERR_FAILURE;
    2418          27 :         if (!m_bUpdate1TriggerDisabled && HasSpatialIndex())
    2419          18 :             WorkaroundUpdate1TriggerIssue();
    2420             :     }
    2421             : 
    2422             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    2423      268778 :     if (bUpsert)
    2424             :     {
    2425          27 :         if (m_nTotalFeatureCount >= 0)
    2426             :         {
    2427             :             // There's no reliable way of knowing if a new row has been inserted
    2428             :             // or just updated, so serialize known value and then
    2429             :             // invalidate feature count.
    2430          10 :             if (m_poDS->m_bHasGPKGOGRContents)
    2431             :             {
    2432             :                 const char *pszCount =
    2433          10 :                     CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
    2434          10 :                 char *pszSQL = sqlite3_mprintf(
    2435             :                     "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
    2436             :                     "lower(table_name )= lower('%q')",
    2437             :                     pszCount, m_pszTableName);
    2438          10 :                 SQLCommand(m_poDS->GetDB(), pszSQL);
    2439          10 :                 sqlite3_free(pszSQL);
    2440             :             }
    2441          10 :             m_nTotalFeatureCount = -1;
    2442             : 
    2443          10 :             if (!m_bOGRFeatureCountTriggersEnabled)
    2444           1 :                 CreateFeatureCountTriggers();
    2445             :         }
    2446             :     }
    2447             :     else
    2448             :     {
    2449             :         // To maximize performance of insertion, disable feature count triggers
    2450      268751 :         if (m_bOGRFeatureCountTriggersEnabled)
    2451             :         {
    2452          44 :             DisableFeatureCountTriggers();
    2453             :         }
    2454             :     }
    2455             : #endif
    2456             : 
    2457      268778 :     CheckGeometryType(poFeature);
    2458             : 
    2459             :     /* Substitute default values for null Date/DateTime fields as the standard
    2460             :      */
    2461             :     /* format of SQLite is not the one mandated by GeoPackage */
    2462      268778 :     poFeature->FillUnsetWithDefault(FALSE, nullptr);
    2463      268778 :     bool bHasDefaultValue = false;
    2464      268778 :     const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    2465     4736870 :     for (int iField = 0; iField < nFieldCount; iField++)
    2466             :     {
    2467     4468100 :         if (poFeature->IsFieldSetUnsafe(iField))
    2468     4466840 :             continue;
    2469             :         const char *pszDefault =
    2470        1257 :             m_poFeatureDefn->GetFieldDefnUnsafe(iField)->GetDefault();
    2471        1257 :         if (pszDefault != nullptr)
    2472             :         {
    2473           1 :             bHasDefaultValue = true;
    2474             :         }
    2475             :     }
    2476             : 
    2477             :     /* In case the FID column has also been created as a regular field */
    2478      268778 :     if (m_iFIDAsRegularColumnIndex >= 0)
    2479             :     {
    2480          13 :         if (poFeature->GetFID() == OGRNullFID)
    2481             :         {
    2482           6 :             if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
    2483             :             {
    2484          10 :                 if (m_poFeatureDefn->GetFieldDefn(m_iFIDAsRegularColumnIndex)
    2485           5 :                         ->GetType() == OFTReal)
    2486             :                 {
    2487           2 :                     bool ok = false;
    2488             :                     const double dfFID =
    2489           2 :                         poFeature->GetFieldAsDouble(m_iFIDAsRegularColumnIndex);
    2490           4 :                     if (dfFID >= static_cast<double>(
    2491           4 :                                      std::numeric_limits<int64_t>::min()) &&
    2492           2 :                         dfFID <= static_cast<double>(
    2493           2 :                                      std::numeric_limits<int64_t>::max()))
    2494             :                     {
    2495           2 :                         const auto nFID = static_cast<GIntBig>(dfFID);
    2496           2 :                         if (static_cast<double>(nFID) == dfFID)
    2497             :                         {
    2498           1 :                             poFeature->SetFID(nFID);
    2499           1 :                             ok = true;
    2500             :                         }
    2501             :                     }
    2502           2 :                     if (!ok)
    2503             :                     {
    2504           1 :                         CPLError(
    2505             :                             CE_Failure, CPLE_AppDefined,
    2506             :                             "Value of FID %g cannot be parsed to an Integer64",
    2507             :                             dfFID);
    2508           1 :                         return OGRERR_FAILURE;
    2509             :                     }
    2510             :                 }
    2511             :                 else
    2512             :                 {
    2513           3 :                     poFeature->SetFID(poFeature->GetFieldAsInteger64(
    2514           3 :                         m_iFIDAsRegularColumnIndex));
    2515             :                 }
    2516             :             }
    2517             :         }
    2518           7 :         else if (!CheckFIDAndFIDColumnConsistency(poFeature,
    2519             :                                                   m_iFIDAsRegularColumnIndex))
    2520             :         {
    2521           3 :             return OGRERR_FAILURE;
    2522             :         }
    2523             :     }
    2524             : 
    2525             :     /* If there's a unset field with a default value, then we must create */
    2526             :     /* a specific INSERT statement to avoid unset fields to be bound to NULL */
    2527      536694 :     if (m_poInsertStatement &&
    2528      267920 :         (bHasDefaultValue ||
    2529      267920 :          m_bInsertStatementWithFID != (poFeature->GetFID() != OGRNullFID) ||
    2530      535835 :          m_bInsertStatementWithUpsert != bUpsert ||
    2531      267917 :          m_osInsertStatementUpsertUniqueColumnName != osUpsertUniqueColumnName))
    2532             :     {
    2533           3 :         sqlite3_finalize(m_poInsertStatement);
    2534           3 :         m_poInsertStatement = nullptr;
    2535             :     }
    2536             : 
    2537      268774 :     if (!m_poInsertStatement)
    2538             :     {
    2539             :         /* Construct a SQL INSERT statement from the OGRFeature */
    2540             :         /* Only work with fields that are set */
    2541             :         /* Do not stick values into SQL, use placeholder and bind values later
    2542             :          */
    2543         857 :         m_bInsertStatementWithFID = poFeature->GetFID() != OGRNullFID;
    2544         857 :         m_bInsertStatementWithUpsert = bUpsert;
    2545         857 :         m_osInsertStatementUpsertUniqueColumnName = osUpsertUniqueColumnName;
    2546             :         CPLString osCommand = FeatureGenerateInsertSQL(
    2547         857 :             poFeature, m_bInsertStatementWithFID, !bHasDefaultValue, bUpsert,
    2548         857 :             osUpsertUniqueColumnName);
    2549             : 
    2550             :         /* Prepare the SQL into a statement */
    2551         857 :         sqlite3 *poDb = m_poDS->GetDB();
    2552         857 :         int err = SQLPrepareWithError(poDb, osCommand, -1, &m_poInsertStatement,
    2553             :                                       nullptr);
    2554         857 :         if (err != SQLITE_OK)
    2555             :         {
    2556           0 :             return OGRERR_FAILURE;
    2557             :         }
    2558             :     }
    2559             : 
    2560             :     /* Bind values onto the statement now */
    2561      537548 :     OGRErr errOgr = FeatureBindInsertParameters(poFeature, m_poInsertStatement,
    2562      268774 :                                                 m_bInsertStatementWithFID,
    2563      268774 :                                                 !bHasDefaultValue);
    2564      268774 :     if (errOgr != OGRERR_NONE)
    2565             :     {
    2566           0 :         sqlite3_reset(m_poInsertStatement);
    2567           0 :         sqlite3_clear_bindings(m_poInsertStatement);
    2568           0 :         sqlite3_finalize(m_poInsertStatement);
    2569           0 :         m_poInsertStatement = nullptr;
    2570           0 :         return errOgr;
    2571             :     }
    2572             : 
    2573             :     /* From here execute the statement and check errors */
    2574      268774 :     const int err = sqlite3_step(m_poInsertStatement);
    2575      268774 :     if (!(err == SQLITE_OK || err == SQLITE_DONE
    2576             : #if SQLITE_VERSION_NUMBER >= 3035000L
    2577             :           || err == SQLITE_ROW
    2578             : #endif
    2579             :           ))
    2580             :     {
    2581           9 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to execute insert : %s",
    2582           9 :                  sqlite3_errmsg(m_poDS->GetDB())
    2583           9 :                      ? sqlite3_errmsg(m_poDS->GetDB())
    2584             :                      : "");
    2585           9 :         sqlite3_reset(m_poInsertStatement);
    2586           9 :         sqlite3_clear_bindings(m_poInsertStatement);
    2587           9 :         sqlite3_finalize(m_poInsertStatement);
    2588           9 :         m_poInsertStatement = nullptr;
    2589           9 :         return OGRERR_FAILURE;
    2590             :     }
    2591             : 
    2592             :     /* Read the latest FID value */
    2593          27 :     const GIntBig nFID = (bUpsert && !osUpsertUniqueColumnName.empty())
    2594      268765 :                              ?
    2595             : #if SQLITE_VERSION_NUMBER >= 3035000L
    2596          15 :                              sqlite3_column_int64(m_poInsertStatement, 0)
    2597             : #else
    2598             :                              OGRNullFID
    2599             : #endif
    2600      268750 :                              : sqlite3_last_insert_rowid(m_poDS->GetDB());
    2601             : 
    2602      268765 :     sqlite3_reset(m_poInsertStatement);
    2603      268765 :     sqlite3_clear_bindings(m_poInsertStatement);
    2604             : 
    2605      268765 :     if (bHasDefaultValue)
    2606             :     {
    2607           1 :         sqlite3_finalize(m_poInsertStatement);
    2608           1 :         m_poInsertStatement = nullptr;
    2609             :     }
    2610             : 
    2611      268765 :     if (nFID != OGRNullFID)
    2612             :     {
    2613      268765 :         poFeature->SetFID(nFID);
    2614      268765 :         if (m_iFIDAsRegularColumnIndex >= 0)
    2615           9 :             poFeature->SetField(m_iFIDAsRegularColumnIndex, nFID);
    2616             :     }
    2617             :     else
    2618             :     {
    2619           0 :         poFeature->SetFID(OGRNullFID);
    2620             :     }
    2621             : 
    2622             :     /* Update the layer extents with this new object */
    2623      268765 :     if (IsGeomFieldSet(poFeature))
    2624             :     {
    2625      263975 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
    2626      263975 :         if (!poGeom->IsEmpty())
    2627             :         {
    2628      262934 :             OGREnvelope oEnv;
    2629      262934 :             poGeom->getEnvelope(&oEnv);
    2630      262934 :             UpdateExtent(&oEnv);
    2631             : 
    2632      262920 :             if (!bUpsert && !m_bDeferredSpatialIndexCreation &&
    2633      525854 :                 HasSpatialIndex() && m_poDS->IsInTransaction())
    2634             :             {
    2635        1640 :                 m_nCountInsertInTransaction++;
    2636        1640 :                 if (m_nCountInsertInTransactionThreshold < 0)
    2637             :                 {
    2638          16 :                     m_nCountInsertInTransactionThreshold =
    2639          16 :                         atoi(CPLGetConfigOption(
    2640             :                             "OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "100"));
    2641             :                 }
    2642        1640 :                 if (m_nCountInsertInTransaction ==
    2643        1640 :                     m_nCountInsertInTransactionThreshold)
    2644             :                 {
    2645          10 :                     StartDeferredSpatialIndexUpdate();
    2646             :                 }
    2647        1630 :                 else if (!m_aoRTreeTriggersSQL.empty())
    2648             :                 {
    2649        1190 :                     if (m_aoRTreeEntries.size() == 1000 * 1000)
    2650             :                     {
    2651           0 :                         if (!FlushPendingSpatialIndexUpdate())
    2652           0 :                             return OGRERR_FAILURE;
    2653             :                     }
    2654             :                     GPKGRTreeEntry sEntry;
    2655        1190 :                     sEntry.nId = nFID;
    2656        1190 :                     sEntry.fMinX = rtreeValueDown(oEnv.MinX);
    2657        1190 :                     sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
    2658        1190 :                     sEntry.fMinY = rtreeValueDown(oEnv.MinY);
    2659        1190 :                     sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
    2660        1190 :                     m_aoRTreeEntries.push_back(sEntry);
    2661             :                 }
    2662             :             }
    2663      261294 :             else if (!bUpsert && m_bAllowedRTreeThread &&
    2664      250225 :                      !m_bErrorDuringRTreeThread)
    2665             :             {
    2666             :                 GPKGRTreeEntry sEntry;
    2667             : #ifdef DEBUG_VERBOSE
    2668             :                 if (m_aoRTreeEntries.empty())
    2669             :                     CPLDebug("GPKG",
    2670             :                              "Starting to fill m_aoRTreeEntries at "
    2671             :                              "FID " CPL_FRMT_GIB,
    2672             :                              nFID);
    2673             : #endif
    2674      246632 :                 sEntry.nId = nFID;
    2675      246632 :                 sEntry.fMinX = rtreeValueDown(oEnv.MinX);
    2676      246632 :                 sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
    2677      246632 :                 sEntry.fMinY = rtreeValueDown(oEnv.MinY);
    2678      246632 :                 sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
    2679             :                 try
    2680             :                 {
    2681      246632 :                     m_aoRTreeEntries.push_back(sEntry);
    2682      246632 :                     if (m_aoRTreeEntries.size() == m_nRTreeBatchSize)
    2683             :                     {
    2684         896 :                         m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
    2685         896 :                         m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
    2686             :                     }
    2687      484965 :                     if (!m_bThreadRTreeStarted &&
    2688      238333 :                         m_oQueueRTreeEntries.size() ==
    2689      238333 :                             m_nRTreeBatchesBeforeStart)
    2690             :                     {
    2691          48 :                         StartAsyncRTree();
    2692             :                     }
    2693             :                 }
    2694           0 :                 catch (const std::bad_alloc &)
    2695             :                 {
    2696           0 :                     CPLDebug("GPKG",
    2697             :                              "Memory allocation error regarding RTree "
    2698             :                              "structures. Falling back to slower method");
    2699           0 :                     if (m_bThreadRTreeStarted)
    2700           0 :                         CancelAsyncRTree();
    2701             :                     else
    2702           0 :                         m_bAllowedRTreeThread = false;
    2703             :                 }
    2704             :             }
    2705             :         }
    2706             :     }
    2707             : 
    2708             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    2709      268765 :     if (m_nTotalFeatureCount >= 0)
    2710             :     {
    2711      268668 :         if (m_nTotalFeatureCount < std::numeric_limits<int64_t>::max())
    2712             :         {
    2713      268667 :             m_nTotalFeatureCount++;
    2714             :         }
    2715             :         else
    2716             :         {
    2717           1 :             if (m_poDS->m_bHasGPKGOGRContents)
    2718             :             {
    2719           1 :                 char *pszSQL = sqlite3_mprintf(
    2720             :                     "UPDATE gpkg_ogr_contents SET feature_count = null "
    2721             :                     "WHERE lower(table_name) = lower('%q')",
    2722             :                     m_pszTableName);
    2723           1 :                 CPL_IGNORE_RET_VAL(sqlite3_exec(m_poDS->hDB, pszSQL, nullptr,
    2724             :                                                 nullptr, nullptr));
    2725           1 :                 sqlite3_free(pszSQL);
    2726             :             }
    2727           1 :             m_nTotalFeatureCount = -1;
    2728             :         }
    2729             :     }
    2730             : #endif
    2731             : 
    2732      268765 :     m_bContentChanged = true;
    2733             : 
    2734             :     /* All done! */
    2735      268765 :     return OGRERR_NONE;
    2736             : }
    2737             : 
    2738         100 : void OGRGeoPackageTableLayer::FinishRollbackTransaction(
    2739             :     const std::string &osSavepointName)
    2740             : {
    2741         100 :     OGRGeoPackageLayer::FinishRollbackTransaction(osSavepointName);
    2742             :     // Invalidate feature count
    2743         100 :     m_nTotalFeatureCount = -1;
    2744         100 : }
    2745             : 
    2746      268751 : OGRErr OGRGeoPackageTableLayer::ICreateFeature(OGRFeature *poFeature)
    2747             : {
    2748      268751 :     return CreateOrUpsertFeature(poFeature, /* bUpsert=*/false);
    2749             : }
    2750             : 
    2751             : /************************************************************************/
    2752             : /*                  SetDeferredSpatialIndexCreation()                   */
    2753             : /************************************************************************/
    2754             : 
    2755         926 : void OGRGeoPackageTableLayer::SetDeferredSpatialIndexCreation(bool bFlag)
    2756             : {
    2757         926 :     m_bDeferredSpatialIndexCreation = bFlag;
    2758         926 :     if (bFlag)
    2759             :     {
    2760             :         // This method is invoked before the layer is added to the dataset,
    2761             :         // so GetLayerCount() will return 0 for the first layer added.
    2762         926 :         m_bAllowedRTreeThread =
    2763        1749 :             m_poDS->GetLayerCount() == 0 && sqlite3_threadsafe() != 0 &&
    2764        2572 :             CPLGetNumCPUs() >= 2 &&
    2765         823 :             CPLTestBool(
    2766             :                 CPLGetConfigOption("OGR_GPKG_ALLOW_THREADED_RTREE", "YES"));
    2767             : 
    2768             :         // For unit tests
    2769         926 :         if (CPLTestBool(CPLGetConfigOption(
    2770             :                 "OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "NO")))
    2771             :         {
    2772          60 :             m_nRTreeBatchSize = 10;
    2773          60 :             m_nRTreeBatchesBeforeStart = 1;
    2774             :         }
    2775             :     }
    2776         926 : }
    2777             : 
    2778             : /************************************************************************/
    2779             : /*                          StartAsyncRTree()                           */
    2780             : /************************************************************************/
    2781             : 
    2782             : // We create a temporary database with only the RTree, and we insert
    2783             : // records into it in a dedicated thread, in parallel of the main thread
    2784             : // that inserts rows in the user table. When the layer is finalized, we
    2785             : // just use bulk copy statements of the form
    2786             : // INSERT INTO rtree_xxxx_rowid/node/parent SELECT * FROM
    2787             : // temp_rtree.my_rtree_rowid/node/parent to copy the RTree auxiliary tables into
    2788             : // the main database, which is a very fast operation.
    2789             : 
    2790          48 : void OGRGeoPackageTableLayer::StartAsyncRTree()
    2791             : {
    2792          48 :     m_osAsyncDBName = m_poDS->GetDescription();
    2793          48 :     m_osAsyncDBName += ".tmp_rtree_";
    2794          48 :     bool bCanUseTableName = false;
    2795          48 :     if (strlen(m_pszTableName) <= 32)
    2796             :     {
    2797          36 :         bCanUseTableName = true;
    2798          36 :         constexpr char DIGIT_ZERO = '0';
    2799         144 :         for (int i = 0; m_pszTableName[i] != '\0'; ++i)
    2800             :         {
    2801         120 :             const char ch = m_pszTableName[i];
    2802         132 :             if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
    2803          12 :                   (ch >= DIGIT_ZERO && ch <= '9') || ch == '.' || ch == '_'))
    2804             :             {
    2805          12 :                 bCanUseTableName = false;
    2806          12 :                 break;
    2807             :             }
    2808             :         }
    2809             :     }
    2810          48 :     if (bCanUseTableName)
    2811          24 :         m_osAsyncDBName += m_pszTableName;
    2812             :     else
    2813             :     {
    2814          24 :         m_osAsyncDBName += CPLMD5String(m_pszTableName);
    2815             :     }
    2816          48 :     m_osAsyncDBName += ".db";
    2817             : 
    2818          48 :     m_osAsyncDBAttachName = "temp_rtree_";
    2819          48 :     m_osAsyncDBAttachName += CPLMD5String(m_pszTableName);
    2820             : 
    2821          48 :     VSIUnlink(m_osAsyncDBName.c_str());
    2822          48 :     CPLDebug("GPKG", "Creating background RTree DB %s",
    2823             :              m_osAsyncDBName.c_str());
    2824          72 :     if (sqlite3_open_v2(m_osAsyncDBName.c_str(), &m_hAsyncDBHandle,
    2825             :                         SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
    2826         120 :                         m_poDS->GetVFS() ? m_poDS->GetVFS()->zName : nullptr) !=
    2827             :         SQLITE_OK)
    2828             :     {
    2829           0 :         CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_open_v2() of %s failed",
    2830             :                  m_osAsyncDBName.c_str());
    2831           0 :         sqlite3_close(m_hAsyncDBHandle);
    2832           0 :         m_hAsyncDBHandle = nullptr;
    2833             :     }
    2834          48 :     if (m_hAsyncDBHandle != nullptr)
    2835             :     {
    2836             :         /* Make sure our auxiliary DB has the same page size as the main one.
    2837             :          * Because the number of RTree cells depends on the SQLite page size.
    2838             :          * However the sqlite implementation limits to 51 cells maximum per page,
    2839             :          * which is reached starting with a page size of 2048 bytes.
    2840             :          * As the default SQLite page size is 4096 currently, having potentially
    2841             :          * different page sizes >= 4096 between the main and auxiliary DBs would
    2842             :          * not be a practical issue, but better be consistent.
    2843             :          */
    2844             :         const int nPageSize =
    2845          48 :             SQLGetInteger(m_poDS->GetDB(), "PRAGMA page_size", nullptr);
    2846             : 
    2847          48 :         if (SQLCommand(m_hAsyncDBHandle,
    2848             :                        CPLSPrintf("PRAGMA page_size = %d;\n"
    2849             :                                   "PRAGMA journal_mode = OFF;\n"
    2850             :                                   "PRAGMA synchronous = OFF;",
    2851          48 :                                   nPageSize)) == OGRERR_NONE)
    2852             :         {
    2853          48 :             char *pszSQL = sqlite3_mprintf("ATTACH DATABASE '%q' AS '%q'",
    2854             :                                            m_osAsyncDBName.c_str(),
    2855             :                                            m_osAsyncDBAttachName.c_str());
    2856          48 :             OGRErr eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    2857          48 :             sqlite3_free(pszSQL);
    2858             : 
    2859          48 :             if (eErr == OGRERR_NONE)
    2860             :             {
    2861          48 :                 m_hRTree = gdal_sqlite_rtree_bl_new(nPageSize);
    2862             :                 try
    2863             :                 {
    2864             :                     m_oThreadRTree =
    2865          96 :                         std::thread([this]() { AsyncRTreeThreadFunction(); });
    2866          48 :                     m_bThreadRTreeStarted = true;
    2867             :                 }
    2868           0 :                 catch (const std::exception &e)
    2869             :                 {
    2870           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2871           0 :                              "RTree thread cannot be created: %s", e.what());
    2872             :                 }
    2873             :             }
    2874             :         }
    2875             : 
    2876          48 :         if (!m_bThreadRTreeStarted)
    2877             :         {
    2878           0 :             if (m_hRTree)
    2879             :             {
    2880           0 :                 gdal_sqlite_rtree_bl_free(m_hRTree);
    2881           0 :                 m_hRTree = nullptr;
    2882             :             }
    2883           0 :             m_oQueueRTreeEntries.clear();
    2884           0 :             m_bErrorDuringRTreeThread = true;
    2885           0 :             sqlite3_close(m_hAsyncDBHandle);
    2886           0 :             m_hAsyncDBHandle = nullptr;
    2887           0 :             VSIUnlink(m_osAsyncDBName.c_str());
    2888             :         }
    2889             :     }
    2890             :     else
    2891             :     {
    2892           0 :         m_oQueueRTreeEntries.clear();
    2893           0 :         m_bErrorDuringRTreeThread = true;
    2894             :     }
    2895          48 : }
    2896             : 
    2897             : /************************************************************************/
    2898             : /*                       RemoveAsyncRTreeTempDB()                       */
    2899             : /************************************************************************/
    2900             : 
    2901          48 : void OGRGeoPackageTableLayer::RemoveAsyncRTreeTempDB()
    2902             : {
    2903          48 :     if (!m_osAsyncDBAttachName.empty())
    2904             :     {
    2905          96 :         SQLCommand(
    2906          48 :             m_poDS->GetDB(),
    2907             :             CPLSPrintf("DETACH DATABASE \"%s\"",
    2908          96 :                        SQLEscapeName(m_osAsyncDBAttachName.c_str()).c_str()));
    2909          48 :         m_osAsyncDBAttachName.clear();
    2910          48 :         VSIUnlink(m_osAsyncDBName.c_str());
    2911          48 :         m_osAsyncDBName.clear();
    2912             :     }
    2913          48 : }
    2914             : 
    2915             : /************************************************************************/
    2916             : /*                          CancelAsyncRTree()                          */
    2917             : /************************************************************************/
    2918             : 
    2919          36 : void OGRGeoPackageTableLayer::CancelAsyncRTree()
    2920             : {
    2921          36 :     CPLDebug("GPKG", "Cancel background RTree creation");
    2922          36 :     m_oQueueRTreeEntries.push({});
    2923          36 :     m_oThreadRTree.join();
    2924          36 :     m_bThreadRTreeStarted = false;
    2925          36 :     if (m_hAsyncDBHandle)
    2926             :     {
    2927          36 :         sqlite3_close(m_hAsyncDBHandle);
    2928          36 :         m_hAsyncDBHandle = nullptr;
    2929             :     }
    2930          36 :     gdal_sqlite_rtree_bl_free(m_hRTree);
    2931          36 :     m_hRTree = nullptr;
    2932          36 :     m_bErrorDuringRTreeThread = true;
    2933          36 :     RemoveAsyncRTreeTempDB();
    2934          36 : }
    2935             : 
    2936             : /************************************************************************/
    2937             : /*                    FinishOrDisableThreadedRTree()                    */
    2938             : /************************************************************************/
    2939             : 
    2940          95 : void OGRGeoPackageTableLayer::FinishOrDisableThreadedRTree()
    2941             : {
    2942          95 :     if (m_bThreadRTreeStarted)
    2943             :     {
    2944          12 :         CreateSpatialIndexIfNecessary();
    2945             :     }
    2946          95 :     m_bAllowedRTreeThread = false;
    2947          95 : }
    2948             : 
    2949             : /************************************************************************/
    2950             : /*                         FlushInMemoryRTree()                         */
    2951             : /************************************************************************/
    2952             : 
    2953          12 : bool OGRGeoPackageTableLayer::FlushInMemoryRTree(sqlite3 *hRTreeDB,
    2954             :                                                  const char *pszRTreeName)
    2955             : {
    2956          12 :     if (hRTreeDB == m_hAsyncDBHandle)
    2957           8 :         SQLCommand(hRTreeDB, "BEGIN");
    2958             : 
    2959          12 :     char *pszErrMsg = nullptr;
    2960          12 :     bool bRet = gdal_sqlite_rtree_bl_serialize(m_hRTree, hRTreeDB, pszRTreeName,
    2961             :                                                "id", "minx", "miny", "maxx",
    2962             :                                                "maxy", &pszErrMsg);
    2963          12 :     if (hRTreeDB == m_hAsyncDBHandle)
    2964             :     {
    2965           8 :         if (bRet)
    2966           8 :             bRet = SQLCommand(hRTreeDB, "COMMIT") == OGRERR_NONE;
    2967             :         else
    2968           0 :             SQLCommand(hRTreeDB, "ROLLBACK");
    2969             :     }
    2970             : 
    2971          12 :     gdal_sqlite_rtree_bl_free(m_hRTree);
    2972          12 :     m_hRTree = nullptr;
    2973             : 
    2974          12 :     if (!bRet)
    2975             :     {
    2976           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2977             :                  "sqlite_rtree_bl_serialize() failed with %s",
    2978           0 :                  pszErrMsg ? pszErrMsg : "(null)");
    2979             : 
    2980           0 :         m_bErrorDuringRTreeThread = true;
    2981             : 
    2982           0 :         if (m_hAsyncDBHandle)
    2983             :         {
    2984           0 :             sqlite3_close(m_hAsyncDBHandle);
    2985           0 :             m_hAsyncDBHandle = nullptr;
    2986             :         }
    2987             : 
    2988           0 :         m_oQueueRTreeEntries.clear();
    2989             :     }
    2990          12 :     sqlite3_free(pszErrMsg);
    2991             : 
    2992          12 :     return bRet;
    2993             : }
    2994             : 
    2995             : /************************************************************************/
    2996             : /*                   GetMaxRAMUsageAllowedForRTree()                    */
    2997             : /************************************************************************/
    2998             : 
    2999         973 : static size_t GetMaxRAMUsageAllowedForRTree()
    3000             : {
    3001         973 :     const uint64_t nUsableRAM = CPLGetUsablePhysicalRAM();
    3002         973 :     uint64_t nMaxRAMUsageAllowed =
    3003         973 :         (nUsableRAM ? nUsableRAM / 10 : 100 * 1024 * 1024);
    3004             :     const char *pszMaxRAMUsageAllowed =
    3005         973 :         CPLGetConfigOption("OGR_GPKG_MAX_RAM_USAGE_RTREE", nullptr);
    3006         973 :     if (pszMaxRAMUsageAllowed)
    3007             :     {
    3008             :         nMaxRAMUsageAllowed = static_cast<uint64_t>(
    3009          20 :             std::strtoull(pszMaxRAMUsageAllowed, nullptr, 10));
    3010             :     }
    3011         973 :     if (nMaxRAMUsageAllowed > std::numeric_limits<size_t>::max() - 1U)
    3012             :     {
    3013           0 :         nMaxRAMUsageAllowed = std::numeric_limits<size_t>::max() - 1U;
    3014             :     }
    3015         973 :     return static_cast<size_t>(nMaxRAMUsageAllowed);
    3016             : }
    3017             : 
    3018             : /************************************************************************/
    3019             : /*                      AsyncRTreeThreadFunction()                      */
    3020             : /************************************************************************/
    3021             : 
    3022          48 : void OGRGeoPackageTableLayer::AsyncRTreeThreadFunction()
    3023             : {
    3024          48 :     CPLAssert(m_hRTree);
    3025             : 
    3026          48 :     const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
    3027          48 :     sqlite3_stmt *hStmt = nullptr;
    3028          48 :     GIntBig nCount = 0;
    3029             :     while (true)
    3030             :     {
    3031         884 :         const auto aoEntries = m_oQueueRTreeEntries.get_and_pop_front();
    3032         884 :         if (aoEntries.empty())
    3033          44 :             break;
    3034             : 
    3035         840 :         constexpr int NOTIFICATION_INTERVAL = 500 * 1000;
    3036             : 
    3037         840 :         auto oIter = aoEntries.begin();
    3038         840 :         if (m_hRTree)
    3039             :         {
    3040        4808 :             for (; oIter != aoEntries.end(); ++oIter)
    3041             :             {
    3042        4372 :                 const auto &entry = *oIter;
    3043        4372 :                 if (gdal_sqlite_rtree_bl_ram_usage(m_hRTree) >
    3044        8736 :                         nMaxRAMUsageAllowed ||
    3045        4364 :                     !gdal_sqlite_rtree_bl_insert(m_hRTree, entry.nId,
    3046        4364 :                                                  entry.fMinX, entry.fMinY,
    3047        4364 :                                                  entry.fMaxX, entry.fMaxY))
    3048             :                 {
    3049           8 :                     CPLDebug("GPKG", "Too large in-memory RTree. "
    3050             :                                      "Flushing it and using memory friendly "
    3051             :                                      "algorithm for the rest");
    3052           8 :                     if (!FlushInMemoryRTree(m_hAsyncDBHandle, "my_rtree"))
    3053           0 :                         return;
    3054           8 :                     break;
    3055             :                 }
    3056        4364 :                 ++nCount;
    3057        4364 :                 if ((nCount % NOTIFICATION_INTERVAL) == 0)
    3058             :                 {
    3059           0 :                     CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree",
    3060             :                              nCount);
    3061             :                 }
    3062             :             }
    3063         444 :             if (oIter == aoEntries.end())
    3064         436 :                 continue;
    3065             :         }
    3066             : 
    3067         404 :         if (hStmt == nullptr)
    3068             :         {
    3069             :             const char *pszInsertSQL =
    3070           8 :                 CPLGetConfigOption(
    3071             :                     "OGR_GPKG_SIMULATE_INSERT_INTO_MY_RTREE_PREPARATION_ERROR",
    3072             :                     nullptr)
    3073           8 :                     ? "INSERT INTO my_rtree_SIMULATE_ERROR VALUES (?,?,?,?,?)"
    3074           8 :                     : "INSERT INTO my_rtree VALUES (?,?,?,?,?)";
    3075           8 :             if (SQLPrepareWithError(m_hAsyncDBHandle, pszInsertSQL, -1, &hStmt,
    3076           8 :                                     nullptr) != SQLITE_OK)
    3077             :             {
    3078           4 :                 m_bErrorDuringRTreeThread = true;
    3079             : 
    3080           4 :                 sqlite3_close(m_hAsyncDBHandle);
    3081           4 :                 m_hAsyncDBHandle = nullptr;
    3082             : 
    3083           4 :                 m_oQueueRTreeEntries.clear();
    3084           4 :                 return;
    3085             :             }
    3086             : 
    3087           4 :             SQLCommand(m_hAsyncDBHandle, "BEGIN");
    3088             :         }
    3089             : 
    3090             : #ifdef DEBUG_VERBOSE
    3091             :         CPLDebug("GPKG",
    3092             :                  "AsyncRTreeThreadFunction(): "
    3093             :                  "Processing batch of %d features, "
    3094             :                  "starting at FID " CPL_FRMT_GIB " and ending "
    3095             :                  "at FID " CPL_FRMT_GIB,
    3096             :                  static_cast<int>(aoEntries.size()), aoEntries.front().nId,
    3097             :                  aoEntries.back().nId);
    3098             : #endif
    3099        4398 :         for (; oIter != aoEntries.end(); ++oIter)
    3100             :         {
    3101        3998 :             const auto &entry = *oIter;
    3102        3998 :             sqlite3_reset(hStmt);
    3103             : 
    3104        3998 :             sqlite3_bind_int64(hStmt, 1, entry.nId);
    3105        3998 :             sqlite3_bind_double(hStmt, 2, entry.fMinX);
    3106        3998 :             sqlite3_bind_double(hStmt, 3, entry.fMaxX);
    3107        3998 :             sqlite3_bind_double(hStmt, 4, entry.fMinY);
    3108        3998 :             sqlite3_bind_double(hStmt, 5, entry.fMaxY);
    3109        3998 :             int sqlite_err = sqlite3_step(hStmt);
    3110        3998 :             if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
    3111             :             {
    3112           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3113             :                          "failed to execute insertion in RTree : %s",
    3114             :                          sqlite3_errmsg(m_hAsyncDBHandle));
    3115           0 :                 m_bErrorDuringRTreeThread = true;
    3116           0 :                 break;
    3117             :             }
    3118        3998 :             ++nCount;
    3119        3998 :             if ((nCount % NOTIFICATION_INTERVAL) == 0)
    3120             :             {
    3121           0 :                 CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree", nCount);
    3122           0 :                 if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
    3123             :                 {
    3124           0 :                     m_bErrorDuringRTreeThread = true;
    3125           0 :                     break;
    3126             :                 }
    3127           0 :                 SQLCommand(m_hAsyncDBHandle, "BEGIN");
    3128             :             }
    3129             :         }
    3130         836 :     }
    3131          44 :     if (!m_hRTree)
    3132             :     {
    3133           4 :         if (m_bErrorDuringRTreeThread)
    3134             :         {
    3135           0 :             SQLCommand(m_hAsyncDBHandle, "ROLLBACK");
    3136             :         }
    3137           4 :         else if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
    3138             :         {
    3139           0 :             m_bErrorDuringRTreeThread = true;
    3140             :         }
    3141             : 
    3142           4 :         sqlite3_finalize(hStmt);
    3143             : 
    3144           4 :         if (m_bErrorDuringRTreeThread)
    3145             :         {
    3146           0 :             sqlite3_close(m_hAsyncDBHandle);
    3147           0 :             m_hAsyncDBHandle = nullptr;
    3148             : 
    3149           0 :             VSIUnlink(m_osAsyncDBName.c_str());
    3150             : 
    3151           0 :             m_oQueueRTreeEntries.clear();
    3152             :         }
    3153             :     }
    3154          44 :     CPLDebug("GPKG",
    3155             :              "AsyncRTreeThreadFunction(): " CPL_FRMT_GIB
    3156             :              " rows inserted into RTree",
    3157             :              nCount);
    3158             : }
    3159             : 
    3160             : /************************************************************************/
    3161             : /*                            ISetFeature()                             */
    3162             : /************************************************************************/
    3163             : 
    3164          59 : OGRErr OGRGeoPackageTableLayer::ISetFeature(OGRFeature *poFeature)
    3165             : {
    3166          59 :     if (!m_bFeatureDefnCompleted)
    3167           0 :         GetLayerDefn();
    3168          59 :     if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
    3169             :     {
    3170           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3171             :                  "SetFeature");
    3172           0 :         return OGRERR_FAILURE;
    3173             :     }
    3174             : 
    3175             :     /* No FID? */
    3176          59 :     if (poFeature->GetFID() == OGRNullFID)
    3177             :     {
    3178           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3179             :                  "FID required on features given to SetFeature().");
    3180           0 :         return OGRERR_FAILURE;
    3181             :     }
    3182             : 
    3183             :     /* In case the FID column has also been created as a regular field */
    3184          68 :     if (m_iFIDAsRegularColumnIndex >= 0 &&
    3185           9 :         !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
    3186             :     {
    3187           1 :         return OGRERR_FAILURE;
    3188             :     }
    3189             : 
    3190          58 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3191           0 :         return OGRERR_FAILURE;
    3192             : 
    3193          58 :     CancelAsyncNextArrowArray();
    3194             : 
    3195          58 :     if (m_bThreadRTreeStarted)
    3196          12 :         CancelAsyncRTree();
    3197          58 :     if (!RunDeferredSpatialIndexUpdate())
    3198           0 :         return OGRERR_FAILURE;
    3199             : 
    3200             :     const sqlite3_int64 nTotalChangesBefore =
    3201             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3202          58 :         sqlite3_total_changes64(m_poDS->GetDB());
    3203             : #else
    3204             :         sqlite3_total_changes(m_poDS->GetDB());
    3205             : #endif
    3206             : 
    3207          58 :     CheckGeometryType(poFeature);
    3208             : 
    3209          58 :     if (!m_osUpdateStatementSQL.empty())
    3210             :     {
    3211           1 :         m_osUpdateStatementSQL.clear();
    3212           1 :         if (m_poUpdateStatement)
    3213           1 :             sqlite3_finalize(m_poUpdateStatement);
    3214           1 :         m_poUpdateStatement = nullptr;
    3215             :     }
    3216          58 :     if (!m_poUpdateStatement)
    3217             :     {
    3218             :         /* Construct a SQL UPDATE statement from the OGRFeature */
    3219             :         /* Only work with fields that are set */
    3220             :         /* Do not stick values into SQL, use placeholder and bind values later
    3221             :          */
    3222          46 :         const std::string osCommand = FeatureGenerateUpdateSQL(poFeature);
    3223          46 :         if (osCommand.empty())
    3224           1 :             return OGRERR_NONE;
    3225             : 
    3226             :         /* Prepare the SQL into a statement */
    3227          45 :         int err = SQLPrepareWithError(m_poDS->GetDB(), osCommand.c_str(),
    3228          45 :                                       static_cast<int>(osCommand.size()),
    3229             :                                       &m_poUpdateStatement, nullptr);
    3230          45 :         if (err != SQLITE_OK)
    3231             :         {
    3232           1 :             return OGRERR_FAILURE;
    3233             :         }
    3234             :     }
    3235             : 
    3236             :     /* Bind values onto the statement now */
    3237          56 :     OGRErr errOgr = FeatureBindUpdateParameters(poFeature, m_poUpdateStatement);
    3238          56 :     if (errOgr != OGRERR_NONE)
    3239             :     {
    3240           0 :         sqlite3_reset(m_poUpdateStatement);
    3241           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3242           0 :         return errOgr;
    3243             :     }
    3244             : 
    3245             :     /* From here execute the statement and check errors */
    3246          56 :     int err = sqlite3_step(m_poUpdateStatement);
    3247          56 :     if (!(err == SQLITE_OK || err == SQLITE_DONE))
    3248             :     {
    3249           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
    3250           0 :                  sqlite3_errmsg(m_poDS->GetDB()));
    3251           0 :         sqlite3_reset(m_poUpdateStatement);
    3252           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3253           0 :         return OGRERR_FAILURE;
    3254             :     }
    3255             : 
    3256          56 :     sqlite3_reset(m_poUpdateStatement);
    3257          56 :     sqlite3_clear_bindings(m_poUpdateStatement);
    3258             : 
    3259             :     const sqlite3_int64 nTotalChangesAfter =
    3260             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3261          56 :         sqlite3_total_changes64(m_poDS->GetDB());
    3262             : #else
    3263             :         sqlite3_total_changes(m_poDS->GetDB());
    3264             : #endif
    3265             : 
    3266             :     /* Only update the envelope if we changed something */
    3267          56 :     OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
    3268          56 :                       ? OGRERR_NONE
    3269             :                       : OGRERR_NON_EXISTING_FEATURE;
    3270          56 :     if (eErr == OGRERR_NONE)
    3271             :     {
    3272             :         /* Update the layer extents with this new object */
    3273          50 :         if (IsGeomFieldSet(poFeature))
    3274             :         {
    3275          36 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
    3276          36 :             if (!poGeom->IsEmpty())
    3277             :             {
    3278          36 :                 OGREnvelope oEnv;
    3279          36 :                 poGeom->getEnvelope(&oEnv);
    3280          36 :                 UpdateExtent(&oEnv);
    3281             :             }
    3282             :         }
    3283             : 
    3284          50 :         m_bContentChanged = true;
    3285             :     }
    3286             : 
    3287             :     /* All done! */
    3288          56 :     return eErr;
    3289             : }
    3290             : 
    3291             : /************************************************************************/
    3292             : /*                           IUpsertFeature()                           */
    3293             : /************************************************************************/
    3294             : 
    3295          27 : OGRErr OGRGeoPackageTableLayer::IUpsertFeature(OGRFeature *poFeature)
    3296             : 
    3297             : {
    3298          27 :     return CreateOrUpsertFeature(poFeature, /* bUpsert = */ true);
    3299             : }
    3300             : 
    3301             : //----------------------------------------------------------------------
    3302             : // FeatureGenerateUpdateSQL()
    3303             : //
    3304             : // Build a SQL UPDATE statement that references all the columns in
    3305             : // the OGRFeatureDefn that the user asked to be updated, then prepare it for
    3306             : // repeated use in a prepared statement. All statements start off with geometry
    3307             : // (if it exists, and if it is asked to be updated), then reference each column
    3308             : // in the order it appears in the OGRFeatureDefn.
    3309             : // FeatureBindParameters operates on the expectation of this
    3310             : // column ordering.
    3311             : 
    3312             : //
    3313          11 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
    3314             :     const OGRFeature *poFeature, int nUpdatedFieldsCount,
    3315             :     const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
    3316             :     const int * /*panUpdatedGeomFieldsIdx*/) const
    3317             : {
    3318          11 :     bool bNeedComma = false;
    3319          11 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
    3320             : 
    3321             :     /* Set up our SQL string basics */
    3322          22 :     std::string osUpdate("UPDATE \"");
    3323          11 :     osUpdate += SQLEscapeName(m_pszTableName);
    3324          11 :     osUpdate += "\" SET ";
    3325             : 
    3326          11 :     if (nUpdatedGeomFieldsCount == 1 && poFeatureDefn->GetGeomFieldCount() > 0)
    3327             :     {
    3328           2 :         osUpdate += '"';
    3329             :         osUpdate +=
    3330           2 :             SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
    3331           2 :         osUpdate += "\"=?";
    3332           2 :         bNeedComma = true;
    3333             :     }
    3334             : 
    3335             :     /* Add attribute column names (except FID) to the SQL */
    3336          20 :     for (int i = 0; i < nUpdatedFieldsCount; i++)
    3337             :     {
    3338           9 :         const int iField = panUpdatedFieldsIdx[i];
    3339          18 :         if (iField == m_iFIDAsRegularColumnIndex ||
    3340           9 :             poFeatureDefn->GetFieldDefn(iField)->IsGenerated())
    3341           0 :             continue;
    3342           9 :         if (!poFeature->IsFieldSet(iField))
    3343           1 :             continue;
    3344           8 :         if (!bNeedComma)
    3345           8 :             bNeedComma = true;
    3346             :         else
    3347           0 :             osUpdate += ", ";
    3348             : 
    3349           8 :         osUpdate += '"';
    3350             :         osUpdate +=
    3351           8 :             SQLEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
    3352           8 :         osUpdate += "\"=?";
    3353             :     }
    3354          11 :     if (!bNeedComma)
    3355           1 :         return CPLString();
    3356             : 
    3357          10 :     osUpdate += " WHERE \"";
    3358          10 :     osUpdate += SQLEscapeName(m_pszFidColumn);
    3359          10 :     osUpdate += "\" = ?";
    3360             : 
    3361          10 :     return osUpdate;
    3362             : }
    3363             : 
    3364             : /************************************************************************/
    3365             : /*                           UpdateFeature()                            */
    3366             : /************************************************************************/
    3367             : 
    3368          11 : OGRErr OGRGeoPackageTableLayer::IUpdateFeature(
    3369             :     OGRFeature *poFeature, int nUpdatedFieldsCount,
    3370             :     const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
    3371             :     const int *panUpdatedGeomFieldsIdx, bool /* bUpdateStyleString*/)
    3372             : 
    3373             : {
    3374          11 :     if (!m_bFeatureDefnCompleted)
    3375           0 :         GetLayerDefn();
    3376          11 :     if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
    3377             :     {
    3378           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3379             :                  "UpdateFeature");
    3380           0 :         return OGRERR_FAILURE;
    3381             :     }
    3382             : 
    3383             :     /* No FID? */
    3384          11 :     if (poFeature->GetFID() == OGRNullFID)
    3385             :     {
    3386           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3387             :                  "FID required on features given to SetFeature().");
    3388           0 :         return OGRERR_FAILURE;
    3389             :     }
    3390             : 
    3391             :     /* In case the FID column has also been created as a regular field */
    3392          11 :     if (m_iFIDAsRegularColumnIndex >= 0 &&
    3393           0 :         !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
    3394             :     {
    3395           0 :         return OGRERR_FAILURE;
    3396             :     }
    3397             : 
    3398          11 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3399           0 :         return OGRERR_FAILURE;
    3400             : 
    3401          11 :     CancelAsyncNextArrowArray();
    3402             : 
    3403          11 :     if (m_bThreadRTreeStarted)
    3404           0 :         CancelAsyncRTree();
    3405          11 :     if (!RunDeferredSpatialIndexUpdate())
    3406           0 :         return OGRERR_FAILURE;
    3407             : 
    3408          11 :     CheckGeometryType(poFeature);
    3409             : 
    3410             :     /* Construct a SQL UPDATE statement from the OGRFeature */
    3411             :     /* Only work with fields that are set */
    3412             :     /* Do not stick values into SQL, use placeholder and bind values later
    3413             :      */
    3414             :     std::string osUpdateStatementSQL = FeatureGenerateUpdateSQL(
    3415             :         poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
    3416          22 :         nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
    3417          11 :     if (osUpdateStatementSQL.empty())
    3418           1 :         return OGRERR_NONE;
    3419             : 
    3420          10 :     if (m_osUpdateStatementSQL != osUpdateStatementSQL)
    3421             :     {
    3422           8 :         if (m_poUpdateStatement)
    3423           5 :             sqlite3_finalize(m_poUpdateStatement);
    3424           8 :         m_poUpdateStatement = nullptr;
    3425             :         /* Prepare the SQL into a statement */
    3426             :         int err =
    3427           8 :             SQLPrepareWithError(m_poDS->GetDB(), osUpdateStatementSQL.c_str(),
    3428           8 :                                 static_cast<int>(osUpdateStatementSQL.size()),
    3429             :                                 &m_poUpdateStatement, nullptr);
    3430           8 :         if (err != SQLITE_OK)
    3431             :         {
    3432           0 :             return OGRERR_FAILURE;
    3433             :         }
    3434           8 :         m_osUpdateStatementSQL = std::move(osUpdateStatementSQL);
    3435             :     }
    3436             : 
    3437             :     /* Bind values onto the statement now */
    3438          10 :     int nColCount = 0;
    3439             :     const OGRErr errOgr =
    3440          10 :         FeatureBindParameters(poFeature, m_poUpdateStatement, &nColCount, false,
    3441             :                               false, nUpdatedFieldsCount, panUpdatedFieldsIdx,
    3442             :                               nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
    3443          10 :     if (errOgr != OGRERR_NONE)
    3444             :     {
    3445           0 :         sqlite3_reset(m_poUpdateStatement);
    3446           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3447           0 :         return errOgr;
    3448             :     }
    3449             : 
    3450             :     // Bind the FID to the "WHERE" clause.
    3451             :     const int sqlite_err =
    3452          10 :         sqlite3_bind_int64(m_poUpdateStatement, nColCount, poFeature->GetFID());
    3453          10 :     if (sqlite_err != SQLITE_OK)
    3454             :     {
    3455           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3456             :                  "failed to bind FID '" CPL_FRMT_GIB "' to statement",
    3457             :                  poFeature->GetFID());
    3458           0 :         sqlite3_reset(m_poUpdateStatement);
    3459           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3460           0 :         return OGRERR_FAILURE;
    3461             :     }
    3462             : 
    3463             :     const sqlite3_int64 nTotalChangesBefore =
    3464             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3465          10 :         sqlite3_total_changes64(m_poDS->GetDB());
    3466             : #else
    3467             :         sqlite3_total_changes(m_poDS->GetDB());
    3468             : #endif
    3469             : 
    3470             :     /* From here execute the statement and check errors */
    3471          10 :     int err = sqlite3_step(m_poUpdateStatement);
    3472          10 :     if (!(err == SQLITE_OK || err == SQLITE_DONE))
    3473             :     {
    3474           0 :         CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
    3475           0 :                  sqlite3_errmsg(m_poDS->GetDB()));
    3476           0 :         sqlite3_reset(m_poUpdateStatement);
    3477           0 :         sqlite3_clear_bindings(m_poUpdateStatement);
    3478           0 :         return OGRERR_FAILURE;
    3479             :     }
    3480             : 
    3481          10 :     sqlite3_reset(m_poUpdateStatement);
    3482          10 :     sqlite3_clear_bindings(m_poUpdateStatement);
    3483             : 
    3484             :     const sqlite3_int64 nTotalChangesAfter =
    3485             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3486          10 :         sqlite3_total_changes64(m_poDS->GetDB());
    3487             : #else
    3488             :         sqlite3_total_changes(m_poDS->GetDB());
    3489             : #endif
    3490             : 
    3491             :     /* Only update the envelope if we changed something */
    3492          10 :     OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
    3493          10 :                       ? OGRERR_NONE
    3494             :                       : OGRERR_NON_EXISTING_FEATURE;
    3495          10 :     if (eErr == OGRERR_NONE)
    3496             :     {
    3497             :         /* Update the layer extents with this new object */
    3498           9 :         if (nUpdatedGeomFieldsCount == 1 && IsGeomFieldSet(poFeature))
    3499             :         {
    3500           1 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
    3501           1 :             if (!poGeom->IsEmpty())
    3502             :             {
    3503           1 :                 OGREnvelope oEnv;
    3504           1 :                 poGeom->getEnvelope(&oEnv);
    3505           1 :                 UpdateExtent(&oEnv);
    3506             :             }
    3507             :         }
    3508             : 
    3509           9 :         m_bContentChanged = true;
    3510             :     }
    3511             : 
    3512             :     /* All done! */
    3513          10 :     return eErr;
    3514             : }
    3515             : 
    3516             : /************************************************************************/
    3517             : /*                         SetAttributeFilter()                         */
    3518             : /************************************************************************/
    3519             : 
    3520          89 : OGRErr OGRGeoPackageTableLayer::SetAttributeFilter(const char *pszQuery)
    3521             : 
    3522             : {
    3523          89 :     if (!m_bFeatureDefnCompleted)
    3524           9 :         GetLayerDefn();
    3525          89 :     CPLFree(m_pszAttrQueryString);
    3526          89 :     m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
    3527             : 
    3528          89 :     if (pszQuery == nullptr)
    3529          40 :         osQuery = "";
    3530             :     else
    3531          49 :         osQuery = pszQuery;
    3532             : 
    3533          89 :     BuildWhere();
    3534             : 
    3535          89 :     ResetReading();
    3536             : 
    3537          89 :     return OGRERR_NONE;
    3538             : }
    3539             : 
    3540             : /************************************************************************/
    3541             : /*                            ResetReading()                            */
    3542             : /************************************************************************/
    3543             : 
    3544       25803 : void OGRGeoPackageTableLayer::ResetReading()
    3545             : {
    3546       25803 :     m_bEOF = false;
    3547       25803 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3548           0 :         return;
    3549             : 
    3550       25803 :     OGRGeoPackageLayer::ResetReading();
    3551             : 
    3552       25803 :     if (m_poInsertStatement)
    3553             :     {
    3554         158 :         sqlite3_finalize(m_poInsertStatement);
    3555         158 :         m_poInsertStatement = nullptr;
    3556             :     }
    3557             : 
    3558       25803 :     if (m_poUpdateStatement)
    3559             :     {
    3560          23 :         sqlite3_finalize(m_poUpdateStatement);
    3561          23 :         m_poUpdateStatement = nullptr;
    3562             :     }
    3563       25803 :     m_osUpdateStatementSQL.clear();
    3564             : 
    3565       25803 :     if (m_poGetFeatureStatement)
    3566             :     {
    3567          22 :         sqlite3_finalize(m_poGetFeatureStatement);
    3568          22 :         m_poGetFeatureStatement = nullptr;
    3569             :     }
    3570             : 
    3571       25803 :     CancelAsyncNextArrowArray();
    3572             : 
    3573       25803 :     m_bGetNextArrowArrayCalledSinceResetReading = false;
    3574             : 
    3575       25803 :     BuildColumns();
    3576             : }
    3577             : 
    3578             : /************************************************************************/
    3579             : /*                           SetNextByIndex()                           */
    3580             : /************************************************************************/
    3581             : 
    3582          17 : OGRErr OGRGeoPackageTableLayer::SetNextByIndex(GIntBig nIndex)
    3583             : {
    3584          17 :     if (nIndex < 0)
    3585             :     {
    3586           3 :         m_bEOF = true;
    3587           3 :         return OGRERR_NON_EXISTING_FEATURE;
    3588             :     }
    3589          14 :     if (m_soColumns.empty())
    3590           2 :         BuildColumns();
    3591          14 :     return ResetStatementInternal(nIndex);
    3592             : }
    3593             : 
    3594             : /************************************************************************/
    3595             : /*                           ResetStatement()                           */
    3596             : /************************************************************************/
    3597             : 
    3598         895 : OGRErr OGRGeoPackageTableLayer::ResetStatement()
    3599             : 
    3600             : {
    3601         895 :     return ResetStatementInternal(0);
    3602             : }
    3603             : 
    3604             : /************************************************************************/
    3605             : /*                       ResetStatementInternal()                       */
    3606             : /************************************************************************/
    3607             : 
    3608         909 : OGRErr OGRGeoPackageTableLayer::ResetStatementInternal(GIntBig nStartIndex)
    3609             : 
    3610             : {
    3611         909 :     ClearStatement();
    3612             : 
    3613             :     /* There is no active query statement set up, */
    3614             :     /* so job #1 is to prepare the statement. */
    3615             :     /* Append the attribute filter, if there is one */
    3616        1818 :     CPLString soSQL;
    3617         909 :     if (!m_soFilter.empty())
    3618             :     {
    3619             :         soSQL.Printf("SELECT %s FROM \"%s\" m WHERE %s", m_soColumns.c_str(),
    3620         280 :                      SQLEscapeName(m_pszTableName).c_str(), m_soFilter.c_str());
    3621             : 
    3622         545 :         if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
    3623         265 :             HasSpatialIndex())
    3624             :         {
    3625         264 :             OGREnvelope sEnvelope;
    3626             : 
    3627         264 :             m_poFilterGeom->getEnvelope(&sEnvelope);
    3628             : 
    3629         264 :             bool bUseSpatialIndex = true;
    3630         522 :             if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
    3631         169 :                 sEnvelope.MinY <= m_poExtent->MinY &&
    3632         662 :                 sEnvelope.MaxX >= m_poExtent->MaxX &&
    3633         140 :                 sEnvelope.MaxY >= m_poExtent->MaxY)
    3634             :             {
    3635             :                 // Selecting from spatial filter on whole extent can be rather
    3636             :                 // slow. So use function based filtering, just in case the
    3637             :                 // advertized global extent might be wrong. Otherwise we might
    3638             :                 // just discard completely the spatial filter.
    3639         137 :                 bUseSpatialIndex = false;
    3640             :             }
    3641             : 
    3642         127 :             if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
    3643         518 :                 !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
    3644         127 :                 !std::isinf(sEnvelope.MaxY))
    3645             :             {
    3646             :                 soSQL.Printf("SELECT %s FROM \"%s\" m "
    3647             :                              "JOIN \"%s\" r "
    3648             :                              "ON m.\"%s\" = r.id WHERE "
    3649             :                              "r.maxx >= %.12f AND r.minx <= %.12f AND "
    3650             :                              "r.maxy >= %.12f AND r.miny <= %.12f",
    3651             :                              m_soColumns.c_str(),
    3652         254 :                              SQLEscapeName(m_pszTableName).c_str(),
    3653         254 :                              SQLEscapeName(m_osRTreeName).c_str(),
    3654         254 :                              SQLEscapeName(m_osFIDForRTree).c_str(),
    3655         127 :                              sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    3656         508 :                              sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
    3657             :             }
    3658             :         }
    3659             :     }
    3660             :     else
    3661             :         soSQL.Printf("SELECT %s FROM \"%s\" m", m_soColumns.c_str(),
    3662         629 :                      SQLEscapeName(m_pszTableName).c_str());
    3663         909 :     if (nStartIndex > 0)
    3664             :     {
    3665          11 :         soSQL += CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB, nStartIndex);
    3666             :     }
    3667             : 
    3668         909 :     CPLDebug("GPKG", "ResetStatement(%s)", soSQL.c_str());
    3669             : 
    3670         909 :     int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
    3671             :                                   &m_poQueryStatement, nullptr);
    3672         909 :     if (err != SQLITE_OK)
    3673             :     {
    3674           1 :         return OGRERR_FAILURE;
    3675             :     }
    3676             : 
    3677         908 :     m_iNextShapeId = nStartIndex;
    3678         908 :     m_bGetNextArrowArrayCalledSinceResetReading = false;
    3679             : 
    3680         908 :     return OGRERR_NONE;
    3681             : }
    3682             : 
    3683             : /************************************************************************/
    3684             : /*                           GetNextFeature()                           */
    3685             : /************************************************************************/
    3686             : 
    3687       12198 : OGRFeature *OGRGeoPackageTableLayer::GetNextFeature()
    3688             : {
    3689       12198 :     if (m_bEOF)
    3690          40 :         return nullptr;
    3691       12158 :     if (!m_bFeatureDefnCompleted)
    3692          36 :         GetLayerDefn();
    3693       12158 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3694           0 :         return nullptr;
    3695             : 
    3696       12158 :     CancelAsyncNextArrowArray();
    3697             : 
    3698       12158 :     if (m_poFilterGeom != nullptr)
    3699             :     {
    3700             :         // Both are exclusive
    3701       10175 :         CreateSpatialIndexIfNecessary();
    3702       10175 :         if (!RunDeferredSpatialIndexUpdate())
    3703           0 :             return nullptr;
    3704             :     }
    3705             : 
    3706       12158 :     OGRFeature *poFeature = OGRGeoPackageLayer::GetNextFeature();
    3707       12158 :     if (poFeature && m_iFIDAsRegularColumnIndex >= 0)
    3708             :     {
    3709           1 :         poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
    3710             :     }
    3711       12158 :     return poFeature;
    3712             : }
    3713             : 
    3714             : /************************************************************************/
    3715             : /*                             GetFeature()                             */
    3716             : /************************************************************************/
    3717             : 
    3718        1302 : OGRFeature *OGRGeoPackageTableLayer::GetFeature(GIntBig nFID)
    3719             : {
    3720        1302 :     if (!m_bFeatureDefnCompleted)
    3721          11 :         GetLayerDefn();
    3722        1302 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3723           0 :         return nullptr;
    3724        1302 :     CancelAsyncNextArrowArray();
    3725             : 
    3726        1302 :     if (m_pszFidColumn == nullptr)
    3727           1 :         return OGRLayer::GetFeature(nFID);
    3728             : 
    3729        1301 :     if (m_poGetFeatureStatement == nullptr)
    3730             :     {
    3731          56 :         CPLString soSQL;
    3732             :         soSQL.Printf("SELECT %s FROM \"%s\" m "
    3733             :                      "WHERE \"%s\" = ?",
    3734         112 :                      m_soColumns.c_str(), SQLEscapeName(m_pszTableName).c_str(),
    3735         168 :                      SQLEscapeName(m_pszFidColumn).c_str());
    3736             : 
    3737          56 :         const int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
    3738             :                                             &m_poGetFeatureStatement, nullptr);
    3739          56 :         if (err != SQLITE_OK)
    3740             :         {
    3741           0 :             return nullptr;
    3742             :         }
    3743             :     }
    3744             : 
    3745        1301 :     CPL_IGNORE_RET_VAL(sqlite3_bind_int64(m_poGetFeatureStatement, 1, nFID));
    3746             : 
    3747             :     /* Should be only one or zero results */
    3748        1301 :     const int err = sqlite3_step(m_poGetFeatureStatement);
    3749             : 
    3750             :     /* Aha, got one */
    3751        1301 :     if (err == SQLITE_ROW)
    3752             :     {
    3753        1289 :         OGRFeature *poFeature = TranslateFeature(m_poGetFeatureStatement);
    3754        1289 :         if (m_iFIDAsRegularColumnIndex >= 0)
    3755             :         {
    3756           7 :             poFeature->SetField(m_iFIDAsRegularColumnIndex,
    3757             :                                 poFeature->GetFID());
    3758             :         }
    3759             : 
    3760        1289 :         sqlite3_reset(m_poGetFeatureStatement);
    3761        1289 :         sqlite3_clear_bindings(m_poGetFeatureStatement);
    3762             : 
    3763        1289 :         return poFeature;
    3764             :     }
    3765             : 
    3766          12 :     sqlite3_reset(m_poGetFeatureStatement);
    3767          12 :     sqlite3_clear_bindings(m_poGetFeatureStatement);
    3768             : 
    3769             :     /* Error out on all other return codes */
    3770          12 :     return nullptr;
    3771             : }
    3772             : 
    3773             : /************************************************************************/
    3774             : /*                           DeleteFeature()                            */
    3775             : /************************************************************************/
    3776             : 
    3777          60 : OGRErr OGRGeoPackageTableLayer::DeleteFeature(GIntBig nFID)
    3778             : {
    3779          60 :     if (!m_bFeatureDefnCompleted)
    3780           4 :         GetLayerDefn();
    3781          60 :     if (!m_poDS->GetUpdate())
    3782             :     {
    3783           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3784             :                  "DeleteFeature");
    3785           0 :         return OGRERR_FAILURE;
    3786             :     }
    3787          60 :     if (m_pszFidColumn == nullptr)
    3788             :     {
    3789           0 :         return OGRERR_FAILURE;
    3790             :     }
    3791             : 
    3792          60 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3793           0 :         return OGRERR_FAILURE;
    3794             : 
    3795          60 :     CancelAsyncNextArrowArray();
    3796             : 
    3797          60 :     if (m_bThreadRTreeStarted)
    3798          12 :         CancelAsyncRTree();
    3799             : 
    3800          60 :     if (!RunDeferredSpatialIndexUpdate())
    3801           0 :         return OGRERR_FAILURE;
    3802             : 
    3803             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    3804          60 :     if (m_bOGRFeatureCountTriggersEnabled)
    3805             :     {
    3806          17 :         DisableFeatureCountTriggers();
    3807             :     }
    3808             : #endif
    3809             : 
    3810             :     /* Clear out any existing query */
    3811          60 :     ResetReading();
    3812             : 
    3813             :     /* No filters apply, just use the FID */
    3814          60 :     CPLString soSQL;
    3815             :     soSQL.Printf("DELETE FROM \"%s\" WHERE \"%s\" = " CPL_FRMT_GIB,
    3816         120 :                  SQLEscapeName(m_pszTableName).c_str(),
    3817         180 :                  SQLEscapeName(m_pszFidColumn).c_str(), nFID);
    3818             : 
    3819             :     const sqlite3_int64 nTotalChangesBefore =
    3820             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3821          60 :         sqlite3_total_changes64(m_poDS->GetDB());
    3822             : #else
    3823             :         sqlite3_total_changes(m_poDS->GetDB());
    3824             : #endif
    3825             : 
    3826          60 :     OGRErr eErr = SQLCommand(m_poDS->GetDB(), soSQL.c_str());
    3827          60 :     if (eErr == OGRERR_NONE)
    3828             :     {
    3829             :         const sqlite3_int64 nTotalChangesAfter =
    3830             : #if SQLITE_VERSION_NUMBER >= 3037000L
    3831          59 :             sqlite3_total_changes64(m_poDS->GetDB());
    3832             : #else
    3833             :             sqlite3_total_changes(m_poDS->GetDB());
    3834             : #endif
    3835             : 
    3836          59 :         eErr = nTotalChangesAfter != nTotalChangesBefore
    3837          59 :                    ? OGRERR_NONE
    3838             :                    : OGRERR_NON_EXISTING_FEATURE;
    3839             : 
    3840          59 :         if (eErr == OGRERR_NONE)
    3841             :         {
    3842             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    3843          51 :             if (m_nTotalFeatureCount >= 0)
    3844          43 :                 m_nTotalFeatureCount--;
    3845             : #endif
    3846             : 
    3847          51 :             m_bContentChanged = true;
    3848             :         }
    3849             :     }
    3850          60 :     return eErr;
    3851             : }
    3852             : 
    3853             : /************************************************************************/
    3854             : /*                      DoJobAtTransactionCommit()                      */
    3855             : /************************************************************************/
    3856             : 
    3857         469 : bool OGRGeoPackageTableLayer::DoJobAtTransactionCommit()
    3858             : {
    3859         469 :     if (m_bAllowedRTreeThread)
    3860         315 :         return true;
    3861             : 
    3862         308 :     bool ret = RunDeferredCreationIfNecessary() == OGRERR_NONE &&
    3863         154 :                RunDeferredSpatialIndexUpdate();
    3864         154 :     m_nCountInsertInTransaction = 0;
    3865         154 :     m_aoRTreeTriggersSQL.clear();
    3866         154 :     m_aoRTreeEntries.clear();
    3867         154 :     return ret;
    3868             : }
    3869             : 
    3870             : /************************************************************************/
    3871             : /*                     DoJobAtTransactionRollback()                     */
    3872             : /************************************************************************/
    3873             : 
    3874          38 : bool OGRGeoPackageTableLayer::DoJobAtTransactionRollback()
    3875             : {
    3876          38 :     if (m_bThreadRTreeStarted)
    3877          12 :         CancelAsyncRTree();
    3878          38 :     m_nCountInsertInTransaction = 0;
    3879          38 :     m_aoRTreeTriggersSQL.clear();
    3880          38 :     m_aoRTreeEntries.clear();
    3881          38 :     if (m_bTableCreatedInTransaction)
    3882             :     {
    3883           2 :         SyncToDisk();
    3884             :     }
    3885             :     else
    3886             :     {
    3887          36 :         bool bDeferredSpatialIndexCreationBackup =
    3888             :             m_bDeferredSpatialIndexCreation;
    3889          36 :         m_bDeferredSpatialIndexCreation = false;
    3890          36 :         SyncToDisk();
    3891          36 :         m_bDeferredSpatialIndexCreation = bDeferredSpatialIndexCreationBackup;
    3892             :     }
    3893             : 
    3894          38 :     ResetReading();
    3895          38 :     return true;
    3896             : }
    3897             : 
    3898             : /************************************************************************/
    3899             : /*                  StartDeferredSpatialIndexUpdate()                   */
    3900             : /************************************************************************/
    3901             : 
    3902          10 : bool OGRGeoPackageTableLayer::StartDeferredSpatialIndexUpdate()
    3903             : {
    3904          10 :     if (m_poFeatureDefn->GetGeomFieldCount() == 0)
    3905           0 :         return true;
    3906             : 
    3907          10 :     RevertWorkaroundUpdate1TriggerIssue();
    3908             : 
    3909          10 :     m_aoRTreeTriggersSQL.clear();
    3910          10 :     m_aoRTreeEntries.clear();
    3911             : 
    3912          10 :     const char *pszT = m_pszTableName;
    3913          10 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    3914          10 :     m_osRTreeName = "rtree_";
    3915          10 :     m_osRTreeName += pszT;
    3916          10 :     m_osRTreeName += "_";
    3917          10 :     m_osRTreeName += pszC;
    3918             : 
    3919          90 :     char *pszSQL = sqlite3_mprintf(
    3920             :         "SELECT sql FROM sqlite_master WHERE type = 'trigger' "
    3921             :         "AND name IN ('%q', '%q', '%q', '%q', '%q', '%q', "
    3922             :         "'%q', '%q', '%q')",
    3923          20 :         (m_osRTreeName + "_insert").c_str(),
    3924          20 :         (m_osRTreeName + "_update1").c_str(),
    3925          20 :         (m_osRTreeName + "_update2").c_str(),
    3926          20 :         (m_osRTreeName + "_update3").c_str(),
    3927          20 :         (m_osRTreeName + "_update4").c_str(),
    3928             :         // update5 replaces update3 in GPKG 1.4
    3929             :         // cf https://github.com/opengeospatial/geopackage/pull/661
    3930          20 :         (m_osRTreeName + "_update5").c_str(),
    3931             :         // update6 and update7 replace update1 in GPKG 1.4
    3932             :         // cf https://github.com/opengeospatial/geopackage/pull/661
    3933          20 :         (m_osRTreeName + "_update6").c_str(),
    3934          20 :         (m_osRTreeName + "_update7").c_str(),
    3935          20 :         (m_osRTreeName + "_delete").c_str());
    3936          20 :     auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    3937          10 :     sqlite3_free(pszSQL);
    3938          10 :     if (oResult)
    3939             :     {
    3940          77 :         for (int iRecord = 0; iRecord < oResult->RowCount(); iRecord++)
    3941             :         {
    3942          67 :             const char *pszTriggerSQL = oResult->GetValue(0, iRecord);
    3943          67 :             if (pszTriggerSQL)
    3944             :             {
    3945          67 :                 m_aoRTreeTriggersSQL.push_back(pszTriggerSQL);
    3946             :             }
    3947             :         }
    3948             :     }
    3949          10 :     if (m_aoRTreeTriggersSQL.size() != 6 && m_aoRTreeTriggersSQL.size() != 7)
    3950             :     {
    3951           0 :         CPLDebug("GPKG", "Could not find expected RTree triggers");
    3952           0 :         m_aoRTreeTriggersSQL.clear();
    3953           0 :         return false;
    3954             :     }
    3955             : 
    3956          10 :     SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers());
    3957             : 
    3958          10 :     return true;
    3959             : }
    3960             : 
    3961             : /************************************************************************/
    3962             : /*                   FlushPendingSpatialIndexUpdate()                   */
    3963             : /************************************************************************/
    3964             : 
    3965           8 : bool OGRGeoPackageTableLayer::FlushPendingSpatialIndexUpdate()
    3966             : {
    3967           8 :     bool ret = true;
    3968             : 
    3969             :     // CPLDebug("GPKG", "Insert %d features in spatial index",
    3970             :     //          static_cast<int>(m_aoRTreeEntries.size()));
    3971             : 
    3972           8 :     const char *pszT = m_pszTableName;
    3973           8 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    3974             : 
    3975           8 :     m_osRTreeName = "rtree_";
    3976           8 :     m_osRTreeName += pszT;
    3977           8 :     m_osRTreeName += "_";
    3978           8 :     m_osRTreeName += pszC;
    3979             : 
    3980           8 :     char *pszSQL = sqlite3_mprintf("INSERT INTO \"%w\" VALUES (?,?,?,?,?)",
    3981             :                                    m_osRTreeName.c_str());
    3982           8 :     sqlite3_stmt *hInsertStmt = nullptr;
    3983           8 :     if (SQLPrepareWithError(m_poDS->GetDB(), pszSQL, -1, &hInsertStmt,
    3984           8 :                             nullptr) != SQLITE_OK)
    3985             :     {
    3986           0 :         sqlite3_free(pszSQL);
    3987           0 :         m_aoRTreeEntries.clear();
    3988           0 :         return false;
    3989             :     }
    3990           8 :     sqlite3_free(pszSQL);
    3991             : 
    3992        1196 :     for (size_t i = 0; i < m_aoRTreeEntries.size(); ++i)
    3993             :     {
    3994        1188 :         sqlite3_reset(hInsertStmt);
    3995             : 
    3996        1188 :         sqlite3_bind_int64(hInsertStmt, 1, m_aoRTreeEntries[i].nId);
    3997        1188 :         sqlite3_bind_double(hInsertStmt, 2, m_aoRTreeEntries[i].fMinX);
    3998        1188 :         sqlite3_bind_double(hInsertStmt, 3, m_aoRTreeEntries[i].fMaxX);
    3999        1188 :         sqlite3_bind_double(hInsertStmt, 4, m_aoRTreeEntries[i].fMinY);
    4000        1188 :         sqlite3_bind_double(hInsertStmt, 5, m_aoRTreeEntries[i].fMaxY);
    4001        1188 :         int sqlite_err = sqlite3_step(hInsertStmt);
    4002        1188 :         if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
    4003             :         {
    4004           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4005             :                      "failed to execute insertion in RTree : %s",
    4006           0 :                      sqlite3_errmsg(m_poDS->GetDB()));
    4007           0 :             ret = false;
    4008           0 :             break;
    4009             :         }
    4010             :     }
    4011           8 :     sqlite3_finalize(hInsertStmt);
    4012           8 :     m_aoRTreeEntries.clear();
    4013           8 :     return ret;
    4014             : }
    4015             : 
    4016             : /************************************************************************/
    4017             : /*                   RunDeferredSpatialIndexUpdate()                    */
    4018             : /************************************************************************/
    4019             : 
    4020       39562 : bool OGRGeoPackageTableLayer::RunDeferredSpatialIndexUpdate()
    4021             : {
    4022       39562 :     m_nCountInsertInTransaction = 0;
    4023       39562 :     if (m_aoRTreeTriggersSQL.empty())
    4024       39554 :         return true;
    4025             : 
    4026           8 :     bool ret = FlushPendingSpatialIndexUpdate();
    4027             : 
    4028           8 :     RevertWorkaroundUpdate1TriggerIssue();
    4029             : 
    4030          62 :     for (const auto &osSQL : m_aoRTreeTriggersSQL)
    4031             :     {
    4032          54 :         ret &= SQLCommand(m_poDS->GetDB(), osSQL) == OGRERR_NONE;
    4033             :     }
    4034           8 :     m_aoRTreeTriggersSQL.clear();
    4035           8 :     return ret;
    4036             : }
    4037             : 
    4038             : /************************************************************************/
    4039             : /*                             SyncToDisk()                             */
    4040             : /************************************************************************/
    4041             : 
    4042       11096 : OGRErr OGRGeoPackageTableLayer::SyncToDisk()
    4043             : {
    4044       11096 :     if (!m_bFeatureDefnCompleted)
    4045        5145 :         return OGRERR_NONE;
    4046             : 
    4047        5951 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4048           0 :         return OGRERR_FAILURE;
    4049             : 
    4050             :     // Both are exclusive
    4051        5951 :     CreateSpatialIndexIfNecessary();
    4052        5951 :     if (!RunDeferredSpatialIndexUpdate())
    4053           0 :         return OGRERR_FAILURE;
    4054        5951 :     RevertWorkaroundUpdate1TriggerIssue();
    4055             : 
    4056             :     /* Save metadata back to the database */
    4057        5951 :     SaveExtent();
    4058        5951 :     SaveTimestamp();
    4059             : 
    4060             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4061        5951 :     CreateFeatureCountTriggers();
    4062             : #endif
    4063             : 
    4064        5951 :     return OGRERR_NONE;
    4065             : }
    4066             : 
    4067             : /************************************************************************/
    4068             : /*                          StartTransaction()                          */
    4069             : /************************************************************************/
    4070             : 
    4071         129 : OGRErr OGRGeoPackageTableLayer::StartTransaction()
    4072             : {
    4073         129 :     CancelAsyncNextArrowArray();
    4074         129 :     return m_poDS->StartTransaction();
    4075             : }
    4076             : 
    4077             : /************************************************************************/
    4078             : /*                         CommitTransaction()                          */
    4079             : /************************************************************************/
    4080             : 
    4081          92 : OGRErr OGRGeoPackageTableLayer::CommitTransaction()
    4082             : {
    4083          92 :     return m_poDS->CommitTransaction();
    4084             : }
    4085             : 
    4086             : /************************************************************************/
    4087             : /*                        RollbackTransaction()                         */
    4088             : /************************************************************************/
    4089             : 
    4090          18 : OGRErr OGRGeoPackageTableLayer::RollbackTransaction()
    4091             : {
    4092          18 :     return m_poDS->RollbackTransaction();
    4093             : }
    4094             : 
    4095             : /************************************************************************/
    4096             : /*                        GetTotalFeatureCount()                        */
    4097             : /************************************************************************/
    4098             : 
    4099         605 : GIntBig OGRGeoPackageTableLayer::GetTotalFeatureCount()
    4100             : {
    4101             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4102         605 :     if (m_nTotalFeatureCount < 0 && m_poDS->m_bHasGPKGOGRContents)
    4103             :     {
    4104             :         char *pszSQL =
    4105          20 :             sqlite3_mprintf("SELECT feature_count FROM gpkg_ogr_contents WHERE "
    4106             :                             "lower(table_name) = lower('%q') LIMIT 2",
    4107             :                             m_pszTableName);
    4108          40 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4109          20 :         sqlite3_free(pszSQL);
    4110          20 :         if (oResult && oResult->RowCount() == 1)
    4111             :         {
    4112          16 :             const char *pszFeatureCount = oResult->GetValue(0, 0);
    4113          16 :             if (pszFeatureCount)
    4114             :             {
    4115           7 :                 m_nTotalFeatureCount =
    4116           7 :                     std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
    4117             :                 // According to https://sqlite.org/limits.html
    4118             :                 // "A 281 terabytes database can hold no more than approximately 2e+13 rows"
    4119             :                 // We do that check to avoid returning values close to INT64_MAX
    4120           7 :                 if (m_nTotalFeatureCount >
    4121             :                     static_cast<GIntBig>(30) * 1000 * 1000 * 1000 * 1000)
    4122             :                 {
    4123           0 :                     CPLDebug("GPKG", "Invalid value in feature_count field of "
    4124             :                                      "gpkg_ogr_contents");
    4125           0 :                     m_nTotalFeatureCount = -1;
    4126             :                 }
    4127             :             }
    4128             :         }
    4129             :     }
    4130         605 :     return m_nTotalFeatureCount;
    4131             : #else
    4132             :     return 0;
    4133             : #endif
    4134             : }
    4135             : 
    4136             : /************************************************************************/
    4137             : /*                          GetFeatureCount()                           */
    4138             : /************************************************************************/
    4139             : 
    4140       23132 : GIntBig OGRGeoPackageTableLayer::GetFeatureCount(int /*bForce*/)
    4141             : {
    4142       23132 :     if (!m_bFeatureDefnCompleted)
    4143         107 :         GetLayerDefn();
    4144             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4145       23132 :     if (m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr)
    4146             :     {
    4147         252 :         const auto nCount = GetTotalFeatureCount();
    4148         252 :         if (nCount >= 0)
    4149         239 :             return nCount;
    4150             :     }
    4151             : #endif
    4152             : 
    4153       22893 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4154           0 :         return 0;
    4155             : 
    4156       22893 :     CancelAsyncNextArrowArray();
    4157             : 
    4158       22893 :     if (m_poFilterGeom != nullptr)
    4159             :     {
    4160             :         // Both are exclusive
    4161       22874 :         CreateSpatialIndexIfNecessary();
    4162       22874 :         if (!RunDeferredSpatialIndexUpdate())
    4163           0 :             return -1;
    4164             :     }
    4165             : 
    4166             :     /* Ignore bForce, because we always do a full count on the database */
    4167             :     OGRErr err;
    4168       45786 :     CPLString soSQL;
    4169       22893 :     bool bUnregisterSQLFunction = false;
    4170       22892 :     if (m_bIsTable && m_poFilterGeom != nullptr &&
    4171       45785 :         m_pszAttrQueryString == nullptr && HasSpatialIndex())
    4172             :     {
    4173       22866 :         OGREnvelope sEnvelope;
    4174             : 
    4175       22866 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    4176             : 
    4177       45732 :         if (!std::isinf(sEnvelope.MinX) && !std::isinf(sEnvelope.MinY) &&
    4178       45732 :             !std::isinf(sEnvelope.MaxX) && !std::isinf(sEnvelope.MaxY))
    4179             :         {
    4180             :             soSQL.Printf("SELECT COUNT(*) FROM \"%s\" WHERE "
    4181             :                          "maxx >= %.12f AND minx <= %.12f AND "
    4182             :                          "maxy >= %.12f AND miny <= %.12f",
    4183       45732 :                          SQLEscapeName(m_osRTreeName).c_str(),
    4184       22866 :                          sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    4185       45732 :                          sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
    4186             : 
    4187       45732 :             if (OGRGeometryFactory::haveGEOS() &&
    4188       22866 :                 !(m_bFilterIsEnvelope &&
    4189       22866 :                   wkbFlatten(
    4190             :                       m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
    4191             :                           ->GetType()) == wkbPoint))
    4192             :             {
    4193       22854 :                 bUnregisterSQLFunction = true;
    4194       22854 :                 sqlite3_create_function(
    4195       22854 :                     m_poDS->hDB, "OGR_GPKG_Intersects_Spatial_Filter", 1,
    4196             :                     SQLITE_UTF8, this, OGR_GPKG_Intersects_Spatial_Filter,
    4197             :                     nullptr, nullptr);
    4198             :                 const char *pszC =
    4199       22854 :                     m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
    4200       22854 :                         ->GetNameRef();
    4201             :                 soSQL.Printf("SELECT COUNT(*) FROM \"%s\" m "
    4202             :                              "JOIN \"%s\" r "
    4203             :                              "ON m.\"%s\" = r.id WHERE "
    4204             :                              "r.maxx >= %.12f AND r.minx <= %.12f AND "
    4205             :                              "r.maxy >= %.12f AND r.miny <= %.12f AND "
    4206             :                              "OGR_GPKG_Intersects_Spatial_Filter(m.\"%s\")",
    4207       45708 :                              SQLEscapeName(m_pszTableName).c_str(),
    4208       45708 :                              SQLEscapeName(m_osRTreeName).c_str(),
    4209       45708 :                              SQLEscapeName(m_osFIDForRTree).c_str(),
    4210       22854 :                              sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    4211       22854 :                              sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11,
    4212      114270 :                              SQLEscapeName(pszC).c_str());
    4213             :             }
    4214             :         }
    4215             :     }
    4216             : 
    4217       22893 :     if (soSQL.empty())
    4218             :     {
    4219          27 :         if (!m_soFilter.empty())
    4220             :             soSQL.Printf("SELECT Count(*) FROM \"%s\" WHERE %s",
    4221          28 :                          SQLEscapeName(m_pszTableName).c_str(),
    4222          28 :                          m_soFilter.c_str());
    4223             :         else
    4224             :             soSQL.Printf("SELECT Count(*) FROM \"%s\"",
    4225          13 :                          SQLEscapeName(m_pszTableName).c_str());
    4226             :     }
    4227             : 
    4228             :     /* Just run the query directly and get back integer */
    4229             :     GIntBig iFeatureCount =
    4230       22893 :         SQLGetInteger64(m_poDS->GetDB(), soSQL.c_str(), &err);
    4231             : 
    4232       22893 :     if (bUnregisterSQLFunction)
    4233             :     {
    4234       22854 :         sqlite3_create_function(m_poDS->hDB,
    4235             :                                 "OGR_GPKG_Intersects_Spatial_Filter", 1,
    4236             :                                 SQLITE_UTF8, this, nullptr, nullptr, nullptr);
    4237             :     }
    4238             : 
    4239             :     /* Generic implementation uses -1 for error condition, so we will too */
    4240       22893 :     if (err == OGRERR_NONE)
    4241             :     {
    4242             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4243       22893 :         if (m_bIsTable && m_poFilterGeom == nullptr &&
    4244          19 :             m_pszAttrQueryString == nullptr)
    4245             :         {
    4246          13 :             m_nTotalFeatureCount = iFeatureCount;
    4247             : 
    4248          13 :             if (m_poDS->GetUpdate() && m_poDS->m_bHasGPKGOGRContents)
    4249             :             {
    4250             :                 const char *pszCount =
    4251           6 :                     CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
    4252           6 :                 char *pszSQL = sqlite3_mprintf(
    4253             :                     "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
    4254             :                     "lower(table_name )= lower('%q')",
    4255             :                     pszCount, m_pszTableName);
    4256           6 :                 SQLCommand(m_poDS->GetDB(), pszSQL);
    4257           6 :                 sqlite3_free(pszSQL);
    4258             :             }
    4259             :         }
    4260             : #endif
    4261       22893 :         return iFeatureCount;
    4262             :     }
    4263             :     else
    4264           0 :         return -1;
    4265             : }
    4266             : 
    4267             : /************************************************************************/
    4268             : /*                         GetExtentFromRTree()                         */
    4269             : /************************************************************************/
    4270             : 
    4271         155 : static bool GetExtentFromRTree(sqlite3 *hDB, const std::string &osRTreeName,
    4272             :                                double &minx, double &miny, double &maxx,
    4273             :                                double &maxy)
    4274             : {
    4275             :     // Cf https://github.com/sqlite/sqlite/blob/master/ext/rtree/rtree.c
    4276             :     // for the description of the content of the rtree _node table
    4277             :     // We fetch the root node (nodeno = 1) and iterates over its cells, to
    4278             :     // take the min/max of their minx/maxx/miny/maxy values.
    4279         155 :     char *pszSQL = sqlite3_mprintf(
    4280             :         "SELECT data FROM \"%w_node\" WHERE nodeno = 1", osRTreeName.c_str());
    4281         155 :     sqlite3_stmt *hStmt = nullptr;
    4282         155 :     CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(hDB, pszSQL, -1, &hStmt, nullptr));
    4283         155 :     sqlite3_free(pszSQL);
    4284         155 :     bool bOK = false;
    4285         155 :     if (hStmt)
    4286             :     {
    4287         310 :         if (sqlite3_step(hStmt) == SQLITE_ROW &&
    4288         155 :             sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
    4289             :         {
    4290         155 :             const int nBytes = sqlite3_column_bytes(hStmt, 0);
    4291             :             // coverity[tainted_data_return]
    4292             :             const GByte *pabyData =
    4293         155 :                 static_cast<const GByte *>(sqlite3_column_blob(hStmt, 0));
    4294         155 :             constexpr int BLOB_HEADER_SIZE = 4;
    4295         155 :             if (nBytes > BLOB_HEADER_SIZE)
    4296             :             {
    4297         155 :                 const int nCellCount = (pabyData[2] << 8) | pabyData[3];
    4298         155 :                 constexpr int SIZEOF_CELL = 24;  // int64_t + 4 float
    4299         155 :                 if (nCellCount >= 1 &&
    4300         148 :                     nBytes >= BLOB_HEADER_SIZE + SIZEOF_CELL * nCellCount)
    4301             :                 {
    4302         148 :                     minx = std::numeric_limits<double>::max();
    4303         148 :                     miny = std::numeric_limits<double>::max();
    4304         148 :                     maxx = -std::numeric_limits<double>::max();
    4305         148 :                     maxy = -std::numeric_limits<double>::max();
    4306         148 :                     size_t offset = BLOB_HEADER_SIZE;
    4307         486 :                     for (int i = 0; i < nCellCount; ++i)
    4308             :                     {
    4309         338 :                         offset += sizeof(int64_t);
    4310             : 
    4311             :                         float fMinX;
    4312         338 :                         memcpy(&fMinX, pabyData + offset, sizeof(float));
    4313         338 :                         offset += sizeof(float);
    4314         338 :                         CPL_MSBPTR32(&fMinX);
    4315         338 :                         minx = std::min(minx, static_cast<double>(fMinX));
    4316             : 
    4317             :                         float fMaxX;
    4318         338 :                         memcpy(&fMaxX, pabyData + offset, sizeof(float));
    4319         338 :                         offset += sizeof(float);
    4320         338 :                         CPL_MSBPTR32(&fMaxX);
    4321         338 :                         maxx = std::max(maxx, static_cast<double>(fMaxX));
    4322             : 
    4323             :                         float fMinY;
    4324         338 :                         memcpy(&fMinY, pabyData + offset, sizeof(float));
    4325         338 :                         offset += sizeof(float);
    4326         338 :                         CPL_MSBPTR32(&fMinY);
    4327         338 :                         miny = std::min(miny, static_cast<double>(fMinY));
    4328             : 
    4329             :                         float fMaxY;
    4330         338 :                         memcpy(&fMaxY, pabyData + offset, sizeof(float));
    4331         338 :                         offset += sizeof(float);
    4332         338 :                         CPL_MSBPTR32(&fMaxY);
    4333         338 :                         maxy = std::max(maxy, static_cast<double>(fMaxY));
    4334             :                     }
    4335             : 
    4336         148 :                     bOK = true;
    4337             :                 }
    4338             :             }
    4339             :         }
    4340         155 :         sqlite3_finalize(hStmt);
    4341             :     }
    4342         155 :     return bOK;
    4343             : }
    4344             : 
    4345             : /************************************************************************/
    4346             : /*                             IGetExtent()                             */
    4347             : /************************************************************************/
    4348             : 
    4349         334 : OGRErr OGRGeoPackageTableLayer::IGetExtent(int /* iGeomField  */,
    4350             :                                            OGREnvelope *psExtent, bool bForce)
    4351             : {
    4352         334 :     if (!m_bFeatureDefnCompleted)
    4353           0 :         GetLayerDefn();
    4354             :     /* Extent already calculated! We're done. */
    4355         334 :     if (m_poExtent != nullptr)
    4356             :     {
    4357         319 :         if (psExtent)
    4358             :         {
    4359         319 :             *psExtent = *m_poExtent;
    4360             :         }
    4361         319 :         return OGRERR_NONE;
    4362             :     }
    4363             : 
    4364          15 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4365           0 :         return OGRERR_FAILURE;
    4366             : 
    4367          15 :     CancelAsyncNextArrowArray();
    4368             : 
    4369          24 :     if (m_poFeatureDefn->GetGeomFieldCount() && HasSpatialIndex() &&
    4370           9 :         CPLTestBool(
    4371             :             CPLGetConfigOption("OGR_GPKG_USE_RTREE_FOR_GET_EXTENT", "TRUE")))
    4372             :     {
    4373           9 :         if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, psExtent->MinX,
    4374           9 :                                psExtent->MinY, psExtent->MaxX, psExtent->MaxY))
    4375             :         {
    4376           2 :             m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
    4377           2 :             m_bExtentChanged = true;
    4378           2 :             SaveExtent();
    4379           2 :             return OGRERR_NONE;
    4380             :         }
    4381             :         else
    4382             :         {
    4383           7 :             UpdateContentsToNullExtent();
    4384           7 :             return OGRERR_FAILURE;
    4385             :         }
    4386             :     }
    4387             : 
    4388             :     /* User is OK with expensive calculation */
    4389           6 :     if (bForce && m_poFeatureDefn->GetGeomFieldCount())
    4390             :     {
    4391             :         /* fall back to default implementation (scan all features) and save */
    4392             :         /* the result for later */
    4393           4 :         const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4394           4 :         char *pszSQL = sqlite3_mprintf(
    4395             :             "SELECT MIN(ST_MinX(\"%w\")), MIN(ST_MinY(\"%w\")), "
    4396             :             "MAX(ST_MaxX(\"%w\")), MAX(ST_MaxY(\"%w\")) FROM \"%w\" WHERE "
    4397             :             "\"%w\" IS NOT NULL AND NOT ST_IsEmpty(\"%w\")",
    4398             :             pszC, pszC, pszC, pszC, m_pszTableName, pszC, pszC);
    4399           8 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4400           4 :         sqlite3_free(pszSQL);
    4401           4 :         m_poExtent.reset();
    4402           8 :         if (oResult && oResult->RowCount() == 1 &&
    4403           4 :             oResult->GetValue(0, 0) != nullptr)
    4404             :         {
    4405           3 :             psExtent->MinX = CPLAtof(oResult->GetValue(0, 0));
    4406           3 :             psExtent->MinY = CPLAtof(oResult->GetValue(1, 0));
    4407           3 :             psExtent->MaxX = CPLAtof(oResult->GetValue(2, 0));
    4408           3 :             psExtent->MaxY = CPLAtof(oResult->GetValue(3, 0));
    4409           3 :             m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
    4410           3 :             m_bExtentChanged = true;
    4411           3 :             SaveExtent();
    4412             :         }
    4413             :         else
    4414             :         {
    4415           1 :             UpdateContentsToNullExtent();
    4416           1 :             return OGRERR_FAILURE;  // we didn't get an extent
    4417             :         }
    4418           3 :         return OGRERR_NONE;
    4419             :     }
    4420             : 
    4421           2 :     return OGRERR_FAILURE;
    4422             : }
    4423             : 
    4424             : /************************************************************************/
    4425             : /*                     UpdateContentsToNullExtent()                     */
    4426             : /************************************************************************/
    4427             : 
    4428           8 : void OGRGeoPackageTableLayer::UpdateContentsToNullExtent()
    4429             : {
    4430           8 :     if (m_poDS->GetUpdate())
    4431             :     {
    4432             :         char *pszSQL =
    4433           3 :             sqlite3_mprintf("UPDATE gpkg_contents SET "
    4434             :                             "min_x = NULL, min_y = NULL, "
    4435             :                             "max_x = NULL, max_y = NULL "
    4436             :                             "WHERE lower(table_name) = lower('%q') AND "
    4437             :                             "Lower(data_type) = 'features'",
    4438             :                             m_pszTableName);
    4439           3 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    4440           3 :         sqlite3_free(pszSQL);
    4441             :     }
    4442           8 :     m_bExtentChanged = false;
    4443           8 : }
    4444             : 
    4445             : /************************************************************************/
    4446             : /*                          RecomputeExtent()                           */
    4447             : /************************************************************************/
    4448             : 
    4449           4 : void OGRGeoPackageTableLayer::RecomputeExtent()
    4450             : {
    4451           4 :     m_bExtentChanged = true;
    4452           4 :     m_poExtent.reset();
    4453           4 :     OGREnvelope sExtent;
    4454           4 :     CPL_IGNORE_RET_VAL(GetExtent(&sExtent, true));
    4455           4 : }
    4456             : 
    4457             : /************************************************************************/
    4458             : /*                           TestCapability()                           */
    4459             : /************************************************************************/
    4460             : 
    4461        2396 : bool OGRGeoPackageTableLayer::TestCapability(const char *pszCap) const
    4462             : {
    4463        2396 :     if (!m_bFeatureDefnCompleted)
    4464          17 :         GetLayerDefn();
    4465        2396 :     if (EQUAL(pszCap, OLCSequentialWrite))
    4466             :     {
    4467          32 :         return m_poDS->GetUpdate();
    4468             :     }
    4469        2364 :     else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
    4470        2335 :              EQUAL(pszCap, OLCAlterFieldDefn) ||
    4471        2328 :              EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
    4472        2327 :              EQUAL(pszCap, OLCReorderFields) || EQUAL(pszCap, OLCRename))
    4473             :     {
    4474          45 :         return m_poDS->GetUpdate() && m_bIsTable;
    4475             :     }
    4476        2319 :     else if (EQUAL(pszCap, OLCDeleteFeature) ||
    4477        2312 :              EQUAL(pszCap, OLCUpsertFeature) ||
    4478        2306 :              EQUAL(pszCap, OLCUpdateFeature) || EQUAL(pszCap, OLCRandomWrite))
    4479             :     {
    4480          18 :         return m_poDS->GetUpdate() && m_pszFidColumn != nullptr;
    4481             :     }
    4482        2301 :     else if (EQUAL(pszCap, OLCRandomRead))
    4483             :     {
    4484           6 :         return m_pszFidColumn != nullptr;
    4485             :     }
    4486        2295 :     else if (EQUAL(pszCap, OLCTransactions))
    4487             :     {
    4488          17 :         return TRUE;
    4489             :     }
    4490             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    4491        2278 :     else if (EQUAL(pszCap, OLCFastFeatureCount))
    4492             :     {
    4493          26 :         return m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr &&
    4494          26 :                m_nTotalFeatureCount >= 0;
    4495             :     }
    4496             : #endif
    4497        2265 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    4498             :     {
    4499           3 :         return HasSpatialIndex() || m_bDeferredSpatialIndexCreation;
    4500             :     }
    4501        2262 :     else if (EQUAL(pszCap, OLCFastSetNextByIndex))
    4502             :     {
    4503             :         // Fast may not be that true on large layers, but better than the
    4504             :         // default implementation for sure...
    4505           0 :         return TRUE;
    4506             :     }
    4507        2262 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    4508             :     {
    4509           5 :         return (m_poExtent != nullptr);
    4510             :     }
    4511        2257 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    4512        1008 :         return TRUE;
    4513        1249 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
    4514        1005 :         return TRUE;
    4515         244 :     else if (EQUAL(pszCap, OLCZGeometries))
    4516         166 :         return TRUE;
    4517          78 :     if (EQUAL(pszCap, OLCFastGetExtent3D))
    4518           0 :         return TRUE;
    4519             :     else
    4520             :     {
    4521          78 :         return OGRGeoPackageLayer::TestCapability(pszCap);
    4522             :     }
    4523             : }
    4524             : 
    4525             : /************************************************************************/
    4526             : /*                   CreateSpatialIndexIfNecessary()                    */
    4527             : /************************************************************************/
    4528             : 
    4529       43622 : void OGRGeoPackageTableLayer::CreateSpatialIndexIfNecessary()
    4530             : {
    4531       43622 :     if (m_bDeferredSpatialIndexCreation)
    4532             :     {
    4533         926 :         CreateSpatialIndex();
    4534             :     }
    4535       43622 : }
    4536             : 
    4537             : /************************************************************************/
    4538             : /*                         CreateSpatialIndex()                         */
    4539             : /************************************************************************/
    4540             : 
    4541         936 : bool OGRGeoPackageTableLayer::CreateSpatialIndex(const char *pszTableName)
    4542             : {
    4543             :     OGRErr err;
    4544             : 
    4545         936 :     if (!m_bFeatureDefnCompleted)
    4546           2 :         GetLayerDefn();
    4547             : 
    4548         936 :     if (!CheckUpdatableTable("CreateSpatialIndex"))
    4549           1 :         return false;
    4550             : 
    4551         935 :     if (m_bDropRTreeTable)
    4552             :     {
    4553           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4554             :                  "Cannot run CreateSpatialIndex() after non-completed deferred "
    4555             :                  "DropSpatialIndex()");
    4556           0 :         return false;
    4557             :     }
    4558             : 
    4559         935 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    4560           0 :         return false;
    4561             : 
    4562         935 :     CancelAsyncNextArrowArray();
    4563             : 
    4564         935 :     m_bDeferredSpatialIndexCreation = false;
    4565             : 
    4566         935 :     if (m_pszFidColumn == nullptr)
    4567           0 :         return false;
    4568             : 
    4569         935 :     if (HasSpatialIndex())
    4570             :     {
    4571           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Spatial index already existing");
    4572           1 :         return false;
    4573             :     }
    4574             : 
    4575         934 :     if (m_poFeatureDefn->GetGeomFieldCount() == 0)
    4576             :     {
    4577           1 :         CPLError(CE_Failure, CPLE_AppDefined, "No geometry column");
    4578           1 :         return false;
    4579             :     }
    4580         933 :     if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
    4581           0 :         return false;
    4582             : 
    4583         933 :     const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
    4584         933 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4585         933 :     const char *pszI = GetFIDColumn();
    4586             : 
    4587         933 :     m_osRTreeName = "rtree_";
    4588         933 :     m_osRTreeName += pszT;
    4589         933 :     m_osRTreeName += "_";
    4590         933 :     m_osRTreeName += pszC;
    4591         933 :     m_osFIDForRTree = m_pszFidColumn;
    4592             : 
    4593         933 :     bool bPopulateFromThreadRTree = false;
    4594         933 :     if (m_bThreadRTreeStarted)
    4595             :     {
    4596          12 :         const bool bThreadHasFinished = m_oQueueRTreeEntries.empty();
    4597          12 :         if (!m_aoRTreeEntries.empty())
    4598           4 :             m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
    4599          12 :         m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
    4600          12 :         m_oQueueRTreeEntries.push(m_aoRTreeEntries);
    4601          12 :         if (!bThreadHasFinished)
    4602           1 :             CPLDebug("GPKG", "Waiting for background RTree building to finish");
    4603          12 :         m_oThreadRTree.join();
    4604          12 :         if (!bThreadHasFinished)
    4605             :         {
    4606           1 :             CPLDebug("GPKG", "Background RTree building finished");
    4607             :         }
    4608          12 :         m_bAllowedRTreeThread = false;
    4609          12 :         m_bThreadRTreeStarted = false;
    4610             : 
    4611          12 :         if (m_hAsyncDBHandle)
    4612             :         {
    4613           8 :             sqlite3_close(m_hAsyncDBHandle);
    4614           8 :             m_hAsyncDBHandle = nullptr;
    4615             :         }
    4616          12 :         if (m_bErrorDuringRTreeThread)
    4617             :         {
    4618           4 :             RemoveAsyncRTreeTempDB();
    4619             :         }
    4620             :         else
    4621             :         {
    4622           8 :             bPopulateFromThreadRTree = true;
    4623             :         }
    4624             :     }
    4625             : 
    4626         933 :     m_poDS->SoftStartTransaction();
    4627             : 
    4628         933 :     if (m_hRTree)
    4629             :     {
    4630           4 :         if (!FlushInMemoryRTree(m_poDS->GetDB(), m_osRTreeName.c_str()))
    4631             :         {
    4632           0 :             m_poDS->SoftRollbackTransaction();
    4633           0 :             return false;
    4634             :         }
    4635             :     }
    4636         929 :     else if (bPopulateFromThreadRTree)
    4637             :     {
    4638             :         /* Create virtual table */
    4639           4 :         char *pszSQL = sqlite3_mprintf("CREATE VIRTUAL TABLE \"%w\" USING "
    4640             :                                        "rtree(id, minx, maxx, miny, maxy)",
    4641             :                                        m_osRTreeName.c_str());
    4642           4 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    4643           4 :         sqlite3_free(pszSQL);
    4644           4 :         if (err != OGRERR_NONE)
    4645             :         {
    4646           0 :             m_poDS->SoftRollbackTransaction();
    4647           0 :             return false;
    4648             :         }
    4649             : 
    4650           4 :         pszSQL = sqlite3_mprintf(
    4651             :             "DELETE FROM \"%w_node\";\n"
    4652             :             "INSERT INTO \"%w_node\" SELECT * FROM \"%w\".my_rtree_node;\n"
    4653             :             "INSERT INTO \"%w_rowid\" SELECT * FROM "
    4654             :             "\"%w\".my_rtree_rowid;\n"
    4655             :             "INSERT INTO \"%w_parent\" SELECT * FROM "
    4656             :             "\"%w\".my_rtree_parent;\n",
    4657             :             m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    4658             :             m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
    4659             :             m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
    4660             :             m_osAsyncDBAttachName.c_str());
    4661           4 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    4662           4 :         sqlite3_free(pszSQL);
    4663           4 :         if (err != OGRERR_NONE)
    4664             :         {
    4665           0 :             m_poDS->SoftRollbackTransaction();
    4666           0 :             RemoveAsyncRTreeTempDB();
    4667           0 :             return false;
    4668             :         }
    4669             :     }
    4670             :     else
    4671             :     {
    4672             :         /* Populate the RTree */
    4673         925 :         const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
    4674         925 :         char *pszErrMsg = nullptr;
    4675             : 
    4676             :         struct ProgressCbk
    4677             :         {
    4678         602 :             static bool progressCbk(const char *pszMessage, void *)
    4679             :             {
    4680         602 :                 CPLDebug("GPKG", "%s", pszMessage);
    4681         602 :                 return true;
    4682             :             }
    4683             :         };
    4684             : 
    4685        1850 :         if (!gdal_sqlite_rtree_bl_from_feature_table(
    4686         925 :                 m_poDS->GetDB(), pszT, pszI, pszC, m_osRTreeName.c_str(), "id",
    4687             :                 "minx", "miny", "maxx", "maxy", nMaxRAMUsageAllowed, &pszErrMsg,
    4688             :                 ProgressCbk::progressCbk, nullptr))
    4689             :         {
    4690           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4691             :                      "gdal_sqlite_rtree_bl_from_feature_table() failed "
    4692             :                      "with %s",
    4693           0 :                      pszErrMsg ? pszErrMsg : "(null)");
    4694           0 :             m_poDS->SoftRollbackTransaction();
    4695           0 :             sqlite3_free(pszErrMsg);
    4696           0 :             return false;
    4697             :         }
    4698             :     }
    4699             : 
    4700        1866 :     CPLString osSQL;
    4701             : 
    4702             :     /* Register the table in gpkg_extensions */
    4703         933 :     char *pszSQL = sqlite3_mprintf(
    4704             :         "INSERT INTO gpkg_extensions "
    4705             :         "(table_name,column_name,extension_name,definition,scope) "
    4706             :         "VALUES ('%q', '%q', 'gpkg_rtree_index', "
    4707             :         "'http://www.geopackage.org/spec120/#extension_rtree', 'write-only')",
    4708             :         pszT, pszC);
    4709         933 :     osSQL += pszSQL;
    4710         933 :     sqlite3_free(pszSQL);
    4711             : 
    4712             :     /* Define Triggers to Maintain Spatial Index Values */
    4713         933 :     osSQL += ";" + ReturnSQLCreateSpatialIndexTriggers(pszTableName, nullptr);
    4714             : 
    4715         933 :     err = SQLCommand(m_poDS->GetDB(), osSQL);
    4716         933 :     if (err != OGRERR_NONE)
    4717             :     {
    4718           0 :         m_poDS->SoftRollbackTransaction();
    4719           0 :         if (bPopulateFromThreadRTree)
    4720             :         {
    4721           0 :             RemoveAsyncRTreeTempDB();
    4722             :         }
    4723           0 :         return false;
    4724             :     }
    4725             : 
    4726         933 :     m_poDS->SoftCommitTransaction();
    4727             : 
    4728         933 :     if (bPopulateFromThreadRTree)
    4729             :     {
    4730           8 :         RemoveAsyncRTreeTempDB();
    4731             :     }
    4732             : 
    4733         933 :     m_bHasSpatialIndex = true;
    4734             : 
    4735         933 :     return true;
    4736             : }
    4737             : 
    4738             : /************************************************************************/
    4739             : /*                   WorkaroundUpdate1TriggerIssue()                    */
    4740             : /************************************************************************/
    4741             : 
    4742          18 : void OGRGeoPackageTableLayer::WorkaroundUpdate1TriggerIssue()
    4743             : {
    4744             :     // Workaround issue of https://sqlite.org/forum/forumpost/8c8de6ff91
    4745             :     // Basically the official _update1 spatial index trigger doesn't work
    4746             :     // with current versions of SQLite when invoked from an UPSERT statement.
    4747             :     // In GeoPackage 1.4, the update6 and update7 triggers replace update1
    4748             : 
    4749          18 :     if (m_bHasUpdate6And7Triggers || m_poFeatureDefn->GetGeomFieldCount() == 0)
    4750          12 :         return;
    4751             : 
    4752          11 :     const char *pszT = m_pszTableName;
    4753          11 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4754          11 :     const char *pszI = GetFIDColumn();
    4755             : 
    4756          11 :     CPLString osRTreeName = "rtree_";
    4757          11 :     osRTreeName += pszT;
    4758          11 :     osRTreeName += "_";
    4759          11 :     osRTreeName += pszC;
    4760             : 
    4761             :     // Check if update6 and update7 triggers are there
    4762             :     {
    4763          22 :         char *pszSQL = sqlite3_mprintf(
    4764             :             "SELECT * FROM sqlite_master WHERE type = 'trigger' "
    4765             :             "AND name IN ('%q', '%q')",
    4766          22 :             (m_osRTreeName + "_update6").c_str(),
    4767          22 :             (m_osRTreeName + "_update7").c_str());
    4768          11 :         auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4769          11 :         sqlite3_free(pszSQL);
    4770          11 :         if (oResult && oResult->RowCount() == 2)
    4771             :         {
    4772           5 :             m_bHasUpdate6And7Triggers = true;
    4773           5 :             return;
    4774             :         }
    4775             :     }
    4776             : 
    4777             :     char *pszSQL =
    4778           6 :         sqlite3_mprintf("SELECT sql FROM sqlite_master WHERE type = 'trigger' "
    4779             :                         "AND name = '%q'",
    4780          12 :                         (m_osRTreeName + "_update1").c_str());
    4781           6 :     auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    4782           6 :     sqlite3_free(pszSQL);
    4783           6 :     if (oResult && oResult->RowCount() == 1)
    4784             :     {
    4785           6 :         const char *pszTriggerSQL = oResult->GetValue(0, 0);
    4786           6 :         if (pszTriggerSQL)
    4787             :         {
    4788           6 :             m_osUpdate1Trigger = pszTriggerSQL;
    4789             :         }
    4790             :     }
    4791           6 :     if (m_osUpdate1Trigger.empty())
    4792           0 :         return;
    4793             : 
    4794           6 :     m_bUpdate1TriggerDisabled = true;
    4795             : 
    4796             :     pszSQL =
    4797           6 :         sqlite3_mprintf("DROP TRIGGER \"%w_update1\"", osRTreeName.c_str());
    4798           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4799           6 :     sqlite3_free(pszSQL);
    4800             : 
    4801           6 :     pszSQL = sqlite3_mprintf(
    4802             :         "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
    4803             :         "ON \"%w\" "
    4804             :         "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4805             :         "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4806             :         "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
    4807             :         "BEGIN "
    4808             :         "UPDATE \"%w\" SET "
    4809             :         "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
    4810             :         "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
    4811             :         "WHERE id = NEW.\"%w\";"
    4812             :         "END",
    4813             :         osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4814             :         osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
    4815           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4816           6 :     sqlite3_free(pszSQL);
    4817             : 
    4818           6 :     pszSQL = sqlite3_mprintf(
    4819             :         "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
    4820             :         "\"%w\" "
    4821             :         "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4822             :         "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4823             :         "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
    4824             :         "BEGIN "
    4825             :         "INSERT INTO \"%w\" VALUES ("
    4826             :         "NEW.\"%w\","
    4827             :         "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4828             :         "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4829             :         "); "
    4830             :         "END",
    4831             :         osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4832             :         osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4833           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4834           6 :     sqlite3_free(pszSQL);
    4835             : }
    4836             : 
    4837             : /************************************************************************/
    4838             : /*                RevertWorkaroundUpdate1TriggerIssue()                 */
    4839             : /************************************************************************/
    4840             : 
    4841        5977 : void OGRGeoPackageTableLayer::RevertWorkaroundUpdate1TriggerIssue()
    4842             : {
    4843        5977 :     if (!m_bUpdate1TriggerDisabled)
    4844        5971 :         return;
    4845           6 :     m_bUpdate1TriggerDisabled = false;
    4846           6 :     CPLAssert(!m_bHasUpdate6And7Triggers);
    4847             : 
    4848           6 :     const char *pszT = m_pszTableName;
    4849           6 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4850             : 
    4851          12 :     CPLString osRTreeName = "rtree_";
    4852           6 :     osRTreeName += pszT;
    4853           6 :     osRTreeName += "_";
    4854           6 :     osRTreeName += pszC;
    4855             : 
    4856             :     char *pszSQL;
    4857             : 
    4858           6 :     SQLCommand(m_poDS->GetDB(), m_osUpdate1Trigger.c_str());
    4859           6 :     m_osUpdate1Trigger.clear();
    4860             : 
    4861             :     pszSQL =
    4862           6 :         sqlite3_mprintf("DROP TRIGGER \"%w_update6\"", osRTreeName.c_str());
    4863           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4864           6 :     sqlite3_free(pszSQL);
    4865             : 
    4866             :     pszSQL =
    4867           6 :         sqlite3_mprintf("DROP TRIGGER \"%w_update7\"", osRTreeName.c_str());
    4868           6 :     SQLCommand(m_poDS->GetDB(), pszSQL);
    4869           6 :     sqlite3_free(pszSQL);
    4870             : }
    4871             : 
    4872             : /************************************************************************/
    4873             : /*                ReturnSQLCreateSpatialIndexTriggers()                 */
    4874             : /************************************************************************/
    4875             : 
    4876         945 : CPLString OGRGeoPackageTableLayer::ReturnSQLCreateSpatialIndexTriggers(
    4877             :     const char *pszTableName, const char *pszGeomColName)
    4878             : {
    4879             :     char *pszSQL;
    4880         945 :     CPLString osSQL;
    4881             : 
    4882         945 :     const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
    4883             :     const char *pszC = (pszGeomColName)
    4884         945 :                            ? pszGeomColName
    4885         941 :                            : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    4886         945 :     const char *pszI = GetFIDColumn();
    4887             : 
    4888        1890 :     CPLString osRTreeName = "rtree_";
    4889         945 :     osRTreeName += pszT;
    4890         945 :     osRTreeName += "_";
    4891         945 :     osRTreeName += pszC;
    4892             : 
    4893             :     /* Conditions: Insertion of non-empty geometry
    4894             :        Actions   : Insert record into rtree */
    4895         945 :     pszSQL = sqlite3_mprintf(
    4896             :         "CREATE TRIGGER \"%w_insert\" AFTER INSERT ON \"%w\" "
    4897             :         "WHEN (new.\"%w\" NOT NULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
    4898             :         "BEGIN "
    4899             :         "INSERT OR REPLACE INTO \"%w\" VALUES ("
    4900             :         "NEW.\"%w\","
    4901             :         "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4902             :         "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4903             :         "); "
    4904             :         "END",
    4905             :         osRTreeName.c_str(), pszT, pszC, pszC, osRTreeName.c_str(), pszI, pszC,
    4906             :         pszC, pszC, pszC);
    4907         945 :     osSQL += pszSQL;
    4908         945 :     sqlite3_free(pszSQL);
    4909             : 
    4910         945 :     if (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
    4911         942 :         m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
    4912             :     {
    4913             :         /* Conditions: Update a non-empty geometry with another non-empty geometry
    4914             :            Actions   : Replace record from R-tree
    4915             :         */
    4916         931 :         pszSQL = sqlite3_mprintf(
    4917             :             "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
    4918             :             "ON \"%w\" "
    4919             :             "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4920             :             "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4921             :             "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
    4922             :             "BEGIN "
    4923             :             "UPDATE \"%w\" SET "
    4924             :             "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
    4925             :             "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
    4926             :             "WHERE id = NEW.\"%w\";"
    4927             :             "END",
    4928             :             osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4929             :             osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
    4930         931 :         osSQL += ";";
    4931         931 :         osSQL += pszSQL;
    4932         931 :         sqlite3_free(pszSQL);
    4933             : 
    4934             :         /* Conditions: Update a null/empty geometry with a non-empty geometry
    4935             :            Actions : Insert record into R-tree
    4936             :         */
    4937         931 :         pszSQL = sqlite3_mprintf(
    4938             :             "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
    4939             :             "\"%w\" "
    4940             :             "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4941             :             "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
    4942             :             "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
    4943             :             "BEGIN "
    4944             :             "INSERT INTO \"%w\" VALUES ("
    4945             :             "NEW.\"%w\","
    4946             :             "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4947             :             "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4948             :             "); "
    4949             :             "END",
    4950             :             osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
    4951             :             osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4952         931 :         osSQL += ";";
    4953         931 :         osSQL += pszSQL;
    4954         931 :         sqlite3_free(pszSQL);
    4955             :     }
    4956             :     else
    4957             :     {
    4958             :         /* Conditions: Update of geometry column to non-empty geometry
    4959             :                    No row ID change
    4960             :            Actions   : Update record in rtree */
    4961          14 :         pszSQL = sqlite3_mprintf(
    4962             :             "CREATE TRIGGER \"%w_update1\" AFTER UPDATE OF \"%w\" ON \"%w\" "
    4963             :             "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4964             :             "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
    4965             :             "BEGIN "
    4966             :             "INSERT OR REPLACE INTO \"%w\" VALUES ("
    4967             :             "NEW.\"%w\","
    4968             :             "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    4969             :             "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    4970             :             "); "
    4971             :             "END",
    4972             :             osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
    4973             :             osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    4974          14 :         osSQL += ";";
    4975          14 :         osSQL += pszSQL;
    4976          14 :         sqlite3_free(pszSQL);
    4977             :     }
    4978             : 
    4979             :     /* Conditions: Update of geometry column to empty geometry
    4980             :                No row ID change
    4981             :        Actions   : Remove record from rtree */
    4982         945 :     pszSQL = sqlite3_mprintf(
    4983             :         "CREATE TRIGGER \"%w_update2\" AFTER UPDATE OF \"%w\" ON \"%w\" "
    4984             :         "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
    4985             :         "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
    4986             :         "BEGIN "
    4987             :         "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
    4988             :         "END",
    4989             :         osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
    4990             :         osRTreeName.c_str(), pszI);
    4991         945 :     osSQL += ";";
    4992         945 :     osSQL += pszSQL;
    4993         945 :     sqlite3_free(pszSQL);
    4994             : 
    4995             :     /* Conditions: Update of any column
    4996             :                     Row ID change
    4997             :                     Non-empty geometry
    4998             :         Actions   : Remove record from rtree for old <i>
    4999             :                     Insert record into rtree for new <i> */
    5000             :     pszSQL =
    5001        1890 :         sqlite3_mprintf("CREATE TRIGGER \"%w_%s\" AFTER UPDATE ON \"%w\" "
    5002             :                         "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
    5003             :                         "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
    5004             :                         "BEGIN "
    5005             :                         "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
    5006             :                         "INSERT OR REPLACE INTO \"%w\" VALUES ("
    5007             :                         "NEW.\"%w\","
    5008             :                         "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
    5009             :                         "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
    5010             :                         "); "
    5011             :                         "END",
    5012             :                         osRTreeName.c_str(),
    5013         945 :                         (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
    5014         942 :                          m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
    5015             :                             ? "update5"
    5016             :                             : "update3",
    5017             :                         pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(), pszI,
    5018             :                         osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
    5019         945 :     osSQL += ";";
    5020         945 :     osSQL += pszSQL;
    5021         945 :     sqlite3_free(pszSQL);
    5022             : 
    5023             :     /* Conditions: Update of any column
    5024             :                     Row ID change
    5025             :                     Empty geometry
    5026             :         Actions   : Remove record from rtree for old and new <i> */
    5027         945 :     pszSQL = sqlite3_mprintf(
    5028             :         "CREATE TRIGGER \"%w_update4\" AFTER UPDATE ON \"%w\" "
    5029             :         "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
    5030             :         "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
    5031             :         "BEGIN "
    5032             :         "DELETE FROM \"%w\" WHERE id IN (OLD.\"%w\", NEW.\"%w\"); "
    5033             :         "END",
    5034             :         osRTreeName.c_str(), pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(),
    5035             :         pszI, pszI);
    5036         945 :     osSQL += ";";
    5037         945 :     osSQL += pszSQL;
    5038         945 :     sqlite3_free(pszSQL);
    5039             : 
    5040             :     /* Conditions: Row deleted
    5041             :         Actions   : Remove record from rtree for old <i> */
    5042         945 :     pszSQL = sqlite3_mprintf(
    5043             :         "CREATE TRIGGER \"%w_delete\" AFTER DELETE ON \"%w\" "
    5044             :         "WHEN old.\"%w\" NOT NULL "
    5045             :         "BEGIN "
    5046             :         "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
    5047             :         "END",
    5048             :         osRTreeName.c_str(), pszT, pszC, osRTreeName.c_str(), pszI);
    5049         945 :     osSQL += ";";
    5050         945 :     osSQL += pszSQL;
    5051         945 :     sqlite3_free(pszSQL);
    5052             : 
    5053        1890 :     return osSQL;
    5054             : }
    5055             : 
    5056             : /************************************************************************/
    5057             : /*                       CheckUnknownExtensions()                       */
    5058             : /************************************************************************/
    5059             : 
    5060        1074 : void OGRGeoPackageTableLayer::CheckUnknownExtensions()
    5061             : {
    5062             :     const std::map<CPLString, std::vector<GPKGExtensionDesc>> &oMap =
    5063        1074 :         m_poDS->GetUnknownExtensionsTableSpecific();
    5064        1074 :     const auto oIter = oMap.find(CPLString(m_pszTableName).toupper());
    5065        1074 :     if (oIter != oMap.end())
    5066             :     {
    5067          12 :         for (size_t i = 0; i < oIter->second.size(); i++)
    5068             :         {
    5069           6 :             const char *pszExtName = oIter->second[i].osExtensionName.c_str();
    5070           6 :             const char *pszDefinition = oIter->second[i].osDefinition.c_str();
    5071           6 :             const char *pszScope = oIter->second[i].osScope.c_str();
    5072           6 :             if (m_poDS->GetUpdate() && EQUAL(pszScope, "write-only"))
    5073             :             {
    5074           1 :                 CPLError(
    5075             :                     CE_Warning, CPLE_AppDefined,
    5076             :                     "Layer %s relies on the '%s' (%s) extension that should "
    5077             :                     "be implemented for safe write-support, but is not "
    5078             :                     "currently. "
    5079             :                     "Update of that layer are strongly discouraged to avoid "
    5080             :                     "corruption.",
    5081             :                     GetName(), pszExtName, pszDefinition);
    5082             :             }
    5083           5 :             else if (m_poDS->GetUpdate() && EQUAL(pszScope, "read-write"))
    5084             :             {
    5085           1 :                 CPLError(
    5086             :                     CE_Warning, CPLE_AppDefined,
    5087             :                     "Layer %s relies on the '%s' (%s) extension that should "
    5088             :                     "be implemented in order to read/write it safely, but is "
    5089             :                     "not currently. "
    5090             :                     "Some data may be missing while reading that layer, and "
    5091             :                     "updates are strongly discouraged.",
    5092             :                     GetName(), pszExtName, pszDefinition);
    5093             :             }
    5094           4 :             else if (EQUAL(pszScope, "read-write") &&
    5095             :                      // None of the NGA extensions at
    5096             :                      // http://ngageoint.github.io/GeoPackage/docs/extensions/
    5097             :                      // affect read-only scenarios
    5098           2 :                      !STARTS_WITH(pszExtName, "nga_"))
    5099             :             {
    5100           2 :                 CPLError(
    5101             :                     CE_Warning, CPLE_AppDefined,
    5102             :                     "Layer %s relies on the '%s' (%s) extension that should "
    5103             :                     "be implemented in order to read it safely, but is not "
    5104             :                     "currently. "
    5105             :                     "Some data may be missing while reading that layer.",
    5106             :                     GetName(), pszExtName, pszDefinition);
    5107             :             }
    5108             :         }
    5109             :     }
    5110        1074 : }
    5111             : 
    5112             : /************************************************************************/
    5113             : /*                 CreateGeometryExtensionIfNecessary()                 */
    5114             : /************************************************************************/
    5115             : 
    5116      264039 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
    5117             :     const OGRGeometry *poGeom)
    5118             : {
    5119      264039 :     bool bRet = true;
    5120      264039 :     if (poGeom != nullptr)
    5121             :     {
    5122      264039 :         OGRwkbGeometryType eGType = wkbFlatten(poGeom->getGeometryType());
    5123      264039 :         if (eGType >= wkbGeometryCollection)
    5124             :         {
    5125          24 :             if (eGType > wkbGeometryCollection)
    5126          12 :                 CreateGeometryExtensionIfNecessary(eGType);
    5127             :             const OGRGeometryCollection *poGC =
    5128          24 :                 dynamic_cast<const OGRGeometryCollection *>(poGeom);
    5129          24 :             if (poGC != nullptr)
    5130             :             {
    5131          13 :                 const int nSubGeoms = poGC->getNumGeometries();
    5132          34 :                 for (int i = 0; i < nSubGeoms; i++)
    5133             :                 {
    5134          21 :                     bRet &= CreateGeometryExtensionIfNecessary(
    5135          21 :                         poGC->getGeometryRef(i));
    5136             :                 }
    5137             :             }
    5138             :         }
    5139             :     }
    5140      264039 :     return bRet;
    5141             : }
    5142             : 
    5143             : /************************************************************************/
    5144             : /*                 CreateGeometryExtensionIfNecessary()                 */
    5145             : /************************************************************************/
    5146             : 
    5147          19 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
    5148             :     OGRwkbGeometryType eGType)
    5149             : {
    5150          19 :     eGType = wkbFlatten(eGType);
    5151          19 :     CPLAssert(eGType > wkbGeometryCollection && eGType <= wkbTriangle);
    5152          19 :     if (m_abHasGeometryExtension[eGType])
    5153           3 :         return true;
    5154             : 
    5155          16 :     if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
    5156           0 :         return false;
    5157             : 
    5158          16 :     const char *pszT = m_pszTableName;
    5159          16 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5160          16 :     const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
    5161             : 
    5162             :     // Check first if the extension isn't registered
    5163          16 :     char *pszSQL = sqlite3_mprintf(
    5164             :         "SELECT 1 FROM gpkg_extensions WHERE lower(table_name) = lower('%q') "
    5165             :         "AND "
    5166             :         "lower(column_name) = lower('%q') AND extension_name = 'gpkg_geom_%s'",
    5167             :         pszT, pszC, pszGeometryType);
    5168          16 :     const bool bExists = SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
    5169          16 :     sqlite3_free(pszSQL);
    5170             : 
    5171          16 :     if (!bExists)
    5172             :     {
    5173          15 :         if (eGType == wkbPolyhedralSurface || eGType == wkbTIN ||
    5174             :             eGType == wkbTriangle)
    5175             :         {
    5176           2 :             CPLError(CE_Warning, CPLE_AppDefined,
    5177             :                      "Registering non-standard gpkg_geom_%s extension",
    5178             :                      pszGeometryType);
    5179             :         }
    5180             : 
    5181             :         /* Register the table in gpkg_extensions */
    5182          15 :         pszSQL = sqlite3_mprintf(
    5183             :             "INSERT INTO gpkg_extensions "
    5184             :             "(table_name,column_name,extension_name,definition,scope) "
    5185             :             "VALUES ('%q', '%q', 'gpkg_geom_%s', "
    5186             :             "'http://www.geopackage.org/spec120/#extension_geometry_types', "
    5187             :             "'read-write')",
    5188             :             pszT, pszC, pszGeometryType);
    5189          15 :         OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
    5190          15 :         sqlite3_free(pszSQL);
    5191          15 :         if (err != OGRERR_NONE)
    5192           0 :             return false;
    5193             :     }
    5194             : 
    5195          16 :     m_abHasGeometryExtension[eGType] = true;
    5196          16 :     return true;
    5197             : }
    5198             : 
    5199             : /************************************************************************/
    5200             : /*                          HasSpatialIndex()                           */
    5201             : /************************************************************************/
    5202             : 
    5203       49149 : bool OGRGeoPackageTableLayer::HasSpatialIndex() const
    5204             : {
    5205       49149 :     if (!m_bFeatureDefnCompleted)
    5206           3 :         GetLayerDefn();
    5207       49149 :     if (m_bHasSpatialIndex >= 0)
    5208       47905 :         return CPL_TO_BOOL(m_bHasSpatialIndex);
    5209        1244 :     m_bHasSpatialIndex = false;
    5210             : 
    5211        3732 :     if (m_pszFidColumn == nullptr ||
    5212        2477 :         m_poFeatureDefn->GetGeomFieldCount() == 0 ||
    5213        1233 :         !m_poDS->HasExtensionsTable())
    5214         711 :         return false;
    5215             : 
    5216         533 :     const char *pszT = m_pszTableName;
    5217         533 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5218             :     CPLString osRTreeName(
    5219        1066 :         CPLString("rtree_").append(pszT).append("_").append(pszC));
    5220             :     const std::map<CPLString, CPLString> &oMap =
    5221         533 :         m_poDS->GetNameTypeMapFromSQliteMaster();
    5222         533 :     if (cpl::contains(oMap, CPLString(osRTreeName).toupper()))
    5223             :     {
    5224         281 :         m_bHasSpatialIndex = true;
    5225         281 :         m_osRTreeName = std::move(osRTreeName);
    5226         281 :         m_osFIDForRTree = m_pszFidColumn;
    5227             :     }
    5228             : 
    5229             :     // Add heuristics to try to detect corrupted RTree generated by GDAL 3.6.0
    5230             :     // Cf https://github.com/OSGeo/gdal/pull/6911
    5231         533 :     if (m_bHasSpatialIndex)
    5232             :     {
    5233             :         const auto nFC =
    5234         281 :             const_cast<OGRGeoPackageTableLayer *>(this)->GetTotalFeatureCount();
    5235         281 :         if (nFC >= atoi(CPLGetConfigOption(
    5236             :                        "OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100000")))
    5237             :         {
    5238           2 :             CPLString osSQL = "SELECT 1 FROM \"";
    5239           1 :             osSQL += SQLEscapeName(pszT);
    5240           1 :             osSQL += "\" WHERE \"";
    5241           2 :             osSQL += SQLEscapeName(
    5242           1 :                 const_cast<OGRGeoPackageTableLayer *>(this)->GetFIDColumn());
    5243           1 :             osSQL += "\" = ";
    5244           1 :             osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
    5245           1 :             osSQL += " AND \"";
    5246           1 :             osSQL += SQLEscapeName(pszC);
    5247           1 :             osSQL += "\" IS NOT NULL AND NOT ST_IsEmpty(\"";
    5248           1 :             osSQL += SQLEscapeName(pszC);
    5249           1 :             osSQL += "\")";
    5250           1 :             if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 1)
    5251             :             {
    5252           1 :                 osSQL = "SELECT 1 FROM \"";
    5253           1 :                 osSQL += SQLEscapeName(m_osRTreeName);
    5254           1 :                 osSQL += "\" WHERE id = ";
    5255           1 :                 osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
    5256           1 :                 if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 0)
    5257             :                 {
    5258           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    5259             :                              "Spatial index (perhaps created with GDAL 3.6.0) "
    5260             :                              "of table %s is corrupted. Disabling its use. "
    5261             :                              "This file should be recreated or its spatial "
    5262             :                              "index recreated",
    5263           1 :                              m_pszTableName);
    5264           1 :                     m_bHasSpatialIndex = false;
    5265             :                 }
    5266             :             }
    5267             :         }
    5268             :     }
    5269             : 
    5270         533 :     return CPL_TO_BOOL(m_bHasSpatialIndex);
    5271             : }
    5272             : 
    5273             : /************************************************************************/
    5274             : /*                          DropSpatialIndex()                          */
    5275             : /************************************************************************/
    5276             : 
    5277          43 : bool OGRGeoPackageTableLayer::DropSpatialIndex(bool bCalledFromSQLFunction)
    5278             : {
    5279          43 :     if (!m_bFeatureDefnCompleted)
    5280           0 :         GetLayerDefn();
    5281          43 :     if (!CheckUpdatableTable("DropSpatialIndex"))
    5282           1 :         return false;
    5283             : 
    5284          42 :     if (m_bDropRTreeTable)
    5285             :     {
    5286           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5287             :                  "Cannot run DropSpatialIndex() after non-completed deferred "
    5288             :                  "DropSpatialIndex()");
    5289           0 :         return false;
    5290             :     }
    5291             : 
    5292          42 :     if (!HasSpatialIndex())
    5293             :     {
    5294           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Spatial index not existing");
    5295           1 :         return false;
    5296             :     }
    5297             : 
    5298          41 :     const char *pszT = m_pszTableName;
    5299          41 :     const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5300             :     {
    5301          41 :         char *pszSQL = sqlite3_mprintf(
    5302             :             "DELETE FROM gpkg_extensions WHERE lower(table_name)=lower('%q') "
    5303             :             "AND lower(column_name)=lower('%q') AND "
    5304             :             "extension_name='gpkg_rtree_index'",
    5305             :             pszT, pszC);
    5306          41 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    5307          41 :         sqlite3_free(pszSQL);
    5308             :     }
    5309             : 
    5310          41 :     if (bCalledFromSQLFunction)
    5311             :     {
    5312             :         /* We cannot drop a table from a SQLite function call, so we just */
    5313             :         /* memorize that we will have to delete the table later */
    5314           6 :         m_bDropRTreeTable = true;
    5315             :     }
    5316             :     else
    5317             :     {
    5318             :         char *pszSQL =
    5319          35 :             sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
    5320          35 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    5321          35 :         sqlite3_free(pszSQL);
    5322             :     }
    5323             : 
    5324          41 :     m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
    5325             : 
    5326          41 :     SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers().c_str());
    5327             : 
    5328          41 :     m_bHasSpatialIndex = false;
    5329          41 :     return true;
    5330             : }
    5331             : 
    5332             : /************************************************************************/
    5333             : /*                RunDeferredDropRTreeTableIfNecessary()                */
    5334             : /************************************************************************/
    5335             : 
    5336        1680 : bool OGRGeoPackageTableLayer::RunDeferredDropRTreeTableIfNecessary()
    5337             : {
    5338        1680 :     bool ret = true;
    5339        1680 :     if (m_bDropRTreeTable)
    5340             :     {
    5341           6 :         OGRGeoPackageTableLayer::ResetReading();
    5342             : 
    5343             :         char *pszSQL =
    5344           6 :             sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
    5345           6 :         ret = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
    5346           6 :         sqlite3_free(pszSQL);
    5347           6 :         m_bDropRTreeTable = false;
    5348             :     }
    5349        1680 :     return ret;
    5350             : }
    5351             : 
    5352             : /************************************************************************/
    5353             : /*                 ReturnSQLDropSpatialIndexTriggers()                  */
    5354             : /************************************************************************/
    5355             : 
    5356          63 : CPLString OGRGeoPackageTableLayer::ReturnSQLDropSpatialIndexTriggers()
    5357             : {
    5358          63 :     char *pszSQL = sqlite3_mprintf(
    5359             :         "DROP TRIGGER \"%w_insert\";"
    5360             :         "DROP TRIGGER IF EXISTS \"%w_update1\";"  // replaced by update6 and update7 in GPKG 1.4
    5361             :         "DROP TRIGGER \"%w_update2\";"
    5362             :         "DROP TRIGGER IF EXISTS \"%w_update3\";"  // replace by update5 in GPKG 1.4
    5363             :         "DROP TRIGGER \"%w_update4\";"
    5364             :         "DROP TRIGGER IF EXISTS \"%w_update5\";"  // replace update3 in GPKG 1.4
    5365             :         "DROP TRIGGER IF EXISTS \"%w_update6\";"  // replace update1 in GPKG 1.4
    5366             :         "DROP TRIGGER IF EXISTS \"%w_update7\";"  // replace update1 in GPKG 1.4
    5367             :         "DROP TRIGGER \"%w_delete\";",
    5368             :         m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    5369             :         m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    5370             :         m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str());
    5371          63 :     CPLString osSQL(pszSQL);
    5372          63 :     sqlite3_free(pszSQL);
    5373             : 
    5374          63 :     return osSQL;
    5375             : }
    5376             : 
    5377             : /************************************************************************/
    5378             : /*                               Rename()                               */
    5379             : /************************************************************************/
    5380             : 
    5381          12 : OGRErr OGRGeoPackageTableLayer::Rename(const char *pszDstTableName)
    5382             : {
    5383          12 :     if (!m_bFeatureDefnCompleted)
    5384           2 :         GetLayerDefn();
    5385          12 :     if (!CheckUpdatableTable("Rename"))
    5386           0 :         return OGRERR_FAILURE;
    5387             : 
    5388          12 :     ResetReading();
    5389          12 :     SyncToDisk();
    5390             : 
    5391          12 :     char *pszSQL = sqlite3_mprintf(
    5392             :         "SELECT 1 FROM sqlite_master WHERE lower(name) = lower('%q') "
    5393             :         "AND type IN ('table', 'view')",
    5394             :         pszDstTableName);
    5395             :     const bool bAlreadyExists =
    5396          12 :         SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
    5397          12 :     sqlite3_free(pszSQL);
    5398          12 :     if (bAlreadyExists)
    5399             :     {
    5400           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
    5401             :                  pszDstTableName);
    5402           3 :         return OGRERR_FAILURE;
    5403             :     }
    5404             : 
    5405             :     // Temporary remove foreign key checks
    5406             :     const GPKGTemporaryForeignKeyCheckDisabler
    5407          18 :         oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
    5408             : 
    5409           9 :     if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    5410             :     {
    5411           0 :         return OGRERR_FAILURE;
    5412             :     }
    5413             : 
    5414             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    5415           9 :     DisableFeatureCountTriggers(false);
    5416             : #endif
    5417             : 
    5418          18 :     CPLString osSQL;
    5419             : 
    5420           9 :     pszSQL = sqlite3_mprintf(
    5421             :         "UPDATE gpkg_geometry_columns SET table_name = '%q' WHERE "
    5422             :         "lower(table_name )= lower('%q');",
    5423             :         pszDstTableName, m_pszTableName);
    5424           9 :     osSQL += pszSQL;
    5425           9 :     sqlite3_free(pszSQL);
    5426             : 
    5427             :     // Rename the identifier if it defaulted to the table name
    5428           9 :     pszSQL = sqlite3_mprintf(
    5429             :         "UPDATE gpkg_contents SET identifier = '%q' WHERE "
    5430             :         "lower(table_name) = lower('%q') AND identifier = '%q';",
    5431             :         pszDstTableName, m_pszTableName, m_pszTableName);
    5432           9 :     osSQL += pszSQL;
    5433           9 :     sqlite3_free(pszSQL);
    5434             : 
    5435           9 :     pszSQL = sqlite3_mprintf("UPDATE gpkg_contents SET table_name = '%q' WHERE "
    5436             :                              "lower(table_name )= lower('%q');",
    5437             :                              pszDstTableName, m_pszTableName);
    5438           9 :     osSQL += pszSQL;
    5439           9 :     sqlite3_free(pszSQL);
    5440             : 
    5441           9 :     if (m_poDS->HasExtensionsTable())
    5442             :     {
    5443           8 :         pszSQL = sqlite3_mprintf(
    5444             :             "UPDATE gpkg_extensions SET table_name = '%q' WHERE "
    5445             :             "lower(table_name )= lower('%q');",
    5446             :             pszDstTableName, m_pszTableName);
    5447           8 :         osSQL += pszSQL;
    5448           8 :         sqlite3_free(pszSQL);
    5449             :     }
    5450             : 
    5451           9 :     if (m_poDS->HasMetadataTables())
    5452             :     {
    5453           4 :         pszSQL = sqlite3_mprintf(
    5454             :             "UPDATE gpkg_metadata_reference SET table_name = '%q' WHERE "
    5455             :             "lower(table_name )= lower('%q');",
    5456             :             pszDstTableName, m_pszTableName);
    5457           4 :         osSQL += pszSQL;
    5458           4 :         sqlite3_free(pszSQL);
    5459             :     }
    5460             : 
    5461           9 :     if (m_poDS->HasDataColumnsTable())
    5462             :     {
    5463           1 :         pszSQL = sqlite3_mprintf(
    5464             :             "UPDATE gpkg_data_columns SET table_name = '%q' WHERE "
    5465             :             "lower(table_name )= lower('%q');",
    5466             :             pszDstTableName, m_pszTableName);
    5467           1 :         osSQL += pszSQL;
    5468           1 :         sqlite3_free(pszSQL);
    5469             :     }
    5470             : 
    5471             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    5472           9 :     if (m_poDS->m_bHasGPKGOGRContents)
    5473             :     {
    5474           9 :         pszSQL = sqlite3_mprintf(
    5475             :             "UPDATE gpkg_ogr_contents SET table_name = '%q' WHERE "
    5476             :             "lower(table_name )= lower('%q');",
    5477             :             pszDstTableName, m_pszTableName);
    5478           9 :         osSQL += pszSQL;
    5479           9 :         sqlite3_free(pszSQL);
    5480             :     }
    5481             : #endif
    5482             : 
    5483           9 :     if (m_poDS->HasGpkgextRelationsTable())
    5484             :     {
    5485           2 :         pszSQL = sqlite3_mprintf(
    5486             :             "UPDATE gpkgext_relations SET base_table_name = '%q' WHERE "
    5487             :             "lower(base_table_name )= lower('%q');",
    5488             :             pszDstTableName, m_pszTableName);
    5489           2 :         osSQL += pszSQL;
    5490           2 :         sqlite3_free(pszSQL);
    5491             : 
    5492           2 :         pszSQL = sqlite3_mprintf(
    5493             :             "UPDATE gpkgext_relations SET related_table_name = '%q' WHERE "
    5494             :             "lower(related_table_name )= lower('%q');",
    5495             :             pszDstTableName, m_pszTableName);
    5496           2 :         osSQL += pszSQL;
    5497             :         ;
    5498           2 :         sqlite3_free(pszSQL);
    5499             : 
    5500           2 :         pszSQL = sqlite3_mprintf(
    5501             :             "UPDATE gpkgext_relations SET mapping_table_name = '%q' WHERE "
    5502             :             "lower(mapping_table_name )= lower('%q');",
    5503             :             pszDstTableName, m_pszTableName);
    5504           2 :         osSQL += pszSQL;
    5505           2 :         sqlite3_free(pszSQL);
    5506             :     }
    5507             : 
    5508           9 :     if (m_poDS->HasQGISLayerStyles())
    5509             :     {
    5510             :         pszSQL =
    5511           1 :             sqlite3_mprintf("UPDATE layer_styles SET f_table_name = '%q' WHERE "
    5512             :                             "f_table_name = '%q';",
    5513             :                             pszDstTableName, m_pszTableName);
    5514           1 :         osSQL += pszSQL;
    5515           1 :         sqlite3_free(pszSQL);
    5516             :     }
    5517             : 
    5518           9 :     pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
    5519             :                              m_pszTableName, pszDstTableName);
    5520           9 :     osSQL += pszSQL;
    5521           9 :     sqlite3_free(pszSQL);
    5522             : 
    5523           9 :     const bool bHasSpatialIndex = HasSpatialIndex();
    5524           9 :     CPLString osRTreeNameNew;
    5525           9 :     if (bHasSpatialIndex)
    5526             :     {
    5527           8 :         osRTreeNameNew = "rtree_";
    5528           8 :         osRTreeNameNew += pszDstTableName;
    5529           8 :         osRTreeNameNew += "_";
    5530           8 :         osRTreeNameNew += m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    5531             : 
    5532           8 :         osSQL += ReturnSQLDropSpatialIndexTriggers();
    5533           8 :         osSQL += ';';
    5534             : 
    5535           8 :         pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
    5536             :                                  m_osRTreeName.c_str(), osRTreeNameNew.c_str());
    5537           8 :         osSQL += pszSQL;
    5538           8 :         sqlite3_free(pszSQL);
    5539             : 
    5540           8 :         osSQL += ReturnSQLCreateSpatialIndexTriggers(pszDstTableName, nullptr);
    5541             :     }
    5542             : 
    5543           9 :     OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL);
    5544             : 
    5545             :     // Check foreign key integrity
    5546           9 :     if (eErr == OGRERR_NONE)
    5547             :     {
    5548           9 :         eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
    5549             :     }
    5550             : 
    5551           9 :     if (eErr == OGRERR_NONE)
    5552             :     {
    5553             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    5554           9 :         CreateFeatureCountTriggers(pszDstTableName);
    5555             : #endif
    5556             : 
    5557           9 :         eErr = m_poDS->SoftCommitTransaction();
    5558           9 :         if (eErr == OGRERR_NONE)
    5559             :         {
    5560           9 :             m_poDS->RemoveTableFromSQLiteMasterCache(m_pszTableName);
    5561             : 
    5562           9 :             CPLFree(m_pszTableName);
    5563           9 :             m_pszTableName = CPLStrdup(pszDstTableName);
    5564             : 
    5565           9 :             if (bHasSpatialIndex)
    5566             :             {
    5567           8 :                 m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
    5568           8 :                 m_osRTreeName = std::move(osRTreeNameNew);
    5569             :             }
    5570             :         }
    5571             :     }
    5572             :     else
    5573             :     {
    5574           0 :         m_poDS->SoftRollbackTransaction();
    5575             :     }
    5576             : 
    5577           9 :     if (eErr == OGRERR_NONE)
    5578             :     {
    5579           9 :         m_poDS->ClearCachedRelationships();
    5580             : 
    5581           9 :         SetDescription(pszDstTableName);
    5582           9 :         whileUnsealing(m_poFeatureDefn)->SetName(pszDstTableName);
    5583             :     }
    5584             : 
    5585           9 :     return eErr;
    5586             : }
    5587             : 
    5588             : /************************************************************************/
    5589             : /*                         ISetSpatialFilter()                          */
    5590             : /************************************************************************/
    5591             : 
    5592       23181 : OGRErr OGRGeoPackageTableLayer::ISetSpatialFilter(int /*iGeomField*/,
    5593             :                                                   const OGRGeometry *poGeomIn)
    5594             : 
    5595             : {
    5596       23181 :     if (!m_bFeatureDefnCompleted)
    5597           0 :         GetLayerDefn();
    5598       23181 :     if (InstallFilter(poGeomIn))
    5599             :     {
    5600       23147 :         BuildWhere();
    5601             : 
    5602       23147 :         ResetReading();
    5603             :     }
    5604       23181 :     return OGRERR_NONE;
    5605             : }
    5606             : 
    5607             : /************************************************************************/
    5608             : /*                        HasFastSpatialFilter()                        */
    5609             : /************************************************************************/
    5610             : 
    5611           2 : bool OGRGeoPackageTableLayer::HasFastSpatialFilter(int m_iGeomColIn)
    5612             : {
    5613           4 :     if (m_iGeomColIn < 0 ||
    5614           2 :         m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
    5615           0 :         return false;
    5616           2 :     return HasSpatialIndex();
    5617             : }
    5618             : 
    5619             : /************************************************************************/
    5620             : /*                          GetSpatialWhere()                           */
    5621             : /************************************************************************/
    5622             : 
    5623       23256 : CPLString OGRGeoPackageTableLayer::GetSpatialWhere(int m_iGeomColIn,
    5624             :                                                    OGRGeometry *poFilterGeom)
    5625             : {
    5626       23256 :     CPLString osSpatialWHERE;
    5627             : 
    5628       46512 :     if (m_iGeomColIn < 0 ||
    5629       23256 :         m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
    5630           2 :         return osSpatialWHERE;
    5631             : 
    5632       23254 :     if (poFilterGeom != nullptr)
    5633             :     {
    5634       23161 :         OGREnvelope sEnvelope;
    5635             : 
    5636       23161 :         poFilterGeom->getEnvelope(&sEnvelope);
    5637             : 
    5638             :         const char *pszC =
    5639       23161 :             m_poFeatureDefn->GetGeomFieldDefn(m_iGeomColIn)->GetNameRef();
    5640             : 
    5641       23169 :         if (std::isinf(sEnvelope.MinX) && sEnvelope.MinX < 0 &&
    5642          12 :             std::isinf(sEnvelope.MinY) && sEnvelope.MinY < 0 &&
    5643          12 :             std::isinf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 &&
    5644       23169 :             std::isinf(sEnvelope.MaxY) && sEnvelope.MaxY > 0)
    5645             :         {
    5646             :             osSpatialWHERE.Printf(
    5647             :                 "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
    5648           4 :                 SQLEscapeName(pszC).c_str(), SQLEscapeName(pszC).c_str());
    5649         149 :             return osSpatialWHERE;
    5650             :         }
    5651             : 
    5652       23157 :         bool bUseSpatialIndex = true;
    5653       46296 :         if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
    5654         222 :             sEnvelope.MinY <= m_poExtent->MinY &&
    5655       46448 :             sEnvelope.MaxX >= m_poExtent->MaxX &&
    5656         152 :             sEnvelope.MaxY >= m_poExtent->MaxY)
    5657             :         {
    5658             :             // Selecting from spatial filter on whole extent can be rather
    5659             :             // slow. So use function based filtering, just in case the
    5660             :             // advertized global extent might be wrong. Otherwise we might
    5661             :             // just discard completely the spatial filter.
    5662         149 :             bUseSpatialIndex = false;
    5663             :         }
    5664             : 
    5665       23157 :         if (bUseSpatialIndex && HasSpatialIndex())
    5666             :         {
    5667             :             osSpatialWHERE.Printf(
    5668             :                 "\"%s\" IN ( SELECT id FROM \"%s\" WHERE "
    5669             :                 "maxx >= %.12f AND minx <= %.12f AND "
    5670             :                 "maxy >= %.12f AND miny <= %.12f)",
    5671       46010 :                 SQLEscapeName(m_osFIDForRTree).c_str(),
    5672       46010 :                 SQLEscapeName(m_osRTreeName).c_str(), sEnvelope.MinX - 1e-11,
    5673       23005 :                 sEnvelope.MaxX + 1e-11, sEnvelope.MinY - 1e-11,
    5674       69015 :                 sEnvelope.MaxY + 1e-11);
    5675             :         }
    5676             :         else
    5677             :         {
    5678         152 :             if (HasSpatialIndex())
    5679             :             {
    5680             :                 // If we do have a spatial index, and our filter contains the
    5681             :                 // bounding box of the RTree, then just filter on non-null
    5682             :                 // non-empty geometries.
    5683             :                 double minx, miny, maxx, maxy;
    5684         146 :                 if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, minx,
    5685         146 :                                        miny, maxx, maxy) &&
    5686         146 :                     sEnvelope.MinX <= minx && sEnvelope.MinY <= miny &&
    5687         292 :                     sEnvelope.MaxX >= maxx && sEnvelope.MaxY >= maxy)
    5688             :                 {
    5689             :                     osSpatialWHERE.Printf(
    5690             :                         "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
    5691         290 :                         SQLEscapeName(pszC).c_str(),
    5692         435 :                         SQLEscapeName(pszC).c_str());
    5693         145 :                     return osSpatialWHERE;
    5694             :                 }
    5695             :             }
    5696             : 
    5697             :             /* A bit inefficient but still faster than OGR filtering */
    5698             :             osSpatialWHERE.Printf(
    5699             :                 "ST_EnvelopesIntersects(\"%s\", %.12f, %.12f, %.12f, %.12f)",
    5700           7 :                 SQLEscapeName(pszC).c_str(), sEnvelope.MinX - 1e-11,
    5701           7 :                 sEnvelope.MinY - 1e-11, sEnvelope.MaxX + 1e-11,
    5702           7 :                 sEnvelope.MaxY + 1e-11);
    5703             :         }
    5704             :     }
    5705             : 
    5706       23105 :     return osSpatialWHERE;
    5707             : }
    5708             : 
    5709             : /************************************************************************/
    5710             : /*                             BuildWhere()                             */
    5711             : /*                                                                      */
    5712             : /*      Build the WHERE statement appropriate to the current set of     */
    5713             : /*      criteria (spatial and attribute queries).                       */
    5714             : /************************************************************************/
    5715             : 
    5716       23236 : void OGRGeoPackageTableLayer::BuildWhere()
    5717             : 
    5718             : {
    5719       23236 :     m_soFilter = "";
    5720             : 
    5721             :     CPLString osSpatialWHERE =
    5722       46472 :         GetSpatialWhere(m_iGeomFieldFilter, m_poFilterGeom);
    5723       23236 :     if (!osSpatialWHERE.empty())
    5724             :     {
    5725       23141 :         m_soFilter += osSpatialWHERE;
    5726             :     }
    5727             : 
    5728       23236 :     if (!osQuery.empty())
    5729             :     {
    5730          46 :         if (m_soFilter.empty())
    5731             :         {
    5732          40 :             m_soFilter += osQuery;
    5733             :         }
    5734             :         else
    5735             :         {
    5736           6 :             m_soFilter += " AND (";
    5737           6 :             m_soFilter += osQuery;
    5738           6 :             m_soFilter += ")";
    5739             :         }
    5740             :     }
    5741       23236 :     CPLDebug("GPKG", "Filter: %s", m_soFilter.c_str());
    5742       23236 : }
    5743             : 
    5744             : /************************************************************************/
    5745             : /*                        SetOpeningParameters()                        */
    5746             : /************************************************************************/
    5747             : 
    5748        3402 : void OGRGeoPackageTableLayer::SetOpeningParameters(
    5749             :     const char *pszTableName, const char *pszObjectType, bool bIsInGpkgContents,
    5750             :     bool bIsSpatial, const char *pszGeomColName, const char *pszGeomType,
    5751             :     bool bHasZ, bool bHasM)
    5752             : {
    5753        3402 :     CPLFree(m_pszTableName);
    5754        3402 :     m_pszTableName = CPLStrdup(pszTableName);
    5755        3402 :     m_bIsTable = EQUAL(pszObjectType, "table");
    5756        3402 :     m_bIsInGpkgContents = bIsInGpkgContents;
    5757        3402 :     m_bIsSpatial = bIsSpatial;
    5758        3402 :     if (pszGeomType)
    5759             :     {
    5760             :         OGRwkbGeometryType eType =
    5761        1277 :             GPkgGeometryTypeToWKB(pszGeomType, bHasZ, bHasM);
    5762        1277 :         m_poFeatureDefn->SetGeomType(eType);
    5763        1277 :         if (eType != wkbNone)
    5764             :         {
    5765        1277 :             m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName);
    5766             :         }
    5767             :     }
    5768        3402 : }
    5769             : 
    5770             : /************************************************************************/
    5771             : /*                       SetCreationParameters()                        */
    5772             : /************************************************************************/
    5773             : 
    5774        1030 : void OGRGeoPackageTableLayer::SetCreationParameters(
    5775             :     OGRwkbGeometryType eGType, const char *pszGeomColumnName, int bGeomNullable,
    5776             :     const OGRSpatialReference *poSRS, const char *pszSRID,
    5777             :     const OGRGeomCoordinatePrecision &oCoordPrec, bool bDiscardCoordLSB,
    5778             :     bool bUndoDiscardCoordLSBOnReading, const char *pszFIDColumnName,
    5779             :     const char *pszIdentifier, const char *pszDescription)
    5780             : {
    5781        1030 :     m_bIsSpatial = eGType != wkbNone;
    5782        1030 :     m_bIsInGpkgContents =
    5783        1112 :         m_bIsSpatial ||
    5784          82 :         !m_poDS->HasNonSpatialTablesNonRegisteredInGpkgContents();
    5785        1030 :     m_bFeatureDefnCompleted = true;
    5786        1030 :     m_bDeferredCreation = true;
    5787        1030 :     m_bTableCreatedInTransaction = m_poDS->IsInTransaction();
    5788        1030 :     m_bHasTriedDetectingFID64 = true;
    5789        1030 :     m_pszFidColumn = CPLStrdup(pszFIDColumnName);
    5790        1030 :     m_bUndoDiscardCoordLSBOnReading = bUndoDiscardCoordLSBOnReading;
    5791             : 
    5792        1030 :     if (eGType != wkbNone)
    5793             :     {
    5794         948 :         m_nZFlag = wkbHasZ(eGType) ? 1 : 0;
    5795         948 :         m_nMFlag = wkbHasM(eGType) ? 1 : 0;
    5796             : 
    5797        1896 :         OGRSpatialReferenceRefCountedPtr poGotSRS;
    5798        1896 :         OGRGeomFieldDefn oGeomFieldDefn(pszGeomColumnName, eGType);
    5799             : 
    5800         948 :         oGeomFieldDefn.SetSpatialRef(poSRS);
    5801         948 :         if (pszSRID)
    5802             :         {
    5803          10 :             m_iSrs = atoi(pszSRID);
    5804          10 :             if (m_iSrs == GDALGeoPackageDataset::FIRST_CUSTOM_SRSID - 1)
    5805             :             {
    5806           1 :                 m_iSrs = m_poDS->GetSrsId(nullptr);
    5807           1 :                 oGeomFieldDefn.SetSpatialRef(nullptr);
    5808             :             }
    5809             :             else
    5810             :             {
    5811             :                 poGotSRS =
    5812          18 :                     m_poDS->GetSpatialRef(m_iSrs, /* bFallbackToEPSG = */ false,
    5813           9 :                                           /* bEmitErrorIfNotFound = */ false);
    5814           9 :                 if (poGotSRS)
    5815             :                 {
    5816           6 :                     oGeomFieldDefn.SetSpatialRef(poGotSRS.get());
    5817             :                 }
    5818             :                 else
    5819             :                 {
    5820           3 :                     bool bOK = false;
    5821             :                     auto poSRSTmp =
    5822           6 :                         OGRSpatialReferenceRefCountedPtr::makeInstance();
    5823           3 :                     if (m_iSrs < 32767)
    5824             :                     {
    5825             :                         CPLErrorHandlerPusher oErrorHandler(
    5826           4 :                             CPLQuietErrorHandler);
    5827           4 :                         CPLErrorStateBackuper oBackuper;
    5828           2 :                         if (poSRSTmp->importFromEPSG(m_iSrs) == OGRERR_NONE)
    5829             :                         {
    5830           1 :                             bOK = true;
    5831           1 :                             poSRSTmp->SetAxisMappingStrategy(
    5832             :                                 OAMS_TRADITIONAL_GIS_ORDER);
    5833           1 :                             m_iSrs = m_poDS->GetSrsId(poSRSTmp.get());
    5834           1 :                             oGeomFieldDefn.SetSpatialRef(poSRSTmp.get());
    5835             :                         }
    5836             :                     }
    5837           3 :                     if (!bOK)
    5838             :                     {
    5839           2 :                         CPLError(
    5840             :                             CE_Warning, CPLE_AppDefined,
    5841             :                             "No entry in gpkg_spatial_ref_sys matching SRID=%s",
    5842             :                             pszSRID);
    5843             :                     }
    5844             :                 }
    5845             :             }
    5846             :         }
    5847             :         else
    5848             :         {
    5849         938 :             m_iSrs = m_poDS->GetSrsId(poSRS);
    5850             :         }
    5851         948 :         oGeomFieldDefn.SetNullable(bGeomNullable);
    5852         948 :         oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec);
    5853             : 
    5854         948 :         if (bDiscardCoordLSB)
    5855           2 :             m_sBinaryPrecision.SetFrom(oCoordPrec);
    5856             : 
    5857             :         // Save coordinate precision in gpkg_metadata/gpkg_metadata_reference
    5858        2834 :         if ((oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
    5859         938 :              oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
    5860        1896 :              oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN) &&
    5861          10 :             (m_poDS->HasMetadataTables() || m_poDS->CreateMetadataTables()))
    5862             :         {
    5863          20 :             std::string osCoordPrecision = "<CoordinatePrecision ";
    5864          10 :             if (oCoordPrec.dfXYResolution !=
    5865             :                 OGRGeomCoordinatePrecision::UNKNOWN)
    5866             :                 osCoordPrecision += CPLSPrintf(" xy_resolution=\"%g\"",
    5867          10 :                                                oCoordPrec.dfXYResolution);
    5868          10 :             if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    5869             :                 osCoordPrecision += CPLSPrintf(" z_resolution=\"%g\"",
    5870           7 :                                                oCoordPrec.dfZResolution);
    5871          10 :             if (oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
    5872             :                 osCoordPrecision += CPLSPrintf(" m_resolution=\"%g\"",
    5873           7 :                                                oCoordPrec.dfMResolution);
    5874             :             osCoordPrecision += CPLSPrintf(" discard_coord_lsb=\"%s\"",
    5875          10 :                                            bDiscardCoordLSB ? "true" : "false");
    5876             :             osCoordPrecision +=
    5877             :                 CPLSPrintf(" undo_discard_coord_lsb_on_reading=\"%s\"",
    5878          10 :                            m_bUndoDiscardCoordLSBOnReading ? "true" : "false");
    5879          10 :             osCoordPrecision += " />";
    5880             : 
    5881          10 :             char *pszSQL = sqlite3_mprintf(
    5882             :                 "INSERT INTO gpkg_metadata "
    5883             :                 "(md_scope, md_standard_uri, mime_type, metadata) VALUES "
    5884             :                 "('dataset','http://gdal.org','text/xml','%q')",
    5885             :                 osCoordPrecision.c_str());
    5886          10 :             CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
    5887          10 :             sqlite3_free(pszSQL);
    5888             : 
    5889             :             const sqlite_int64 nFID =
    5890          10 :                 sqlite3_last_insert_rowid(m_poDS->GetDB());
    5891          10 :             pszSQL = sqlite3_mprintf(
    5892             :                 "INSERT INTO gpkg_metadata_reference (reference_scope, "
    5893             :                 "table_name, column_name, timestamp, md_file_id) VALUES "
    5894             :                 "('column', '%q', '%q', %s, %d)",
    5895             :                 m_pszTableName, pszGeomColumnName,
    5896          20 :                 m_poDS->GetCurrentDateEscapedSQL().c_str(),
    5897             :                 static_cast<int>(nFID));
    5898          10 :             CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
    5899          10 :             sqlite3_free(pszSQL);
    5900             :         }
    5901             : 
    5902         948 :         m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
    5903             :     }
    5904        1030 :     if (pszIdentifier)
    5905             :     {
    5906           3 :         m_osIdentifierLCO = pszIdentifier;
    5907           3 :         OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
    5908             :     }
    5909        1030 :     if (pszDescription)
    5910             :     {
    5911           5 :         m_osDescriptionLCO = pszDescription;
    5912           5 :         OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
    5913             :     }
    5914             : 
    5915        1030 :     m_poFeatureDefn->Seal(/* bSealFields = */ true);
    5916        1030 : }
    5917             : 
    5918             : /************************************************************************/
    5919             : /*                       RegisterGeometryColumn()                       */
    5920             : /************************************************************************/
    5921             : 
    5922         951 : OGRErr OGRGeoPackageTableLayer::RegisterGeometryColumn()
    5923             : {
    5924         951 :     OGRwkbGeometryType eGType = GetGeomType();
    5925         951 :     const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
    5926             :     /* Requirement 27: The z value in a gpkg_geometry_columns table row */
    5927             :     /* SHALL be one of 0 (none), 1 (mandatory), or 2 (optional) */
    5928             : 
    5929             :     /* Update gpkg_geometry_columns with the table info */
    5930             :     char *pszSQL =
    5931         951 :         sqlite3_mprintf("INSERT INTO gpkg_geometry_columns "
    5932             :                         "(table_name,column_name,geometry_type_name,srs_id,z,m)"
    5933             :                         " VALUES "
    5934             :                         "('%q','%q','%q',%d,%d,%d)",
    5935             :                         GetName(), GetGeometryColumn(), pszGeometryType, m_iSrs,
    5936             :                         m_nZFlag, m_nMFlag);
    5937             : 
    5938         951 :     OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
    5939         951 :     sqlite3_free(pszSQL);
    5940         951 :     if (err != OGRERR_NONE)
    5941           0 :         return OGRERR_FAILURE;
    5942             : 
    5943         951 :     if (wkbFlatten(eGType) > wkbGeometryCollection)
    5944             :     {
    5945           6 :         CreateGeometryExtensionIfNecessary(eGType);
    5946             :     }
    5947             : 
    5948         951 :     return OGRERR_NONE;
    5949             : }
    5950             : 
    5951             : /************************************************************************/
    5952             : /*                      GetColumnsOfCreateTable()                       */
    5953             : /************************************************************************/
    5954             : 
    5955        1058 : CPLString OGRGeoPackageTableLayer::GetColumnsOfCreateTable(
    5956             :     const std::vector<OGRFieldDefn *> &apoFields)
    5957             : {
    5958        1058 :     CPLString osSQL;
    5959             : 
    5960        1058 :     char *pszSQL = nullptr;
    5961        1058 :     bool bNeedComma = false;
    5962        1058 :     if (m_pszFidColumn != nullptr)
    5963             :     {
    5964             :         pszSQL =
    5965        1058 :             sqlite3_mprintf("\"%w\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
    5966             :                             m_pszFidColumn);
    5967        1058 :         osSQL += pszSQL;
    5968        1058 :         sqlite3_free(pszSQL);
    5969        1058 :         bNeedComma = true;
    5970             :     }
    5971             : 
    5972        1058 :     const OGRwkbGeometryType eGType = GetGeomType();
    5973        1058 :     if (eGType != wkbNone)
    5974             :     {
    5975         977 :         if (bNeedComma)
    5976             :         {
    5977         977 :             osSQL += ", ";
    5978             :         }
    5979         977 :         bNeedComma = true;
    5980             : 
    5981             :         /* Requirement 25: The geometry_type_name value in a
    5982             :          * gpkg_geometry_columns */
    5983             :         /* row SHALL be one of the uppercase geometry type names specified in */
    5984             :         /* Geometry Types (Normative). */
    5985         977 :         const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
    5986             : 
    5987             :         pszSQL =
    5988         977 :             sqlite3_mprintf("\"%w\" %s", GetGeometryColumn(), pszGeometryType);
    5989         977 :         osSQL += pszSQL;
    5990         977 :         sqlite3_free(pszSQL);
    5991         977 :         if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsNullable())
    5992             :         {
    5993           2 :             osSQL += " NOT NULL";
    5994             :         }
    5995             :     }
    5996             : 
    5997        5609 :     for (size_t i = 0; i < apoFields.size(); i++)
    5998             :     {
    5999        4551 :         OGRFieldDefn *poFieldDefn = apoFields[i];
    6000             :         // Eg. when a geometry type is specified + an sql statement returns no
    6001             :         // or NULL geometry values, the geom column is incorrectly treated as
    6002             :         // an attribute column as well with the same name. Not ideal, but skip
    6003             :         // this column here to avoid duplicate column name error. Issue: #6976.
    6004        8948 :         if ((eGType != wkbNone) &&
    6005        4397 :             (EQUAL(poFieldDefn->GetNameRef(), GetGeometryColumn())))
    6006             :         {
    6007           0 :             continue;
    6008             :         }
    6009        4551 :         if (bNeedComma)
    6010             :         {
    6011        4551 :             osSQL += ", ";
    6012             :         }
    6013        4551 :         bNeedComma = true;
    6014             : 
    6015        4551 :         pszSQL = sqlite3_mprintf("\"%w\" %s", poFieldDefn->GetNameRef(),
    6016             :                                  GPkgFieldFromOGR(poFieldDefn->GetType(),
    6017             :                                                   poFieldDefn->GetSubType(),
    6018             :                                                   poFieldDefn->GetWidth()));
    6019        4551 :         osSQL += pszSQL;
    6020        4551 :         sqlite3_free(pszSQL);
    6021        4551 :         if (!poFieldDefn->IsNullable())
    6022             :         {
    6023          15 :             osSQL += " NOT NULL";
    6024             :         }
    6025        4551 :         if (poFieldDefn->IsUnique())
    6026             :         {
    6027          18 :             osSQL += " UNIQUE";
    6028             :         }
    6029        4551 :         const char *pszDefault = poFieldDefn->GetDefault();
    6030        4566 :         if (pszDefault != nullptr &&
    6031          15 :             (!poFieldDefn->IsDefaultDriverSpecific() ||
    6032           1 :              (pszDefault[0] == '(' &&
    6033           1 :               pszDefault[strlen(pszDefault) - 1] == ')' &&
    6034           1 :               (STARTS_WITH_CI(pszDefault + 1, "strftime") ||
    6035           0 :                STARTS_WITH_CI(pszDefault + 1, " strftime")))))
    6036             :         {
    6037          15 :             osSQL += " DEFAULT ";
    6038             :             OGRField sField;
    6039          19 :             if (poFieldDefn->GetType() == OFTDateTime &&
    6040           4 :                 OGRParseDate(pszDefault, &sField, 0))
    6041             :             {
    6042             :                 char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
    6043           0 :                 OGRGetISO8601DateTime(&sField, false, szBuffer);
    6044           0 :                 osSQL += szBuffer;
    6045             :             }
    6046             :             /* Make sure CURRENT_TIMESTAMP is translated into appropriate format
    6047             :              * for GeoPackage */
    6048          19 :             else if (poFieldDefn->GetType() == OFTDateTime &&
    6049           4 :                      EQUAL(pszDefault, "CURRENT_TIMESTAMP"))
    6050             :             {
    6051           1 :                 osSQL += "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))";
    6052             :             }
    6053             :             else
    6054             :             {
    6055          14 :                 osSQL += poFieldDefn->GetDefault();
    6056             :             }
    6057             :         }
    6058             :     }
    6059             : 
    6060        1058 :     return osSQL;
    6061             : }
    6062             : 
    6063             : /************************************************************************/
    6064             : /*                   RunDeferredCreationIfNecessary()                   */
    6065             : /************************************************************************/
    6066             : 
    6067        6005 : OGRErr OGRGeoPackageTableLayer::RunDeferredCreationIfNecessary()
    6068             : {
    6069        6005 :     if (!m_bDeferredCreation)
    6070        4975 :         return OGRERR_NONE;
    6071        1030 :     m_bDeferredCreation = false;
    6072             : 
    6073        1030 :     const char *pszLayerName = m_poFeatureDefn->GetName();
    6074             : 
    6075             :     /* Create the table! */
    6076        2060 :     CPLString osCommand;
    6077             : 
    6078        1030 :     char *pszSQL = sqlite3_mprintf("CREATE TABLE \"%w\" ( ", pszLayerName);
    6079        1030 :     osCommand += pszSQL;
    6080        1030 :     sqlite3_free(pszSQL);
    6081             : 
    6082        2060 :     std::vector<OGRFieldDefn *> apoFields;
    6083        5520 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
    6084             :     {
    6085        4490 :         if (i == m_iFIDAsRegularColumnIndex)
    6086           4 :             continue;
    6087        4486 :         apoFields.push_back(m_poFeatureDefn->GetFieldDefn(i));
    6088             :     }
    6089             : 
    6090        1030 :     osCommand += GetColumnsOfCreateTable(apoFields);
    6091             : 
    6092        1030 :     osCommand += ")";
    6093             : 
    6094             : #ifdef DEBUG
    6095        1030 :     CPLDebug("GPKG", "exec(%s)", osCommand.c_str());
    6096             : #endif
    6097        1030 :     OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
    6098        1030 :     if (OGRERR_NONE != err)
    6099           0 :         return OGRERR_FAILURE;
    6100             : 
    6101        5516 :     for (auto &poField : apoFields)
    6102             :     {
    6103        4486 :         if (!DoSpecialProcessingForColumnCreation(poField))
    6104             :         {
    6105           0 :             return OGRERR_FAILURE;
    6106             :         }
    6107             :     }
    6108             : 
    6109             :     /* Update gpkg_contents with the table info */
    6110        1030 :     const OGRwkbGeometryType eGType = GetGeomType();
    6111        1030 :     const bool bIsSpatial = (eGType != wkbNone);
    6112             : 
    6113        1030 :     if (bIsSpatial || m_eASpatialVariant == GPKG_ATTRIBUTES)
    6114             :     {
    6115        1018 :         const char *pszIdentifier = GetMetadataItem("IDENTIFIER");
    6116        1018 :         if (pszIdentifier == nullptr)
    6117        1015 :             pszIdentifier = pszLayerName;
    6118        1018 :         const char *pszDescription = GetMetadataItem("DESCRIPTION");
    6119        1018 :         if (pszDescription == nullptr)
    6120        1013 :             pszDescription = "";
    6121             : 
    6122        1018 :         pszSQL = sqlite3_mprintf(
    6123             :             "INSERT INTO gpkg_contents "
    6124             :             "(table_name,data_type,identifier,description,last_change,srs_id) "
    6125             :             "VALUES "
    6126             :             "('%q','%q','%q','%q',%s,%d)",
    6127             :             pszLayerName, (bIsSpatial ? "features" : "attributes"),
    6128             :             pszIdentifier, pszDescription,
    6129        2036 :             GDALGeoPackageDataset::GetCurrentDateEscapedSQL().c_str(), m_iSrs);
    6130             : 
    6131        1018 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    6132        1018 :         sqlite3_free(pszSQL);
    6133        1018 :         if (err != OGRERR_NONE)
    6134           0 :             return OGRERR_FAILURE;
    6135             :     }
    6136             : 
    6137        1030 :     if (bIsSpatial)
    6138             :     {
    6139             :         // Insert into gpkg_geometry_columns after gpkg_contents because of
    6140             :         // foreign key constraints
    6141         949 :         err = RegisterGeometryColumn();
    6142         949 :         if (err != OGRERR_NONE)
    6143           0 :             return OGRERR_FAILURE;
    6144             :     }
    6145             : 
    6146             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    6147        1030 :     if (m_poDS->m_bHasGPKGOGRContents)
    6148             :     {
    6149        1024 :         pszSQL = sqlite3_mprintf("DELETE FROM gpkg_ogr_contents WHERE "
    6150             :                                  "lower(table_name) = lower('%q')",
    6151             :                                  pszLayerName);
    6152        1024 :         SQLCommand(m_poDS->GetDB(), pszSQL);
    6153        1024 :         sqlite3_free(pszSQL);
    6154             : 
    6155        1024 :         pszSQL = sqlite3_mprintf(
    6156             :             "INSERT INTO gpkg_ogr_contents (table_name, feature_count) "
    6157             :             "VALUES ('%q', 0)",
    6158             :             pszLayerName);
    6159        1024 :         err = SQLCommand(m_poDS->GetDB(), pszSQL);
    6160        1024 :         sqlite3_free(pszSQL);
    6161        1024 :         if (err == OGRERR_NONE)
    6162             :         {
    6163        1024 :             m_nTotalFeatureCount = 0;
    6164        1024 :             m_bAddOGRFeatureCountTriggers = true;
    6165             :         }
    6166             :     }
    6167             : #endif
    6168             : 
    6169        1030 :     ResetReading();
    6170             : 
    6171        1030 :     return OGRERR_NONE;
    6172             : }
    6173             : 
    6174             : /************************************************************************/
    6175             : /*                            GetMetadata()                             */
    6176             : /************************************************************************/
    6177             : 
    6178       15444 : CSLConstList OGRGeoPackageTableLayer::GetMetadata(const char *pszDomain)
    6179             : 
    6180             : {
    6181       15444 :     if (!m_bFeatureDefnCompleted)
    6182         362 :         GetLayerDefn();
    6183       15444 :     if (!m_bHasTriedDetectingFID64 && m_pszFidColumn != nullptr)
    6184             :     {
    6185         450 :         m_bHasTriedDetectingFID64 = true;
    6186             : 
    6187             :         /* --------------------------------------------------------------------
    6188             :          */
    6189             :         /*      Find if the FID holds 64bit values */
    6190             :         /* --------------------------------------------------------------------
    6191             :          */
    6192             : 
    6193             :         // Normally the fid should be AUTOINCREMENT, so check sqlite_sequence
    6194         450 :         OGRErr err = OGRERR_NONE;
    6195             :         char *pszSQL =
    6196         450 :             sqlite3_mprintf("SELECT seq FROM sqlite_sequence WHERE name = '%q'",
    6197             :                             m_pszTableName);
    6198         450 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    6199         450 :         GIntBig nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
    6200         450 :         CPLPopErrorHandler();
    6201         450 :         sqlite3_free(pszSQL);
    6202         450 :         if (err != OGRERR_NONE)
    6203             :         {
    6204          47 :             CPLErrorReset();
    6205             : 
    6206             :             // In case of error, fallback to taking the MAX of the FID
    6207          47 :             pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
    6208             :                                      m_pszFidColumn, m_pszTableName);
    6209             : 
    6210          47 :             nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, nullptr);
    6211          47 :             sqlite3_free(pszSQL);
    6212             :         }
    6213         450 :         if (nMaxId > INT_MAX)
    6214           1 :             OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
    6215             :     }
    6216             : 
    6217       15444 :     if (m_bHasReadMetadataFromStorage)
    6218       13972 :         return OGRLayer::GetMetadata(pszDomain);
    6219             : 
    6220        1472 :     m_bHasReadMetadataFromStorage = true;
    6221             : 
    6222        1472 :     if (!m_poDS->HasMetadataTables())
    6223        1139 :         return OGRLayer::GetMetadata(pszDomain);
    6224             : 
    6225         333 :     char *pszSQL = sqlite3_mprintf(
    6226             :         "SELECT md.metadata, md.md_standard_uri, md.mime_type, "
    6227             :         "mdr.reference_scope "
    6228             :         "FROM gpkg_metadata md "
    6229             :         "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
    6230             :         "WHERE lower(mdr.table_name) = lower('%q') ORDER BY md.id "
    6231             :         "LIMIT 1000",  // to avoid denial of service
    6232             :         m_pszTableName);
    6233             : 
    6234         666 :     auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
    6235         333 :     sqlite3_free(pszSQL);
    6236         333 :     if (!oResult)
    6237             :     {
    6238           0 :         return OGRLayer::GetMetadata(pszDomain);
    6239             :     }
    6240             : 
    6241         333 :     char **papszMetadata = CSLDuplicate(OGRLayer::GetMetadata());
    6242             : 
    6243             :     /* GDAL metadata */
    6244         634 :     for (int i = 0; i < oResult->RowCount(); i++)
    6245             :     {
    6246         301 :         const char *pszMetadata = oResult->GetValue(0, i);
    6247         301 :         const char *pszMDStandardURI = oResult->GetValue(1, i);
    6248         301 :         const char *pszMimeType = oResult->GetValue(2, i);
    6249         301 :         const char *pszReferenceScope = oResult->GetValue(3, i);
    6250         301 :         if (pszMetadata && pszMDStandardURI && pszMimeType &&
    6251         301 :             pszReferenceScope && EQUAL(pszMDStandardURI, "http://gdal.org") &&
    6252         301 :             EQUAL(pszMimeType, "text/xml") && EQUAL(pszReferenceScope, "table"))
    6253             :         {
    6254         282 :             CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
    6255         282 :             if (psXMLNode)
    6256             :             {
    6257         564 :                 GDALMultiDomainMetadata oLocalMDMD;
    6258         282 :                 oLocalMDMD.XMLInit(psXMLNode, FALSE);
    6259             : 
    6260             :                 papszMetadata =
    6261         282 :                     CSLMerge(papszMetadata, oLocalMDMD.GetMetadata());
    6262         282 :                 CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
    6263         282 :                 CSLConstList papszIter = papszDomainList;
    6264         597 :                 while (papszIter && *papszIter)
    6265             :                 {
    6266         315 :                     if (!EQUAL(*papszIter, ""))
    6267          34 :                         oMDMD.SetMetadata(oLocalMDMD.GetMetadata(*papszIter),
    6268             :                                           *papszIter);
    6269         315 :                     papszIter++;
    6270             :                 }
    6271             : 
    6272         282 :                 CPLDestroyXMLNode(psXMLNode);
    6273             :             }
    6274             :         }
    6275             :     }
    6276             : 
    6277         333 :     OGRLayer::SetMetadata(papszMetadata);
    6278         333 :     CSLDestroy(papszMetadata);
    6279         333 :     papszMetadata = nullptr;
    6280             : 
    6281             :     /* Add non-GDAL metadata now */
    6282         333 :     int nNonGDALMDILocal = 1;
    6283         634 :     for (int i = 0; i < oResult->RowCount(); i++)
    6284             :     {
    6285         301 :         const char *pszMetadata = oResult->GetValue(0, i);
    6286         301 :         const char *pszMDStandardURI = oResult->GetValue(1, i);
    6287         301 :         const char *pszMimeType = oResult->GetValue(2, i);
    6288             :         // const char* pszReferenceScope = oResult->GetValue(3, i);
    6289         301 :         if (pszMetadata == nullptr || pszMDStandardURI == nullptr ||
    6290             :             pszMimeType == nullptr
    6291             :             /* || pszReferenceScope == nullptr */)
    6292             :         {
    6293             :             // should not happen as there are NOT NULL constraints
    6294             :             // But a database could lack such NOT NULL constraints or have
    6295             :             // large values that would cause a memory allocation failure.
    6296           0 :             continue;
    6297             :         }
    6298             :         // int bIsGPKGScope = EQUAL(pszReferenceScope, "geopackage");
    6299         301 :         if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
    6300         301 :             EQUAL(pszMimeType, "text/xml"))
    6301         301 :             continue;
    6302             : 
    6303           0 :         if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
    6304           0 :             EQUAL(pszMimeType, "text/plain"))
    6305             :         {
    6306           0 :             if (STARTS_WITH_CI(pszMetadata, "coordinate_epoch="))
    6307             :             {
    6308           0 :                 continue;
    6309             :             }
    6310             :         }
    6311             : 
    6312             :         /*if( strcmp( pszMDStandardURI, "http://www.isotc211.org/2005/gmd" ) ==
    6313             :         0 && strcmp( pszMimeType, "text/xml" ) == 0 )
    6314             :         {
    6315             :             char* apszMD[2];
    6316             :             apszMD[0] = (char*)pszMetadata;
    6317             :             apszMD[1] = NULL;
    6318             :             oMDMD.SetMetadata(apszMD, "xml:MD_Metadata");
    6319             :         }
    6320             :         else*/
    6321             :         {
    6322           0 :             oMDMD.SetMetadataItem(
    6323             :                 CPLSPrintf("GPKG_METADATA_ITEM_%d", nNonGDALMDILocal),
    6324             :                 pszMetadata);
    6325           0 :             nNonGDALMDILocal++;
    6326             :         }
    6327             :     }
    6328             : 
    6329         333 :     return OGRLayer::GetMetadata(pszDomain);
    6330             : }
    6331             : 
    6332             : /************************************************************************/
    6333             : /*                          GetMetadataItem()                           */
    6334             : /************************************************************************/
    6335             : 
    6336       13168 : const char *OGRGeoPackageTableLayer::GetMetadataItem(const char *pszName,
    6337             :                                                      const char *pszDomain)
    6338             : {
    6339       13168 :     return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
    6340             : }
    6341             : 
    6342             : /************************************************************************/
    6343             : /*                       GetMetadataDomainList()                        */
    6344             : /************************************************************************/
    6345             : 
    6346         359 : char **OGRGeoPackageTableLayer::GetMetadataDomainList()
    6347             : {
    6348         359 :     GetMetadata();
    6349         359 :     return OGRLayer::GetMetadataDomainList();
    6350             : }
    6351             : 
    6352             : /************************************************************************/
    6353             : /*                            SetMetadata()                             */
    6354             : /************************************************************************/
    6355             : 
    6356         167 : CPLErr OGRGeoPackageTableLayer::SetMetadata(CSLConstList papszMetadata,
    6357             :                                             const char *pszDomain)
    6358             : {
    6359         167 :     GetMetadata(); /* force loading from storage if needed */
    6360         167 :     CPLErr eErr = OGRLayer::SetMetadata(papszMetadata, pszDomain);
    6361         167 :     m_poDS->SetMetadataDirty();
    6362         167 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    6363             :     {
    6364          98 :         if (!m_osIdentifierLCO.empty())
    6365           1 :             OGRLayer::SetMetadataItem("IDENTIFIER", m_osIdentifierLCO);
    6366          98 :         if (!m_osDescriptionLCO.empty())
    6367           1 :             OGRLayer::SetMetadataItem("DESCRIPTION", m_osDescriptionLCO);
    6368             :     }
    6369         167 :     return eErr;
    6370             : }
    6371             : 
    6372             : /************************************************************************/
    6373             : /*                          SetMetadataItem()                           */
    6374             : /************************************************************************/
    6375             : 
    6376         682 : CPLErr OGRGeoPackageTableLayer::SetMetadataItem(const char *pszName,
    6377             :                                                 const char *pszValue,
    6378             :                                                 const char *pszDomain)
    6379             : {
    6380         682 :     GetMetadata(); /* force loading from storage if needed */
    6381         683 :     if (!m_osIdentifierLCO.empty() && EQUAL(pszName, "IDENTIFIER") &&
    6382           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    6383           1 :         return CE_None;
    6384         682 :     if (!m_osDescriptionLCO.empty() && EQUAL(pszName, "DESCRIPTION") &&
    6385           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    6386           1 :         return CE_None;
    6387         680 :     m_poDS->SetMetadataDirty();
    6388         680 :     return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
    6389             : }
    6390             : 
    6391             : /************************************************************************/
    6392             : /*                           RecreateTable()                            */
    6393             : /************************************************************************/
    6394             : 
    6395             : OGRErr
    6396          12 : OGRGeoPackageTableLayer::RecreateTable(const CPLString &osColumnsForCreate,
    6397             :                                        const CPLString &osFieldListForSelect)
    6398             : {
    6399             :     /* -------------------------------------------------------------------- */
    6400             :     /*      Save existing related triggers and index                        */
    6401             :     /* -------------------------------------------------------------------- */
    6402          12 :     sqlite3 *hDB = m_poDS->GetDB();
    6403             : 
    6404          12 :     char *pszSQL = sqlite3_mprintf(
    6405             :         "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') "
    6406             :         "AND lower(tbl_name)=lower('%q') LIMIT 10000",
    6407             :         m_pszTableName);
    6408          12 :     OGRErr eErr = OGRERR_NONE;
    6409          12 :     auto oTriggers = SQLQuery(hDB, pszSQL);
    6410          12 :     sqlite3_free(pszSQL);
    6411             : 
    6412             :     /* -------------------------------------------------------------------- */
    6413             :     /*      Make a temporary table with new content.                        */
    6414             :     /* -------------------------------------------------------------------- */
    6415          12 :     if (oTriggers)
    6416             :     {
    6417          12 :         pszSQL = sqlite3_mprintf("CREATE TABLE \"%w_ogr_tmp\" (%s)",
    6418             :                                  m_pszTableName, osColumnsForCreate.c_str());
    6419          12 :         eErr = SQLCommand(hDB, pszSQL);
    6420          12 :         sqlite3_free(pszSQL);
    6421             :     }
    6422             :     else
    6423             :     {
    6424           0 :         eErr = OGRERR_FAILURE;
    6425             :     }
    6426             : 
    6427          12 :     if (eErr == OGRERR_NONE)
    6428             :     {
    6429          12 :         pszSQL = sqlite3_mprintf(
    6430             :             "INSERT INTO \"%w_ogr_tmp\" SELECT %s FROM \"%w\"", m_pszTableName,
    6431             :             osFieldListForSelect.c_str(), m_pszTableName);
    6432          12 :         eErr = SQLCommand(hDB, pszSQL);
    6433          12 :         sqlite3_free(pszSQL);
    6434             :     }
    6435             : 
    6436             :     /* -------------------------------------------------------------------- */
    6437             :     /*      Drop the original table                                         */
    6438             :     /* -------------------------------------------------------------------- */
    6439          12 :     if (eErr == OGRERR_NONE)
    6440             :     {
    6441          10 :         pszSQL = sqlite3_mprintf("DROP TABLE \"%w\"", m_pszTableName);
    6442          10 :         eErr = SQLCommand(hDB, pszSQL);
    6443          10 :         sqlite3_free(pszSQL);
    6444             :     }
    6445             : 
    6446             :     /* -------------------------------------------------------------------- */
    6447             :     /*      Rename temporary table as new table                             */
    6448             :     /* -------------------------------------------------------------------- */
    6449          12 :     if (eErr == OGRERR_NONE)
    6450             :     {
    6451          10 :         pszSQL = sqlite3_mprintf("ALTER TABLE \"%w_ogr_tmp\" RENAME TO \"%w\"",
    6452             :                                  m_pszTableName, m_pszTableName);
    6453          10 :         eErr = SQLCommand(hDB, pszSQL);
    6454          10 :         sqlite3_free(pszSQL);
    6455             :     }
    6456             : 
    6457             :     /* -------------------------------------------------------------------- */
    6458             :     /*      Recreate existing related tables, triggers and index            */
    6459             :     /* -------------------------------------------------------------------- */
    6460          89 :     for (int i = 0;
    6461          89 :          oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
    6462             :     {
    6463          77 :         const char *pszSQLTriggerIdx = oTriggers->GetValue(0, i);
    6464          77 :         if (pszSQLTriggerIdx != nullptr && *pszSQLTriggerIdx != '\0')
    6465             :         {
    6466          76 :             eErr = SQLCommand(hDB, pszSQLTriggerIdx);
    6467             :         }
    6468             :     }
    6469             : 
    6470          24 :     return eErr;
    6471             : }
    6472             : 
    6473             : /************************************************************************/
    6474             : /*                        BuildSelectFieldList()                        */
    6475             : /************************************************************************/
    6476             : 
    6477          12 : CPLString OGRGeoPackageTableLayer::BuildSelectFieldList(
    6478             :     const std::vector<OGRFieldDefn *> &apoFields)
    6479             : {
    6480          12 :     CPLString osFieldListForSelect;
    6481             : 
    6482          12 :     char *pszSQL = nullptr;
    6483          12 :     bool bNeedComma = false;
    6484             : 
    6485          12 :     if (m_pszFidColumn != nullptr)
    6486             :     {
    6487          12 :         pszSQL = sqlite3_mprintf("\"%w\"", m_pszFidColumn);
    6488          12 :         osFieldListForSelect += pszSQL;
    6489          12 :         sqlite3_free(pszSQL);
    6490          12 :         bNeedComma = true;
    6491             :     }
    6492             : 
    6493          12 :     if (GetGeomType() != wkbNone)
    6494             :     {
    6495          12 :         if (bNeedComma)
    6496             :         {
    6497          12 :             osFieldListForSelect += ", ";
    6498             :         }
    6499          12 :         bNeedComma = true;
    6500             : 
    6501          12 :         pszSQL = sqlite3_mprintf("\"%w\"", GetGeometryColumn());
    6502          12 :         osFieldListForSelect += pszSQL;
    6503          12 :         sqlite3_free(pszSQL);
    6504             :     }
    6505             : 
    6506          33 :     for (size_t iField = 0; iField < apoFields.size(); iField++)
    6507             :     {
    6508          21 :         if (bNeedComma)
    6509             :         {
    6510          21 :             osFieldListForSelect += ", ";
    6511             :         }
    6512          21 :         bNeedComma = true;
    6513             : 
    6514          21 :         OGRFieldDefn *poFieldDefn = apoFields[iField];
    6515          21 :         pszSQL = sqlite3_mprintf("\"%w\"", poFieldDefn->GetNameRef());
    6516          21 :         osFieldListForSelect += pszSQL;
    6517          21 :         sqlite3_free(pszSQL);
    6518             :     }
    6519             : 
    6520          12 :     return osFieldListForSelect;
    6521             : }
    6522             : 
    6523             : /************************************************************************/
    6524             : /*                            DeleteField()                             */
    6525             : /************************************************************************/
    6526             : 
    6527         163 : OGRErr OGRGeoPackageTableLayer::DeleteField(int iFieldToDelete)
    6528             : {
    6529         163 :     if (!m_bFeatureDefnCompleted)
    6530           3 :         GetLayerDefn();
    6531         163 :     if (!CheckUpdatableTable("DeleteField"))
    6532           2 :         return OGRERR_FAILURE;
    6533             : 
    6534         321 :     if (iFieldToDelete < 0 ||
    6535         160 :         iFieldToDelete >= m_poFeatureDefn->GetFieldCount())
    6536             :     {
    6537           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    6538           2 :         return OGRERR_FAILURE;
    6539             :     }
    6540             : 
    6541         159 :     ResetReading();
    6542         159 :     RunDeferredCreationIfNecessary();
    6543         159 :     if (!RunDeferredSpatialIndexUpdate())
    6544           0 :         return OGRERR_FAILURE;
    6545             : 
    6546             :     const char *pszFieldName =
    6547         159 :         m_poFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
    6548             : 
    6549             :     /* -------------------------------------------------------------------- */
    6550             :     /*      Drop any iterator since we change the DB structure              */
    6551             :     /* -------------------------------------------------------------------- */
    6552         159 :     m_poDS->ResetReadingAllLayers();
    6553             : 
    6554             :     // Temporary remove foreign key checks
    6555             :     const GPKGTemporaryForeignKeyCheckDisabler
    6556         318 :         oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
    6557             : 
    6558         174 :     if (m_poDS->GetCurrentSavepoint().empty() &&
    6559          15 :         m_poDS->SoftStartTransaction() != OGRERR_NONE)
    6560             :     {
    6561           0 :         return OGRERR_FAILURE;
    6562             :     }
    6563             : 
    6564             :     // ALTER TABLE ... DROP COLUMN ... was first implemented in 3.35.0 but
    6565             :     // there was bug fixes related to it until 3.35.5
    6566             : #if SQLITE_VERSION_NUMBER >= 3035005L
    6567         159 :     OGRErr eErr = SQLCommand(
    6568         318 :         m_poDS->GetDB(), CPLString()
    6569             :                              .Printf("ALTER TABLE \"%s\" DROP COLUMN \"%s\"",
    6570         318 :                                      SQLEscapeName(m_pszTableName).c_str(),
    6571         477 :                                      SQLEscapeName(pszFieldName).c_str())
    6572             :                              .c_str());
    6573             : #else
    6574             :     /* -------------------------------------------------------------------- */
    6575             :     /*      Recreate table in a transaction                                 */
    6576             :     /*      Build list of old fields, and the list of new fields.           */
    6577             :     /* -------------------------------------------------------------------- */
    6578             :     std::vector<OGRFieldDefn *> apoFields;
    6579             :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    6580             :     {
    6581             :         if (iField == iFieldToDelete)
    6582             :             continue;
    6583             : 
    6584             :         OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
    6585             :         apoFields.push_back(poFieldDefn);
    6586             :     }
    6587             : 
    6588             :     CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
    6589             :     CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
    6590             : 
    6591             :     OGRErr eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
    6592             : #endif
    6593             : 
    6594             :     /* -------------------------------------------------------------------- */
    6595             :     /*      Update gpkg_extensions if needed.                               */
    6596             :     /* -------------------------------------------------------------------- */
    6597         159 :     if (eErr == OGRERR_NONE && m_poDS->HasExtensionsTable())
    6598             :     {
    6599         154 :         char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_extensions WHERE "
    6600             :                                        "lower(table_name) = lower('%q') AND "
    6601             :                                        "lower(column_name) = lower('%q')",
    6602             :                                        m_pszTableName, pszFieldName);
    6603         154 :         eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6604         154 :         sqlite3_free(pszSQL);
    6605             :     }
    6606             : 
    6607             :     /* -------------------------------------------------------------------- */
    6608             :     /*      Update gpkg_data_columns if needed.                             */
    6609             :     /* -------------------------------------------------------------------- */
    6610         159 :     if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
    6611             :     {
    6612           4 :         char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_data_columns WHERE "
    6613             :                                        "lower(table_name) = lower('%q') AND "
    6614             :                                        "lower(column_name) = lower('%q')",
    6615             :                                        m_pszTableName, pszFieldName);
    6616           4 :         eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6617           4 :         sqlite3_free(pszSQL);
    6618             :     }
    6619             : 
    6620             :     /* -------------------------------------------------------------------- */
    6621             :     /*      Update gpkg_metadata_reference if needed.                       */
    6622             :     /* -------------------------------------------------------------------- */
    6623         159 :     if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
    6624             :     {
    6625             :         {
    6626             :             // Delete from gpkg_metadata metadata records that are only
    6627             :             // referenced by the column we are about to drop
    6628           3 :             char *pszSQL = sqlite3_mprintf(
    6629             :                 "DELETE FROM gpkg_metadata WHERE id IN ("
    6630             :                 "SELECT DISTINCT md_file_id FROM "
    6631             :                 "gpkg_metadata_reference WHERE "
    6632             :                 "lower(table_name) = lower('%q') "
    6633             :                 "AND lower(column_name) = lower('%q') "
    6634             :                 "AND md_parent_id is NULL) "
    6635             :                 "AND id NOT IN ("
    6636             :                 "SELECT DISTINCT md_file_id FROM gpkg_metadata_reference WHERE "
    6637             :                 "md_file_id IN ("
    6638             :                 "SELECT DISTINCT md_file_id FROM "
    6639             :                 "gpkg_metadata_reference WHERE "
    6640             :                 "lower(table_name) = lower('%q') "
    6641             :                 "AND lower(column_name) = lower('%q') "
    6642             :                 "AND md_parent_id is NULL) "
    6643             :                 "AND ("
    6644             :                 "lower(table_name) <> lower('%q') "
    6645             :                 "OR column_name IS NULL "
    6646             :                 "OR lower(column_name) <> lower('%q')))",
    6647             :                 m_pszTableName, pszFieldName, m_pszTableName, pszFieldName,
    6648             :                 m_pszTableName, pszFieldName);
    6649           3 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6650           3 :             sqlite3_free(pszSQL);
    6651             :         }
    6652             : 
    6653           3 :         if (eErr == OGRERR_NONE)
    6654             :         {
    6655             :             char *pszSQL =
    6656           3 :                 sqlite3_mprintf("DELETE FROM gpkg_metadata_reference WHERE "
    6657             :                                 "lower(table_name) = lower('%q') AND "
    6658             :                                 "lower(column_name) = lower('%q')",
    6659             :                                 m_pszTableName, pszFieldName);
    6660           3 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    6661           3 :             sqlite3_free(pszSQL);
    6662             :         }
    6663             :     }
    6664             : 
    6665             :     /* -------------------------------------------------------------------- */
    6666             :     /*      Check foreign key integrity if enforcement of foreign keys      */
    6667             :     /*      constraint is enabled.                                          */
    6668             :     /* -------------------------------------------------------------------- */
    6669         318 :     if (eErr == OGRERR_NONE &&
    6670         159 :         SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
    6671             :     {
    6672           0 :         CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
    6673           0 :         eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
    6674             :     }
    6675             : 
    6676             :     /* -------------------------------------------------------------------- */
    6677             :     /*      Finish                                                          */
    6678             :     /* -------------------------------------------------------------------- */
    6679         159 :     if (eErr == OGRERR_NONE)
    6680             :     {
    6681             : 
    6682         159 :         if (m_poDS->GetCurrentSavepoint().empty())
    6683          15 :             eErr = m_poDS->SoftCommitTransaction();
    6684             : 
    6685         159 :         if (eErr == OGRERR_NONE)
    6686             :         {
    6687             : 
    6688         159 :             if (m_poDS->IsInTransaction())
    6689             :             {
    6690         153 :                 auto poFieldDefn{whileUnsealing(m_poFeatureDefn)
    6691         306 :                                      ->StealFieldDefn(iFieldToDelete)};
    6692         153 :                 if (poFieldDefn)
    6693             :                 {
    6694             :                     m_apoFieldDefnChanges.emplace_back(
    6695         153 :                         std::move(poFieldDefn), iFieldToDelete,
    6696         153 :                         FieldChangeType::DELETE_FIELD,
    6697         306 :                         m_poDS->GetCurrentSavepoint());
    6698             :                 }
    6699             :                 else
    6700             :                 {
    6701           0 :                     eErr = OGRERR_FAILURE;
    6702             :                 }
    6703             :             }
    6704             :             else
    6705             :             {
    6706          12 :                 eErr = whileUnsealing(m_poFeatureDefn)
    6707           6 :                            ->DeleteFieldDefn(iFieldToDelete);
    6708             :             }
    6709         159 :             ResetReading();
    6710             :         }
    6711             :     }
    6712           0 :     else if (m_poDS->GetCurrentSavepoint().empty())
    6713             :     {
    6714           0 :         m_poDS->SoftRollbackTransaction();
    6715             :     }
    6716             : 
    6717         159 :     return eErr;
    6718             : }
    6719             : 
    6720             : /************************************************************************/
    6721             : /*                    RenameFieldInAuxiliaryTables()                    */
    6722             : /************************************************************************/
    6723             : 
    6724             : OGRErr
    6725          17 : OGRGeoPackageTableLayer::RenameFieldInAuxiliaryTables(const char *pszOldName,
    6726             :                                                       const char *pszNewName)
    6727             : {
    6728          17 :     OGRErr eErr = OGRERR_NONE;
    6729          17 :     sqlite3 *hDB = m_poDS->GetDB();
    6730             : 
    6731             :     /* -------------------------------------------------------------------- */
    6732             :     /*      Update gpkg_extensions if needed.                               */
    6733             :     /* -------------------------------------------------------------------- */
    6734          17 :     if (/* eErr == OGRERR_NONE && */ m_poDS->HasExtensionsTable())
    6735             :     {
    6736          15 :         char *pszSQL = sqlite3_mprintf(
    6737             :             "UPDATE gpkg_extensions SET column_name = '%q' WHERE "
    6738             :             "lower(table_name) = lower('%q') AND lower(column_name) = "
    6739             :             "lower('%q')",
    6740             :             pszNewName, m_pszTableName, pszOldName);
    6741          15 :         eErr = SQLCommand(hDB, pszSQL);
    6742          15 :         sqlite3_free(pszSQL);
    6743             :     }
    6744             : 
    6745             :     /* -------------------------------------------------------------------- */
    6746             :     /*      Update gpkg_data_columns if needed.                             */
    6747             :     /* -------------------------------------------------------------------- */
    6748          17 :     if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
    6749             :     {
    6750           6 :         char *pszSQL = sqlite3_mprintf(
    6751             :             "UPDATE gpkg_data_columns SET column_name = '%q' WHERE "
    6752             :             "lower(table_name) = lower('%q') AND lower(column_name) = "
    6753             :             "lower('%q')",
    6754             :             pszNewName, m_pszTableName, pszOldName);
    6755           6 :         eErr = SQLCommand(hDB, pszSQL);
    6756           6 :         sqlite3_free(pszSQL);
    6757             :     }
    6758             : 
    6759             :     /* -------------------------------------------------------------------- */
    6760             :     /*      Update gpkg_metadata_reference if needed.                       */
    6761             :     /* -------------------------------------------------------------------- */
    6762          17 :     if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
    6763             :     {
    6764           7 :         char *pszSQL = sqlite3_mprintf(
    6765             :             "UPDATE gpkg_metadata_reference SET column_name = '%q' WHERE "
    6766             :             "lower(table_name) = lower('%q') AND lower(column_name) = "
    6767             :             "lower('%q')",
    6768             :             pszNewName, m_pszTableName, pszOldName);
    6769           7 :         eErr = SQLCommand(hDB, pszSQL);
    6770           7 :         sqlite3_free(pszSQL);
    6771             :     }
    6772             : 
    6773          17 :     return eErr;
    6774             : }
    6775             : 
    6776             : /************************************************************************/
    6777             : /*                           AlterFieldDefn()                           */
    6778             : /************************************************************************/
    6779             : 
    6780          34 : OGRErr OGRGeoPackageTableLayer::AlterFieldDefn(int iFieldToAlter,
    6781             :                                                OGRFieldDefn *poNewFieldDefn,
    6782             :                                                int nFlagsIn)
    6783             : {
    6784          34 :     if (!m_bFeatureDefnCompleted)
    6785           1 :         GetLayerDefn();
    6786          34 :     if (!CheckUpdatableTable("AlterFieldDefn"))
    6787           2 :         return OGRERR_FAILURE;
    6788             : 
    6789          32 :     if (iFieldToAlter < 0 || iFieldToAlter >= m_poFeatureDefn->GetFieldCount())
    6790             :     {
    6791           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    6792           1 :         return OGRERR_FAILURE;
    6793             :     }
    6794             : 
    6795             :     /* -------------------------------------------------------------------- */
    6796             :     /*      Deferred actions, reset state.                                   */
    6797             :     /* -------------------------------------------------------------------- */
    6798          31 :     ResetReading();
    6799          31 :     RunDeferredCreationIfNecessary();
    6800          31 :     if (m_bThreadRTreeStarted)
    6801           0 :         CancelAsyncRTree();
    6802          31 :     if (!RunDeferredSpatialIndexUpdate())
    6803           0 :         return OGRERR_FAILURE;
    6804             : 
    6805             :     /* -------------------------------------------------------------------- */
    6806             :     /*      Check that the new column name is not a duplicate.              */
    6807             :     /* -------------------------------------------------------------------- */
    6808             : 
    6809             :     OGRFieldDefn *poFieldDefnToAlter =
    6810          31 :         m_poFeatureDefn->GetFieldDefn(iFieldToAlter);
    6811          62 :     const CPLString osOldColName(poFieldDefnToAlter->GetNameRef());
    6812          31 :     const CPLString osNewColName((nFlagsIn & ALTER_NAME_FLAG)
    6813             :                                      ? CPLString(poNewFieldDefn->GetNameRef())
    6814          62 :                                      : osOldColName);
    6815             : 
    6816             :     const bool bRenameCol =
    6817          60 :         (nFlagsIn & ALTER_NAME_FLAG) &&
    6818          29 :         strcmp(poNewFieldDefn->GetNameRef(), osOldColName) != 0;
    6819          31 :     if (bRenameCol)
    6820             :     {
    6821          54 :         if ((m_pszFidColumn &&
    6822          18 :              strcmp(poNewFieldDefn->GetNameRef(), m_pszFidColumn) == 0) ||
    6823          17 :             (GetGeomType() != wkbNone &&
    6824          17 :              strcmp(poNewFieldDefn->GetNameRef(),
    6825          53 :                     m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()) == 0) ||
    6826          16 :             m_poFeatureDefn->GetFieldIndex(poNewFieldDefn->GetNameRef()) >= 0)
    6827             :         {
    6828           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    6829             :                      "Field name %s is already used for another field",
    6830             :                      poNewFieldDefn->GetNameRef());
    6831           4 :             return OGRERR_FAILURE;
    6832             :         }
    6833             :     }
    6834             : 
    6835             :     /* -------------------------------------------------------------------- */
    6836             :     /*      Build the modified field definition from the flags.             */
    6837             :     /* -------------------------------------------------------------------- */
    6838          54 :     OGRFieldDefn oTmpFieldDefn(poFieldDefnToAlter);
    6839          27 :     bool bUseRewriteSchemaMethod = (m_poDS->m_nSoftTransactionLevel == 0);
    6840          27 :     int nActualFlags = 0;
    6841          27 :     if (bRenameCol)
    6842             :     {
    6843          14 :         nActualFlags |= ALTER_NAME_FLAG;
    6844          14 :         oTmpFieldDefn.SetName(poNewFieldDefn->GetNameRef());
    6845             :     }
    6846          50 :     if ((nFlagsIn & ALTER_TYPE_FLAG) != 0 &&
    6847          23 :         (poFieldDefnToAlter->GetType() != poNewFieldDefn->GetType() ||
    6848          17 :          poFieldDefnToAlter->GetSubType() != poNewFieldDefn->GetSubType()))
    6849             :     {
    6850           8 :         nActualFlags |= ALTER_TYPE_FLAG;
    6851           8 :         oTmpFieldDefn.SetSubType(OFSTNone);
    6852           8 :         oTmpFieldDefn.SetType(poNewFieldDefn->GetType());
    6853           8 :         oTmpFieldDefn.SetSubType(poNewFieldDefn->GetSubType());
    6854             :     }
    6855          71 :     if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) != 0 &&
    6856          44 :         (poFieldDefnToAlter->GetWidth() != poNewFieldDefn->GetWidth() ||
    6857          21 :          poFieldDefnToAlter->GetPrecision() != poNewFieldDefn->GetPrecision()))
    6858             :     {
    6859           2 :         nActualFlags |= ALTER_WIDTH_PRECISION_FLAG;
    6860           2 :         oTmpFieldDefn.SetWidth(poNewFieldDefn->GetWidth());
    6861           2 :         oTmpFieldDefn.SetPrecision(poNewFieldDefn->GetPrecision());
    6862             :     }
    6863          50 :     if ((nFlagsIn & ALTER_NULLABLE_FLAG) != 0 &&
    6864          23 :         poFieldDefnToAlter->IsNullable() != poNewFieldDefn->IsNullable())
    6865             :     {
    6866           7 :         nActualFlags |= ALTER_NULLABLE_FLAG;
    6867           7 :         if (!poNewFieldDefn->IsNullable())
    6868           5 :             bUseRewriteSchemaMethod = false;
    6869           7 :         oTmpFieldDefn.SetNullable(poNewFieldDefn->IsNullable());
    6870             :     }
    6871          50 :     if ((nFlagsIn & ALTER_DEFAULT_FLAG) != 0 &&
    6872          23 :         !((poFieldDefnToAlter->GetDefault() == nullptr &&
    6873          20 :            poNewFieldDefn->GetDefault() == nullptr) ||
    6874           4 :           (poFieldDefnToAlter->GetDefault() != nullptr &&
    6875           3 :            poNewFieldDefn->GetDefault() != nullptr &&
    6876           2 :            strcmp(poFieldDefnToAlter->GetDefault(),
    6877             :                   poNewFieldDefn->GetDefault()) == 0)))
    6878             :     {
    6879           2 :         nActualFlags |= ALTER_DEFAULT_FLAG;
    6880           2 :         oTmpFieldDefn.SetDefault(poNewFieldDefn->GetDefault());
    6881             :     }
    6882          50 :     if ((nFlagsIn & ALTER_UNIQUE_FLAG) != 0 &&
    6883          23 :         poFieldDefnToAlter->IsUnique() != poNewFieldDefn->IsUnique())
    6884             :     {
    6885           2 :         nActualFlags |= ALTER_UNIQUE_FLAG;
    6886           2 :         if (poNewFieldDefn->IsUnique())
    6887           1 :             bUseRewriteSchemaMethod = false;
    6888           2 :         oTmpFieldDefn.SetUnique(poNewFieldDefn->IsUnique());
    6889             :     }
    6890          50 :     if ((nFlagsIn & ALTER_DOMAIN_FLAG) != 0 &&
    6891          23 :         poFieldDefnToAlter->GetDomainName() != poNewFieldDefn->GetDomainName())
    6892             :     {
    6893           3 :         nActualFlags |= ALTER_DOMAIN_FLAG;
    6894           3 :         oTmpFieldDefn.SetDomainName(poNewFieldDefn->GetDomainName());
    6895             :     }
    6896          51 :     if ((nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG) != 0 &&
    6897          24 :         strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
    6898             :                poNewFieldDefn->GetAlternativeNameRef()) != 0)
    6899             :     {
    6900           5 :         nActualFlags |= ALTER_ALTERNATIVE_NAME_FLAG;
    6901           5 :         oTmpFieldDefn.SetAlternativeName(
    6902             :             poNewFieldDefn->GetAlternativeNameRef());
    6903             :     }
    6904          51 :     if ((nFlagsIn & ALTER_COMMENT_FLAG) != 0 &&
    6905          24 :         poFieldDefnToAlter->GetComment() != poNewFieldDefn->GetComment())
    6906             :     {
    6907           3 :         nActualFlags |= ALTER_COMMENT_FLAG;
    6908           3 :         oTmpFieldDefn.SetComment(poNewFieldDefn->GetComment());
    6909             :     }
    6910             : 
    6911             :     /* -------------------------------------------------------------------- */
    6912             :     /*      Build list of old fields, and the list of new fields.           */
    6913             :     /* -------------------------------------------------------------------- */
    6914          54 :     std::vector<OGRFieldDefn *> apoFields;
    6915          54 :     std::vector<OGRFieldDefn *> apoFieldsOld;
    6916          90 :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    6917             :     {
    6918             :         OGRFieldDefn *poFieldDefn;
    6919          63 :         if (iField == iFieldToAlter)
    6920             :         {
    6921          27 :             poFieldDefn = &oTmpFieldDefn;
    6922             :         }
    6923             :         else
    6924             :         {
    6925          36 :             poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
    6926             :         }
    6927          63 :         apoFields.push_back(poFieldDefn);
    6928          63 :         apoFieldsOld.push_back(m_poFeatureDefn->GetFieldDefn(iField));
    6929             :     }
    6930             : 
    6931          54 :     const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
    6932             : 
    6933             :     /* -------------------------------------------------------------------- */
    6934             :     /*      Drop any iterator since we change the DB structure              */
    6935             :     /* -------------------------------------------------------------------- */
    6936          27 :     m_poDS->ResetReadingAllLayers();
    6937             : 
    6938             : #if SQLITE_VERSION_NUMBER >= 3053000L
    6939             :     const bool bUseRenameColumn =
    6940             :         (nActualFlags == ALTER_NAME_FLAG ||
    6941             :          nActualFlags == (ALTER_NAME_FLAG | ALTER_NULLABLE_FLAG));
    6942             : #else
    6943          27 :     const bool bUseRenameColumn = (nActualFlags == ALTER_NAME_FLAG);
    6944             : #endif
    6945          27 :     if (bUseRenameColumn)
    6946           4 :         bUseRewriteSchemaMethod = false;
    6947             : 
    6948          27 :     if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    6949           0 :         return OGRERR_FAILURE;
    6950             : 
    6951          27 :     sqlite3 *hDB = m_poDS->GetDB();
    6952          27 :     OGRErr eErr = OGRERR_NONE;
    6953             : 
    6954             :     /* -------------------------------------------------------------------- */
    6955             :     /*      Drop triggers and index that look like to be related to the     */
    6956             :     /*      column if renaming. We re-install some indexes afterwards.      */
    6957             :     /* -------------------------------------------------------------------- */
    6958           0 :     std::unique_ptr<SQLResult> oTriggers;
    6959             :     // cppcheck-suppress knownConditionTrueFalse
    6960          27 :     if (bRenameCol && !bUseRenameColumn)
    6961             :     {
    6962          10 :         char *pszSQL = sqlite3_mprintf(
    6963             :             "SELECT name, type, sql FROM sqlite_master WHERE "
    6964             :             "type IN ('trigger','index') "
    6965             :             "AND lower(tbl_name)=lower('%q') AND sql LIKE '%%%q%%' LIMIT 10000",
    6966          20 :             m_pszTableName, SQLEscapeName(osOldColName).c_str());
    6967          10 :         oTriggers = SQLQuery(hDB, pszSQL);
    6968          10 :         sqlite3_free(pszSQL);
    6969             : 
    6970          10 :         if (!oTriggers)
    6971             :         {
    6972           0 :             eErr = OGRERR_FAILURE;
    6973             :         }
    6974             : 
    6975          12 :         for (int i = 0; oTriggers && i < oTriggers->RowCount(); i++)
    6976             :         {
    6977             :             pszSQL =
    6978           2 :                 sqlite3_mprintf("DROP %s \"%w\"", oTriggers->GetValue(1, i),
    6979             :                                 oTriggers->GetValue(0, i));
    6980           2 :             eErr = SQLCommand(hDB, pszSQL);
    6981           2 :             sqlite3_free(pszSQL);
    6982             :         }
    6983             :     }
    6984             : 
    6985          27 :     bool bUseRecreateTableMethod =
    6986          27 :         !bUseRenameColumn && !bUseRewriteSchemaMethod;
    6987             : 
    6988             : #if SQLITE_VERSION_NUMBER >= 3053000L
    6989             :     if (eErr == OGRERR_NONE &&
    6990             :         (nActualFlags == ALTER_NULLABLE_FLAG ||
    6991             :          nActualFlags == (ALTER_NAME_FLAG | ALTER_NULLABLE_FLAG)))
    6992             :     {
    6993             :         eErr =
    6994             :             SQLCommand(m_poDS->GetDB(),
    6995             :                        CPLString().Printf(
    6996             :                            "ALTER TABLE \"%s\" ALTER COLUMN \"%s\" %s NOT NULL",
    6997             :                            SQLEscapeName(m_pszTableName).c_str(),
    6998             :                            SQLEscapeName(osOldColName).c_str(),
    6999             :                            poNewFieldDefn->IsNullable() ? "DROP" : "SET"));
    7000             :         bUseRecreateTableMethod = false;
    7001             :         bUseRewriteSchemaMethod = false;
    7002             :     }
    7003             : #endif
    7004             : 
    7005          27 :     if (bUseRenameColumn)
    7006             :     {
    7007           4 :         if (eErr == OGRERR_NONE)
    7008             :         {
    7009           4 :             CPLDebug("GPKG", "Running ALTER TABLE RENAME COLUMN");
    7010           4 :             eErr = SQLCommand(
    7011           4 :                 m_poDS->GetDB(),
    7012           8 :                 CPLString()
    7013             :                     .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
    7014           8 :                             SQLEscapeName(m_pszTableName).c_str(),
    7015           8 :                             SQLEscapeName(osOldColName).c_str(),
    7016          16 :                             SQLEscapeName(osNewColName).c_str())
    7017             :                     .c_str());
    7018             :         }
    7019             :     }
    7020          23 :     else if (bUseRecreateTableMethod)
    7021             :     {
    7022             :         /* --------------------------------------------------------------------
    7023             :          */
    7024             :         /*      If we are within a transaction, we cannot use the method */
    7025             :         /*      that consists in altering the database in a raw way. */
    7026             :         /* --------------------------------------------------------------------
    7027             :          */
    7028             :         const CPLString osFieldListForSelect(
    7029          22 :             BuildSelectFieldList(apoFieldsOld));
    7030             : 
    7031          11 :         if (eErr == OGRERR_NONE)
    7032             :         {
    7033          11 :             eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
    7034             :         }
    7035             :     }
    7036          12 :     else if (bUseRewriteSchemaMethod)
    7037             :     {
    7038             :         /* --------------------------------------------------------------------
    7039             :          */
    7040             :         /*      Rewrite schema in a transaction by altering the database */
    7041             :         /*      schema in a rather raw way, as described at bottom of */
    7042             :         /*      https://www.sqlite.org/lang_altertable.html */
    7043             :         /* --------------------------------------------------------------------
    7044             :          */
    7045             : 
    7046             :         /* --------------------------------------------------------------------
    7047             :          */
    7048             :         /*      Collect schema version number. */
    7049             :         /* --------------------------------------------------------------------
    7050             :          */
    7051          12 :         int nSchemaVersion = SQLGetInteger(hDB, "PRAGMA schema_version", &eErr);
    7052             : 
    7053             :         /* --------------------------------------------------------------------
    7054             :          */
    7055             :         /*      Turn on writable schema. */
    7056             :         /* --------------------------------------------------------------------
    7057             :          */
    7058          12 :         if (eErr == OGRERR_NONE)
    7059             :         {
    7060          12 :             eErr = m_poDS->PragmaCheck("writable_schema=ON", "", 0);
    7061             :         }
    7062             : 
    7063             :         /* --------------------------------------------------------------------
    7064             :          */
    7065             :         /*      Rewrite CREATE TABLE statement. */
    7066             :         /* --------------------------------------------------------------------
    7067             :          */
    7068          12 :         if (eErr == OGRERR_NONE)
    7069             :         {
    7070             :             char *psSQLCreateTable =
    7071          12 :                 sqlite3_mprintf("CREATE TABLE \"%w\" (%s)", m_pszTableName,
    7072             :                                 osColumnsForCreate.c_str());
    7073          12 :             char *pszSQL = sqlite3_mprintf("UPDATE sqlite_master SET sql='%q' "
    7074             :                                            "WHERE type='table' AND name='%q'",
    7075             :                                            psSQLCreateTable, m_pszTableName);
    7076          12 :             eErr = SQLCommand(hDB, pszSQL);
    7077          12 :             sqlite3_free(psSQLCreateTable);
    7078          12 :             sqlite3_free(pszSQL);
    7079             :         }
    7080             : 
    7081             :         /* --------------------------------------------------------------------
    7082             :          */
    7083             :         /*      Increment schema number. */
    7084             :         /* --------------------------------------------------------------------
    7085             :          */
    7086          12 :         if (eErr == OGRERR_NONE)
    7087             :         {
    7088          12 :             char *pszSQL = sqlite3_mprintf("PRAGMA schema_version = %d",
    7089             :                                            nSchemaVersion + 1);
    7090          12 :             eErr = SQLCommand(hDB, pszSQL);
    7091          12 :             sqlite3_free(pszSQL);
    7092             :         }
    7093             : 
    7094             :         /* --------------------------------------------------------------------
    7095             :          */
    7096             :         /*      Turn off writable schema. */
    7097             :         /* --------------------------------------------------------------------
    7098             :          */
    7099          12 :         if (eErr == OGRERR_NONE)
    7100             :         {
    7101          12 :             eErr = m_poDS->PragmaCheck("writable_schema=OFF", "", 0);
    7102             :         }
    7103             :     }
    7104             : 
    7105             :     /* -------------------------------------------------------------------- */
    7106             :     /*      Update auxiliary tables                                        */
    7107             :     /* -------------------------------------------------------------------- */
    7108          27 :     if (bRenameCol && eErr == OGRERR_NONE)
    7109             :     {
    7110          13 :         eErr = RenameFieldInAuxiliaryTables(osOldColName.c_str(),
    7111             :                                             poNewFieldDefn->GetNameRef());
    7112             :     }
    7113             : 
    7114             :     /* -------------------------------------------------------------------- */
    7115             :     /*      Update gpkgext_relations if needed.                             */
    7116             :     /* -------------------------------------------------------------------- */
    7117          27 :     if (bRenameCol && eErr == OGRERR_NONE && m_poDS->HasGpkgextRelationsTable())
    7118             :     {
    7119           2 :         char *pszSQL = sqlite3_mprintf(
    7120             :             "UPDATE gpkgext_relations SET base_primary_column = '%q' WHERE "
    7121             :             "lower(base_table_name) = lower('%q') AND "
    7122             :             "lower(base_primary_column) = lower('%q')",
    7123             :             poNewFieldDefn->GetNameRef(), m_pszTableName, osOldColName.c_str());
    7124           2 :         eErr = SQLCommand(hDB, pszSQL);
    7125           2 :         sqlite3_free(pszSQL);
    7126             : 
    7127           2 :         if (eErr == OGRERR_NONE)
    7128             :         {
    7129             :             pszSQL =
    7130           2 :                 sqlite3_mprintf("UPDATE gpkgext_relations SET "
    7131             :                                 "related_primary_column = '%q' WHERE "
    7132             :                                 "lower(related_table_name) = lower('%q') AND "
    7133             :                                 "lower(related_primary_column) = lower('%q')",
    7134             :                                 poNewFieldDefn->GetNameRef(), m_pszTableName,
    7135             :                                 osOldColName.c_str());
    7136           2 :             eErr = SQLCommand(hDB, pszSQL);
    7137           2 :             sqlite3_free(pszSQL);
    7138             :         }
    7139           2 :         m_poDS->ClearCachedRelationships();
    7140             :     }
    7141             : 
    7142             :     /* -------------------------------------------------------------------- */
    7143             :     /*      Run integrity check only if explicitly required.                */
    7144             :     /* -------------------------------------------------------------------- */
    7145          52 :     if (eErr == OGRERR_NONE &&
    7146          25 :         CPLTestBool(CPLGetConfigOption("OGR_GPKG_INTEGRITY_CHECK", "NO")))
    7147             :     {
    7148           0 :         CPLDebug("GPKG", "Running PRAGMA integrity_check");
    7149           0 :         eErr = m_poDS->PragmaCheck("integrity_check", "ok", 1);
    7150             :     }
    7151             : 
    7152             :     /* -------------------------------------------------------------------- */
    7153             :     /*      Otherwise check foreign key integrity if enforcement of foreign */
    7154             :     /*      kets constraint is enabled.                                     */
    7155             :     /* -------------------------------------------------------------------- */
    7156          52 :     else if (eErr == OGRERR_NONE &&
    7157          25 :              SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
    7158             :     {
    7159           0 :         CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
    7160           0 :         eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
    7161             :     }
    7162             : 
    7163             :     /* -------------------------------------------------------------------- */
    7164             :     /*      Finish                                                          */
    7165             :     /* -------------------------------------------------------------------- */
    7166          27 :     if (eErr == OGRERR_NONE)
    7167             :     {
    7168             : 
    7169          25 :         eErr = m_poDS->SoftCommitTransaction();
    7170             : 
    7171             :         // We need to force database reopening due to schema change
    7172          37 :         if (eErr == OGRERR_NONE && bUseRewriteSchemaMethod &&
    7173          12 :             !m_poDS->ReOpenDB())
    7174             :         {
    7175           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen database");
    7176           0 :             eErr = OGRERR_FAILURE;
    7177             :         }
    7178          25 :         hDB = m_poDS->GetDB();
    7179             : 
    7180             :         /* --------------------------------------------------------------------
    7181             :          */
    7182             :         /*      Recreate indices. */
    7183             :         /* --------------------------------------------------------------------
    7184             :          */
    7185          27 :         for (int i = 0;
    7186          27 :              oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
    7187             :         {
    7188           2 :             if (EQUAL(oTriggers->GetValue(1, i), "index"))
    7189             :             {
    7190           4 :                 CPLString osSQL(oTriggers->GetValue(2, i));
    7191             :                 // CREATE INDEX idx_name ON table_name(column_name)
    7192           2 :                 char **papszTokens = SQLTokenize(osSQL);
    7193           2 :                 if (CSLCount(papszTokens) == 8 &&
    7194           2 :                     EQUAL(papszTokens[0], "CREATE") &&
    7195           2 :                     EQUAL(papszTokens[1], "INDEX") &&
    7196           6 :                     EQUAL(papszTokens[3], "ON") && EQUAL(papszTokens[5], "(") &&
    7197           2 :                     EQUAL(papszTokens[7], ")"))
    7198             :                 {
    7199           2 :                     osSQL = "CREATE INDEX ";
    7200           2 :                     osSQL += papszTokens[2];
    7201           2 :                     osSQL += " ON ";
    7202           2 :                     osSQL += papszTokens[4];
    7203           2 :                     osSQL += "(\"";
    7204           2 :                     osSQL += SQLEscapeName(osNewColName);
    7205           2 :                     osSQL += "\")";
    7206           2 :                     eErr = SQLCommand(hDB, osSQL);
    7207             :                 }
    7208           2 :                 CSLDestroy(papszTokens);
    7209             :             }
    7210             :         }
    7211             : 
    7212          25 :         if (eErr == OGRERR_NONE)
    7213             :         {
    7214             : 
    7215          25 :             if (m_poDS->IsInTransaction())
    7216             :             {
    7217             :                 m_apoFieldDefnChanges.emplace_back(
    7218           6 :                     std::make_unique<OGRFieldDefn>(poFieldDefnToAlter),
    7219           6 :                     iFieldToAlter, FieldChangeType::ALTER_FIELD,
    7220          18 :                     m_poDS->GetCurrentSavepoint());
    7221             :             }
    7222             : 
    7223          50 :             auto oTemporaryUnsealer(poFieldDefnToAlter->GetTemporaryUnsealer());
    7224          25 :             bool bNeedsEntryInGpkgDataColumns = false;
    7225             : 
    7226             :             // field type
    7227          25 :             if (nActualFlags & ALTER_TYPE_FLAG)
    7228             :             {
    7229           8 :                 poFieldDefnToAlter->SetSubType(OFSTNone);
    7230           8 :                 poFieldDefnToAlter->SetType(poNewFieldDefn->GetType());
    7231           8 :                 poFieldDefnToAlter->SetSubType(poNewFieldDefn->GetSubType());
    7232             :             }
    7233          37 :             if (poFieldDefnToAlter->GetType() == OFTString &&
    7234          12 :                 poFieldDefnToAlter->GetSubType() == OFSTJSON)
    7235             :             {
    7236           1 :                 bNeedsEntryInGpkgDataColumns = true;
    7237             :             }
    7238             : 
    7239             :             // name
    7240          25 :             if (nActualFlags & ALTER_NAME_FLAG)
    7241             :             {
    7242          13 :                 poFieldDefnToAlter->SetName(poNewFieldDefn->GetNameRef());
    7243             :             }
    7244             : 
    7245             :             // width/precision
    7246          25 :             if (nActualFlags & ALTER_WIDTH_PRECISION_FLAG)
    7247             :             {
    7248           2 :                 poFieldDefnToAlter->SetWidth(poNewFieldDefn->GetWidth());
    7249           2 :                 poFieldDefnToAlter->SetPrecision(
    7250             :                     poNewFieldDefn->GetPrecision());
    7251             :             }
    7252             : 
    7253             :             // constraints
    7254          25 :             if (nActualFlags & ALTER_NULLABLE_FLAG)
    7255           5 :                 poFieldDefnToAlter->SetNullable(poNewFieldDefn->IsNullable());
    7256          25 :             if (nActualFlags & ALTER_DEFAULT_FLAG)
    7257           2 :                 poFieldDefnToAlter->SetDefault(poNewFieldDefn->GetDefault());
    7258          25 :             if (nActualFlags & ALTER_UNIQUE_FLAG)
    7259           2 :                 poFieldDefnToAlter->SetUnique(poNewFieldDefn->IsUnique());
    7260             : 
    7261             :             // domain
    7262          28 :             if ((nActualFlags & ALTER_DOMAIN_FLAG) &&
    7263           3 :                 poFieldDefnToAlter->GetDomainName() !=
    7264           3 :                     poNewFieldDefn->GetDomainName())
    7265             :             {
    7266           3 :                 poFieldDefnToAlter->SetDomainName(
    7267             :                     poNewFieldDefn->GetDomainName());
    7268             :             }
    7269          25 :             if (!poFieldDefnToAlter->GetDomainName().empty())
    7270             :             {
    7271           3 :                 bNeedsEntryInGpkgDataColumns = true;
    7272             :             }
    7273             : 
    7274             :             // alternative name
    7275          30 :             if ((nActualFlags & ALTER_ALTERNATIVE_NAME_FLAG) &&
    7276           5 :                 strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
    7277             :                        poNewFieldDefn->GetAlternativeNameRef()) != 0)
    7278             :             {
    7279           5 :                 poFieldDefnToAlter->SetAlternativeName(
    7280             :                     poNewFieldDefn->GetAlternativeNameRef());
    7281             :             }
    7282          50 :             if (!std::string(poFieldDefnToAlter->GetAlternativeNameRef())
    7283          25 :                      .empty())
    7284             :             {
    7285           7 :                 bNeedsEntryInGpkgDataColumns = true;
    7286             :             }
    7287             : 
    7288             :             // comment
    7289          28 :             if ((nActualFlags & ALTER_COMMENT_FLAG) &&
    7290           3 :                 poFieldDefnToAlter->GetComment() !=
    7291           3 :                     poNewFieldDefn->GetComment())
    7292             :             {
    7293           3 :                 poFieldDefnToAlter->SetComment(poNewFieldDefn->GetComment());
    7294             :             }
    7295          25 :             if (!poFieldDefnToAlter->GetComment().empty())
    7296             :             {
    7297           3 :                 bNeedsEntryInGpkgDataColumns = true;
    7298             :             }
    7299             : 
    7300          25 :             if (m_poDS->HasDataColumnsTable())
    7301             :             {
    7302          14 :                 char *pszSQL = sqlite3_mprintf(
    7303             :                     "DELETE FROM gpkg_data_columns WHERE "
    7304             :                     "lower(table_name) = lower('%q') AND "
    7305             :                     "lower(column_name) = lower('%q')",
    7306             :                     m_pszTableName, poFieldDefnToAlter->GetNameRef());
    7307          14 :                 eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    7308          14 :                 sqlite3_free(pszSQL);
    7309             :             }
    7310             : 
    7311          25 :             if (bNeedsEntryInGpkgDataColumns)
    7312             :             {
    7313          12 :                 if (!DoSpecialProcessingForColumnCreation(poFieldDefnToAlter))
    7314           0 :                     eErr = OGRERR_FAILURE;
    7315             :             }
    7316             : 
    7317          25 :             ResetReading();
    7318             :         }
    7319             :     }
    7320             :     else
    7321             :     {
    7322           2 :         m_poDS->SoftRollbackTransaction();
    7323             :     }
    7324             : 
    7325          27 :     return eErr;
    7326             : }
    7327             : 
    7328             : /************************************************************************/
    7329             : /*                         AlterGeomFieldDefn()                         */
    7330             : /************************************************************************/
    7331             : 
    7332           8 : OGRErr OGRGeoPackageTableLayer::AlterGeomFieldDefn(
    7333             :     int iGeomFieldToAlter, const OGRGeomFieldDefn *poNewGeomFieldDefn,
    7334             :     int nFlagsIn)
    7335             : {
    7336           8 :     if (!m_bFeatureDefnCompleted)
    7337           1 :         GetLayerDefn();
    7338           8 :     if (!CheckUpdatableTable("AlterGeomFieldDefn"))
    7339           0 :         return OGRERR_FAILURE;
    7340             : 
    7341          16 :     if (iGeomFieldToAlter < 0 ||
    7342           8 :         iGeomFieldToAlter >= m_poFeatureDefn->GetGeomFieldCount())
    7343             :     {
    7344           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    7345           0 :         return OGRERR_FAILURE;
    7346             :     }
    7347             : 
    7348             :     /* -------------------------------------------------------------------- */
    7349             :     /*      Deferred actions, reset state.                                   */
    7350             :     /* -------------------------------------------------------------------- */
    7351           8 :     ResetReading();
    7352           8 :     RunDeferredCreationIfNecessary();
    7353           8 :     if (m_bThreadRTreeStarted)
    7354           0 :         CancelAsyncRTree();
    7355           8 :     if (!RunDeferredSpatialIndexUpdate())
    7356           0 :         return OGRERR_FAILURE;
    7357           8 :     RevertWorkaroundUpdate1TriggerIssue();
    7358             : 
    7359             :     /* -------------------------------------------------------------------- */
    7360             :     /*      Drop any iterator since we change the DB structure              */
    7361             :     /* -------------------------------------------------------------------- */
    7362           8 :     m_poDS->ResetReadingAllLayers();
    7363             : 
    7364           8 :     auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter);
    7365          16 :     auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
    7366             : 
    7367           8 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
    7368             :     {
    7369             :         // could be potentially done. Requires rewriting the CREATE TABLE
    7370             :         // statement
    7371           0 :         if (poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
    7372             :         {
    7373           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7374             :                      "Altering the geometry field type is not currently "
    7375             :                      "supported for "
    7376             :                      "GeoPackage");
    7377           0 :             return OGRERR_FAILURE;
    7378             :         }
    7379             :     }
    7380             : 
    7381           8 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
    7382             :     {
    7383             :         // could be potentially done. Requires rewriting the CREATE TABLE
    7384             :         // statement
    7385           0 :         if (poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
    7386             :         {
    7387           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7388             :                      "Altering the nullable state of the geometry field "
    7389             :                      "is not currently supported for GeoPackage");
    7390           0 :             return OGRERR_FAILURE;
    7391             :         }
    7392             :     }
    7393             : 
    7394          12 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0 &&
    7395           4 :         strcmp(poGeomFieldDefn->GetNameRef(),
    7396             :                poNewGeomFieldDefn->GetNameRef()) != 0)
    7397             :     {
    7398           4 :         const bool bHasSpatialIndex = HasSpatialIndex();
    7399             : 
    7400           4 :         if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    7401           0 :             return OGRERR_FAILURE;
    7402             : 
    7403             :         // Rename geometry field
    7404           4 :         auto eErr = SQLCommand(
    7405           4 :             m_poDS->GetDB(),
    7406           4 :             CPLString()
    7407             :                 .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
    7408           8 :                         SQLEscapeName(m_pszTableName).c_str(),
    7409           8 :                         SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7410          16 :                         SQLEscapeName(poNewGeomFieldDefn->GetNameRef()).c_str())
    7411             :                 .c_str());
    7412           4 :         if (eErr != OGRERR_NONE)
    7413             :         {
    7414           0 :             m_poDS->SoftRollbackTransaction();
    7415           0 :             return OGRERR_FAILURE;
    7416             :         }
    7417             : 
    7418             :         // Update gpkg_geometry_columns
    7419           4 :         eErr = SQLCommand(
    7420           4 :             m_poDS->GetDB(),
    7421           4 :             CPLString()
    7422             :                 .Printf(
    7423             :                     "UPDATE gpkg_geometry_columns SET column_name = '%s' "
    7424             :                     "WHERE lower(table_name) = lower('%s') "
    7425             :                     "AND lower(column_name) = lower('%s')",
    7426           8 :                     SQLEscapeLiteral(poNewGeomFieldDefn->GetNameRef()).c_str(),
    7427           8 :                     SQLEscapeLiteral(m_pszTableName).c_str(),
    7428          16 :                     SQLEscapeLiteral(poGeomFieldDefn->GetNameRef()).c_str())
    7429             :                 .c_str());
    7430           4 :         if (eErr != OGRERR_NONE)
    7431             :         {
    7432           0 :             m_poDS->SoftRollbackTransaction();
    7433           0 :             return OGRERR_FAILURE;
    7434             :         }
    7435             : 
    7436             :         // Update auxiliary tables
    7437           4 :         eErr = RenameFieldInAuxiliaryTables(poGeomFieldDefn->GetNameRef(),
    7438             :                                             poNewGeomFieldDefn->GetNameRef());
    7439           4 :         if (eErr != OGRERR_NONE)
    7440             :         {
    7441           0 :             m_poDS->SoftRollbackTransaction();
    7442           0 :             return OGRERR_FAILURE;
    7443             :         }
    7444             : 
    7445           4 :         std::string osNewRTreeName;
    7446           4 :         if (bHasSpatialIndex)
    7447             :         {
    7448           4 :             osNewRTreeName = "rtree_";
    7449           4 :             osNewRTreeName += m_pszTableName;
    7450           4 :             osNewRTreeName += "_";
    7451           4 :             osNewRTreeName += poNewGeomFieldDefn->GetNameRef();
    7452             : 
    7453             :             // Rename spatial index tables (not strictly needed, but for
    7454             :             // consistency)
    7455             :             eErr =
    7456           4 :                 SQLCommand(m_poDS->GetDB(),
    7457           4 :                            CPLString().Printf(
    7458             :                                "ALTER TABLE \"%s\" RENAME TO \"%s\"",
    7459           8 :                                SQLEscapeName(m_osRTreeName.c_str()).c_str(),
    7460          12 :                                SQLEscapeName(osNewRTreeName.c_str()).c_str()));
    7461           4 :             if (eErr != OGRERR_NONE)
    7462             :             {
    7463           0 :                 m_poDS->SoftRollbackTransaction();
    7464           0 :                 return OGRERR_FAILURE;
    7465             :             }
    7466             : 
    7467             :             // Finally rename triggers (not strictly needed, but for
    7468             :             // consistency)
    7469           4 :             std::string osTriggerSQL;
    7470           4 :             osTriggerSQL = ReturnSQLDropSpatialIndexTriggers();
    7471           4 :             osTriggerSQL += ";";
    7472           8 :             osTriggerSQL += ReturnSQLCreateSpatialIndexTriggers(
    7473           4 :                 nullptr, poNewGeomFieldDefn->GetNameRef());
    7474           4 :             eErr = SQLCommand(m_poDS->GetDB(), osTriggerSQL.c_str());
    7475           4 :             if (eErr != OGRERR_NONE)
    7476             :             {
    7477           0 :                 m_poDS->SoftRollbackTransaction();
    7478           0 :                 return OGRERR_FAILURE;
    7479             :             }
    7480             :         }
    7481             : 
    7482           4 :         eErr = m_poDS->SoftCommitTransaction();
    7483           4 :         if (eErr != OGRERR_NONE)
    7484             :         {
    7485           0 :             return OGRERR_FAILURE;
    7486             :         }
    7487             : 
    7488           4 :         poGeomFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
    7489             : 
    7490           4 :         if (bHasSpatialIndex)
    7491             :         {
    7492           4 :             m_osRTreeName = osNewRTreeName;
    7493             :         }
    7494             :     }
    7495             : 
    7496           8 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0 ||
    7497           5 :         (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
    7498             :     {
    7499           4 :         const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
    7500           4 :         const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
    7501             : 
    7502           4 :         OGRSpatialReferenceRefCountedPtr poNewSRS;
    7503           4 :         if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0)
    7504             :         {
    7505           3 :             if (poNewSRSRef != nullptr)
    7506             :             {
    7507             :                 poNewSRS =
    7508           2 :                     OGRSpatialReferenceRefCountedPtr::makeClone(poNewSRSRef);
    7509           2 :                 if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) ==
    7510             :                     0)
    7511             :                 {
    7512           2 :                     if (poOldSRS)
    7513           1 :                         poNewSRS->SetCoordinateEpoch(
    7514             :                             poOldSRS->GetCoordinateEpoch());
    7515             :                 }
    7516             :             }
    7517             :         }
    7518           1 :         else if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
    7519             :         {
    7520           1 :             if (poOldSRS != nullptr)
    7521             :             {
    7522             :                 poNewSRS =
    7523           1 :                     OGRSpatialReferenceRefCountedPtr::makeClone(poOldSRS);
    7524           1 :                 if (poNewSRSRef)
    7525           1 :                     poNewSRS->SetCoordinateEpoch(
    7526             :                         poNewSRSRef->GetCoordinateEpoch());
    7527             :             }
    7528             :         }
    7529             : 
    7530           4 :         const char *const apszOptions[] = {
    7531             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    7532           4 :         if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
    7533          10 :             (poOldSRS != nullptr && poNewSRS == nullptr) ||
    7534           2 :             (poOldSRS != nullptr && poNewSRS != nullptr &&
    7535           2 :              !poOldSRS->IsSame(poNewSRS.get(), apszOptions)))
    7536             :         {
    7537             :             // Temporary remove foreign key checks
    7538             :             const GPKGTemporaryForeignKeyCheckDisabler
    7539           4 :                 oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
    7540             : 
    7541           4 :             if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    7542           0 :                 return OGRERR_FAILURE;
    7543             : 
    7544           4 :             const int nNewSRID = m_poDS->GetSrsId(poNewSRS.get());
    7545             : 
    7546             :             // Replace the old SRID by the new ones in geometry blobs
    7547           4 :             int32_t nNewSRID_LSB = nNewSRID;
    7548           4 :             CPL_LSBPTR32(&nNewSRID_LSB);
    7549           4 :             GByte abySRID_LSB[5] = {0, 0, 0, 0};
    7550           4 :             memcpy(abySRID_LSB, &nNewSRID_LSB, 4);
    7551           4 :             char *pszSRID_LSB_HEX = CPLBinaryToHex(4, abySRID_LSB);
    7552             : 
    7553           4 :             int32_t nNewSRID_MSB = nNewSRID;
    7554           4 :             CPL_MSBPTR32(&nNewSRID_MSB);
    7555           4 :             GByte abySRID_MSB[5] = {0, 0, 0, 0};
    7556           4 :             memcpy(abySRID_MSB, &nNewSRID_MSB, 4);
    7557           4 :             char *pszSRID_MSB_HEX = CPLBinaryToHex(4, abySRID_MSB);
    7558             : 
    7559             :             // Black magic below...
    7560             :             // the substr(hex(...) IN ('0','2',...'E') checks if bit 0 of the
    7561             :             // 4th byte is 0 and use that to decide how to replace the old SRID
    7562             :             // by the new one.
    7563           4 :             CPLString osSQL;
    7564             :             osSQL.Printf(
    7565             :                 "UPDATE \"%s\" SET \"%s\" = "
    7566             :                 "CAST(substr(\"%s\", 1, 4) || "
    7567             :                 "(CASE WHEN substr(hex(substr(\"%s\", 4, 1)),2) IN "
    7568             :                 "('0','2','4','6','8','A','C','E') "
    7569             :                 "THEN x'%s' ELSE x'%s' END) || substr(\"%s\", 9) AS BLOB) "
    7570             :                 "WHERE \"%s\" IS NOT NULL",
    7571           8 :                 SQLEscapeName(m_pszTableName).c_str(),
    7572           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7573           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7574           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7575             :                 pszSRID_MSB_HEX, pszSRID_LSB_HEX,
    7576           8 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
    7577          28 :                 SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str());
    7578           4 :             OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL.c_str());
    7579           4 :             CPLFree(pszSRID_MSB_HEX);
    7580           4 :             CPLFree(pszSRID_LSB_HEX);
    7581           4 :             if (eErr != OGRERR_NONE)
    7582             :             {
    7583           0 :                 m_poDS->SoftRollbackTransaction();
    7584           0 :                 return OGRERR_FAILURE;
    7585             :             }
    7586             : 
    7587           4 :             char *pszSQL = sqlite3_mprintf(
    7588             :                 "UPDATE gpkg_contents SET srs_id = %d WHERE table_name = '%q'",
    7589             :                 nNewSRID, m_pszTableName);
    7590           4 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    7591           4 :             sqlite3_free(pszSQL);
    7592           4 :             if (eErr != OGRERR_NONE)
    7593             :             {
    7594           0 :                 m_poDS->SoftRollbackTransaction();
    7595           0 :                 return OGRERR_FAILURE;
    7596             :             }
    7597             : 
    7598           4 :             pszSQL = sqlite3_mprintf(
    7599             :                 "UPDATE gpkg_geometry_columns SET srs_id = %d WHERE "
    7600             :                 "table_name = '%q' AND column_name = '%q'",
    7601             :                 nNewSRID, m_pszTableName, poGeomFieldDefn->GetNameRef());
    7602           4 :             eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
    7603           4 :             sqlite3_free(pszSQL);
    7604           4 :             if (eErr != OGRERR_NONE)
    7605             :             {
    7606           0 :                 m_poDS->SoftRollbackTransaction();
    7607           0 :                 return OGRERR_FAILURE;
    7608             :             }
    7609             : 
    7610           4 :             if (m_poDS->SoftCommitTransaction() != OGRERR_NONE)
    7611             :             {
    7612           0 :                 return OGRERR_FAILURE;
    7613             :             }
    7614             : 
    7615           4 :             m_iSrs = nNewSRID;
    7616           4 :             poGeomFieldDefn->SetSpatialRef(poNewSRS.get());
    7617             :         }
    7618             :     }
    7619             : 
    7620           8 :     return OGRERR_NONE;
    7621             : }
    7622             : 
    7623             : /************************************************************************/
    7624             : /*                           ReorderFields()                            */
    7625             : /************************************************************************/
    7626             : 
    7627           4 : OGRErr OGRGeoPackageTableLayer::ReorderFields(int *panMap)
    7628             : {
    7629           4 :     if (!m_bFeatureDefnCompleted)
    7630           0 :         GetLayerDefn();
    7631           4 :     if (!CheckUpdatableTable("ReorderFields"))
    7632           2 :         return OGRERR_FAILURE;
    7633             : 
    7634           2 :     if (m_poFeatureDefn->GetFieldCount() == 0)
    7635           0 :         return OGRERR_NONE;
    7636             : 
    7637           2 :     OGRErr eErr = OGRCheckPermutation(panMap, m_poFeatureDefn->GetFieldCount());
    7638           2 :     if (eErr != OGRERR_NONE)
    7639           1 :         return eErr;
    7640             : 
    7641             :     /* -------------------------------------------------------------------- */
    7642             :     /*      Deferred actions, reset state.                                   */
    7643             :     /* -------------------------------------------------------------------- */
    7644           1 :     ResetReading();
    7645           1 :     RunDeferredCreationIfNecessary();
    7646           1 :     if (m_bThreadRTreeStarted)
    7647           0 :         CancelAsyncRTree();
    7648           1 :     if (!RunDeferredSpatialIndexUpdate())
    7649           0 :         return OGRERR_FAILURE;
    7650             : 
    7651             :     /* -------------------------------------------------------------------- */
    7652             :     /*      Drop any iterator since we change the DB structure              */
    7653             :     /* -------------------------------------------------------------------- */
    7654           1 :     m_poDS->ResetReadingAllLayers();
    7655             : 
    7656             :     /* -------------------------------------------------------------------- */
    7657             :     /*      Build list of old fields, and the list of new fields.           */
    7658             :     /* -------------------------------------------------------------------- */
    7659           2 :     std::vector<OGRFieldDefn *> apoFields;
    7660           3 :     for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
    7661             :     {
    7662             :         OGRFieldDefn *poFieldDefn =
    7663           2 :             m_poFeatureDefn->GetFieldDefn(panMap[iField]);
    7664           2 :         apoFields.push_back(poFieldDefn);
    7665             :     }
    7666             : 
    7667           2 :     const CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
    7668           2 :     const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
    7669             : 
    7670             :     /* -------------------------------------------------------------------- */
    7671             :     /*      Recreate table in a transaction                                 */
    7672             :     /* -------------------------------------------------------------------- */
    7673           1 :     if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
    7674           0 :         return OGRERR_FAILURE;
    7675             : 
    7676           1 :     eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
    7677             : 
    7678             :     /* -------------------------------------------------------------------- */
    7679             :     /*      Finish                                                          */
    7680             :     /* -------------------------------------------------------------------- */
    7681           1 :     if (eErr == OGRERR_NONE)
    7682             :     {
    7683           1 :         eErr = m_poDS->SoftCommitTransaction();
    7684             : 
    7685           1 :         if (eErr == OGRERR_NONE)
    7686             :         {
    7687           1 :             eErr = whileUnsealing(m_poFeatureDefn)->ReorderFieldDefns(panMap);
    7688             :         }
    7689             : 
    7690           1 :         ResetReading();
    7691             :     }
    7692             :     else
    7693             :     {
    7694           0 :         m_poDS->SoftRollbackTransaction();
    7695             :     }
    7696             : 
    7697           1 :     return eErr;
    7698             : }
    7699             : 
    7700             : /************************************************************************/
    7701             : /*                   OGR_GPKG_GeometryTypeAggregate()                   */
    7702             : /************************************************************************/
    7703             : 
    7704             : namespace
    7705             : {
    7706             : struct GeometryTypeAggregateContext
    7707             : {
    7708             :     sqlite3 *m_hDB = nullptr;
    7709             :     int m_nFlags = 0;
    7710             :     bool m_bIsGeometryTypeAggregateInterrupted = false;
    7711             :     std::map<OGRwkbGeometryType, int64_t> m_oMapCount{};
    7712             :     std::set<OGRwkbGeometryType> m_oSetNotNull{};
    7713             : 
    7714          14 :     explicit GeometryTypeAggregateContext(sqlite3 *hDB, int nFlags)
    7715          14 :         : m_hDB(hDB), m_nFlags(nFlags)
    7716             :     {
    7717          14 :     }
    7718             : 
    7719             :     GeometryTypeAggregateContext(const GeometryTypeAggregateContext &) = delete;
    7720             :     GeometryTypeAggregateContext &
    7721             :     operator=(const GeometryTypeAggregateContext &) = delete;
    7722             : 
    7723           2 :     void SetGeometryTypeAggregateInterrupted(bool b)
    7724             :     {
    7725           2 :         m_bIsGeometryTypeAggregateInterrupted = b;
    7726           2 :         if (b)
    7727           2 :             sqlite3_interrupt(m_hDB);
    7728           2 :     }
    7729             : };
    7730             : 
    7731             : }  // namespace
    7732             : 
    7733        1376 : static void OGR_GPKG_GeometryTypeAggregate_Step(sqlite3_context *pContext,
    7734             :                                                 int /*argc*/,
    7735             :                                                 sqlite3_value **argv)
    7736             : {
    7737             :     const GByte *pabyBLOB =
    7738        1376 :         reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
    7739             : 
    7740             :     auto poContext = static_cast<GeometryTypeAggregateContext *>(
    7741        1376 :         sqlite3_user_data(pContext));
    7742             : 
    7743        1376 :     OGRwkbGeometryType eGeometryType = wkbNone;
    7744        1376 :     OGRErr err = OGRERR_FAILURE;
    7745        1376 :     if (pabyBLOB != nullptr)
    7746             :     {
    7747             :         GPkgHeader sHeader;
    7748        1353 :         const int nBLOBLen = sqlite3_value_bytes(argv[0]);
    7749        2706 :         if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, &sHeader) == OGRERR_NONE &&
    7750        1353 :             static_cast<size_t>(nBLOBLen) >= sHeader.nHeaderLen + 5)
    7751             :         {
    7752        1353 :             err = OGRReadWKBGeometryType(pabyBLOB + sHeader.nHeaderLen,
    7753             :                                          wkbVariantIso, &eGeometryType);
    7754        1353 :             if (eGeometryType == wkbGeometryCollection25D &&
    7755           4 :                 (poContext->m_nFlags & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
    7756             :             {
    7757             :                 auto poGeom = std::unique_ptr<OGRGeometry>(
    7758           2 :                     GPkgGeometryToOGR(pabyBLOB, nBLOBLen, nullptr));
    7759           1 :                 if (poGeom)
    7760             :                 {
    7761           1 :                     const auto poGC = poGeom->toGeometryCollection();
    7762           1 :                     if (poGC->getNumGeometries() > 0)
    7763             :                     {
    7764             :                         auto eSubGeomType =
    7765           1 :                             poGC->getGeometryRef(0)->getGeometryType();
    7766           1 :                         if (eSubGeomType == wkbTINZ)
    7767           1 :                             eGeometryType = wkbTINZ;
    7768             :                     }
    7769             :                 }
    7770             :             }
    7771             :         }
    7772             :     }
    7773             :     else
    7774             :     {
    7775             :         // NULL geometry
    7776          23 :         err = OGRERR_NONE;
    7777             :     }
    7778        1376 :     if (err == OGRERR_NONE)
    7779             :     {
    7780        1376 :         ++poContext->m_oMapCount[eGeometryType];
    7781        1376 :         if (eGeometryType != wkbNone &&
    7782        1353 :             (poContext->m_nFlags & OGR_GGT_STOP_IF_MIXED) != 0)
    7783             :         {
    7784           4 :             poContext->m_oSetNotNull.insert(eGeometryType);
    7785           4 :             if (poContext->m_oSetNotNull.size() == 2)
    7786             :             {
    7787           2 :                 poContext->SetGeometryTypeAggregateInterrupted(true);
    7788             :             }
    7789             :         }
    7790             :     }
    7791        1376 : }
    7792             : 
    7793          11 : static void OGR_GPKG_GeometryTypeAggregate_Finalize(sqlite3_context *)
    7794             : {
    7795          11 : }
    7796             : 
    7797             : /************************************************************************/
    7798             : /*                          GetGeometryTypes()                          */
    7799             : /************************************************************************/
    7800             : 
    7801          15 : OGRGeometryTypeCounter *OGRGeoPackageTableLayer::GetGeometryTypes(
    7802             :     int iGeomField, int nFlagsGGT, int &nEntryCountOut,
    7803             :     GDALProgressFunc pfnProgress, void *pProgressData)
    7804             : {
    7805          15 :     const OGRFeatureDefn *poDefn = GetLayerDefn();
    7806             : 
    7807             :     /* -------------------------------------------------------------------- */
    7808             :     /*      Deferred actions, reset state.                                   */
    7809             :     /* -------------------------------------------------------------------- */
    7810          15 :     RunDeferredCreationIfNecessary();
    7811          15 :     if (!RunDeferredSpatialIndexUpdate())
    7812             :     {
    7813           0 :         nEntryCountOut = 0;
    7814           0 :         return nullptr;
    7815             :     }
    7816             : 
    7817          15 :     const int nGeomFieldCount = poDefn->GetGeomFieldCount();
    7818          15 :     if (iGeomField < 0 || iGeomField >= nGeomFieldCount)
    7819             :     {
    7820           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for iGeomField");
    7821           1 :         nEntryCountOut = 0;
    7822           1 :         return nullptr;
    7823             :     }
    7824             : 
    7825             : #ifdef SQLITE_HAS_PROGRESS_HANDLER
    7826             :     struct CancelCallback
    7827             :     {
    7828             :         sqlite3 *m_hDB = nullptr;
    7829             :         GDALProgressFunc m_pfnProgress = nullptr;
    7830             :         void *m_pProgressData = nullptr;
    7831             : 
    7832          14 :         CancelCallback(sqlite3 *hDB, GDALProgressFunc pfnProgressIn,
    7833             :                        void *pProgressDataIn)
    7834          14 :             : m_hDB(hDB),
    7835          14 :               m_pfnProgress(pfnProgressIn != GDALDummyProgress ? pfnProgressIn
    7836             :                                                                : nullptr),
    7837          14 :               m_pProgressData(pProgressDataIn)
    7838             :         {
    7839          14 :             if (m_pfnProgress)
    7840             :             {
    7841             :                 // If changing that value, update
    7842             :                 // ogr_gpkg.py::test_ogr_gpkg_get_geometry_types
    7843           2 :                 constexpr int COUNT_VM_INSTRUCTIONS = 1000;
    7844           2 :                 sqlite3_progress_handler(m_hDB, COUNT_VM_INSTRUCTIONS,
    7845             :                                          ProgressHandler, this);
    7846             :             }
    7847          14 :         }
    7848             : 
    7849          14 :         ~CancelCallback()
    7850          14 :         {
    7851          14 :             if (m_pfnProgress)
    7852             :             {
    7853           2 :                 sqlite3_progress_handler(m_hDB, 0, nullptr, nullptr);
    7854             :             }
    7855          14 :         }
    7856             : 
    7857             :         CancelCallback(const CancelCallback &) = delete;
    7858             :         CancelCallback &operator=(const CancelCallback &) = delete;
    7859             : 
    7860           1 :         static int ProgressHandler(void *pData)
    7861             :         {
    7862           1 :             CancelCallback *psCancelCallback =
    7863             :                 static_cast<CancelCallback *>(pData);
    7864           1 :             return psCancelCallback->m_pfnProgress != nullptr &&
    7865           1 :                            psCancelCallback->m_pfnProgress(
    7866             :                                0.0, "", psCancelCallback->m_pProgressData)
    7867           2 :                        ? 0
    7868           1 :                        : 1;
    7869             :         }
    7870             :     };
    7871             : 
    7872          28 :     CancelCallback oCancelCallback(m_poDS->hDB, pfnProgress, pProgressData);
    7873             : #else
    7874             :     CPL_IGNORE_RET_VAL(pfnProgress);
    7875             :     CPL_IGNORE_RET_VAL(pProgressData);
    7876             : #endif
    7877             : 
    7878             :     // For internal use only
    7879             : 
    7880          28 :     GeometryTypeAggregateContext sContext(m_poDS->hDB, nFlagsGGT);
    7881             : 
    7882          28 :     CPLString osFuncName;
    7883          14 :     osFuncName.Printf("OGR_GPKG_GeometryTypeAggregate_INTERNAL_%p", &sContext);
    7884             : 
    7885          14 :     sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
    7886             :                             &sContext, nullptr,
    7887             :                             OGR_GPKG_GeometryTypeAggregate_Step,
    7888             :                             OGR_GPKG_GeometryTypeAggregate_Finalize);
    7889             : 
    7890             :     // Using this aggregate function is slightly faster than using
    7891             :     // sqlite3_step() to loop over each geometry blob (650 ms vs 750ms on a 1.6
    7892             :     // GB db with 3.3 million features)
    7893          28 :     char *pszSQL = sqlite3_mprintf(
    7894             :         "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
    7895          14 :         poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
    7896          30 :         m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
    7897          14 :     char *pszErrMsg = nullptr;
    7898             :     const int rc =
    7899          14 :         sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
    7900             : 
    7901             :     // Delete function
    7902          14 :     sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
    7903             :                             nullptr, nullptr, nullptr, nullptr);
    7904             : 
    7905          14 :     if (rc != SQLITE_OK && !sContext.m_bIsGeometryTypeAggregateInterrupted)
    7906             :     {
    7907           1 :         if (rc != SQLITE_INTERRUPT)
    7908             :         {
    7909           0 :             CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
    7910             :                      pszSQL, pszErrMsg);
    7911             :         }
    7912           1 :         sqlite3_free(pszErrMsg);
    7913           1 :         sqlite3_free(pszSQL);
    7914           1 :         nEntryCountOut = 0;
    7915           1 :         return nullptr;
    7916             :     }
    7917          13 :     sqlite3_free(pszErrMsg);
    7918          13 :     sqlite3_free(pszSQL);
    7919             : 
    7920             :     // Format result
    7921          13 :     nEntryCountOut = static_cast<int>(sContext.m_oMapCount.size());
    7922             :     OGRGeometryTypeCounter *pasRet = static_cast<OGRGeometryTypeCounter *>(
    7923          13 :         CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
    7924          13 :     int i = 0;
    7925          47 :     for (const auto &sEntry : sContext.m_oMapCount)
    7926             :     {
    7927          34 :         pasRet[i].eGeomType = sEntry.first;
    7928          34 :         pasRet[i].nCount = sEntry.second;
    7929          34 :         ++i;
    7930             :     }
    7931          13 :     return pasRet;
    7932             : }
    7933             : 
    7934             : /************************************************************************/
    7935             : /*                    OGR_GPKG_FillArrowArray_Step()                    */
    7936             : /************************************************************************/
    7937             : 
    7938             : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
    7939             :                                   sqlite3_value **argv);
    7940             : 
    7941       64974 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
    7942             :                                   sqlite3_value **argv)
    7943             : {
    7944             :     auto psFillArrowArray = static_cast<OGRGPKGTableLayerFillArrowArray *>(
    7945       64974 :         sqlite3_user_data(pContext));
    7946             : 
    7947             :     // To be called under psFillArrowArray->oMutex
    7948             :     const auto SubmitBatchToCaller =
    7949         762 :         [psFillArrowArray](std::unique_lock<std::mutex> &oLock)
    7950             :     {
    7951         127 :         psFillArrowArray->psHelper->Shrink(psFillArrowArray->nCountRows);
    7952         127 :         psFillArrowArray->bThreadReady = true;
    7953         127 :         psFillArrowArray->oCV.notify_one();
    7954         254 :         while (psFillArrowArray->nCountRows > 0)
    7955             :         {
    7956         127 :             psFillArrowArray->oCV.wait(oLock);
    7957             :         }
    7958       65101 :     };
    7959             : 
    7960             :     const auto DealWithTooBigArray =
    7961         549 :         [&SubmitBatchToCaller, psFillArrowArray](int iFeat)
    7962             :     {
    7963         134 :         CPLDebug("GPKG",
    7964             :                  "OGR_GPKG_FillArrowArray_Step(): premature "
    7965             :                  "notification of %d features to consumer due "
    7966             :                  "to too big array",
    7967             :                  iFeat);
    7968         134 :         if (psFillArrowArray->bAsynchronousMode)
    7969             :         {
    7970         242 :             std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7971         121 :             psFillArrowArray->bMemoryLimitReached = true;
    7972         121 :             SubmitBatchToCaller(oLock);
    7973             :         }
    7974             :         else
    7975             :         {
    7976             :             {
    7977          13 :                 std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7978          13 :                 psFillArrowArray->bMemoryLimitReached = true;
    7979             :             }
    7980          13 :             sqlite3_interrupt(psFillArrowArray->hDB);
    7981             :         }
    7982         134 :     };
    7983             : 
    7984             :     {
    7985       64974 :         std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    7986      129948 :         if (psFillArrowArray->nCountRows >=
    7987       64974 :             psFillArrowArray->psHelper->m_nMaxBatchSize)
    7988             :         {
    7989           6 :             if (psFillArrowArray->bAsynchronousMode)
    7990             :             {
    7991           6 :                 SubmitBatchToCaller(oLock);
    7992             :                 // Note that psFillArrowArray->psHelper.get() will generally now be
    7993             :                 // different from before the wait()
    7994             :             }
    7995             :             else
    7996             :             {
    7997             :                 // should not happen !
    7998             :                 psFillArrowArray->osErrorMsg = "OGR_GPKG_FillArrowArray_Step() "
    7999           0 :                                                "got more rows than expected!";
    8000           0 :                 sqlite3_interrupt(psFillArrowArray->hDB);
    8001           0 :                 psFillArrowArray->bErrorOccurred = true;
    8002           0 :                 return;
    8003             :             }
    8004             :         }
    8005       64974 :         if (psFillArrowArray->nCountRows < 0)
    8006           2 :             return;
    8007             :     }
    8008             : 
    8009       64972 :     if (psFillArrowArray->nMemLimit == 0)
    8010         148 :         psFillArrowArray->nMemLimit = OGRArrowArrayHelper::GetMemLimit();
    8011       64972 :     const auto nMemLimit = psFillArrowArray->nMemLimit;
    8012             :     const int SQLITE_MAX_FUNCTION_ARG =
    8013       64972 :         sqlite3_limit(psFillArrowArray->hDB, SQLITE_LIMIT_FUNCTION_ARG, -1);
    8014       64972 :     const bool bDateTimeAsString = psFillArrowArray->bDateTimeAsString;
    8015       65093 : begin:
    8016             :     int iFeat;
    8017             :     OGRArrowArrayHelper *psHelper;
    8018             :     {
    8019      130186 :         std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    8020       65093 :         iFeat = psFillArrowArray->nCountRows;
    8021       65093 :         psHelper = psFillArrowArray->psHelper.get();
    8022             :     }
    8023       65093 :     int iCol = 0;
    8024       65093 :     const int iFieldStart = sqlite3_value_int(argv[iCol]);
    8025       65093 :     ++iCol;
    8026       65093 :     int iField = std::max(0, iFieldStart);
    8027             : 
    8028             :     GIntBig nFID;
    8029       65093 :     if (iFieldStart < 0)
    8030             :     {
    8031       65087 :         nFID = sqlite3_value_int64(argv[iCol]);
    8032       65087 :         iCol++;
    8033       65087 :         if (psHelper->m_panFIDValues)
    8034             :         {
    8035       65068 :             psHelper->m_panFIDValues[iFeat] = nFID;
    8036             :         }
    8037       65087 :         psFillArrowArray->nCurFID = nFID;
    8038             :     }
    8039             :     else
    8040             :     {
    8041           6 :         nFID = psFillArrowArray->nCurFID;
    8042             :     }
    8043             : 
    8044      130175 :     if (iFieldStart < 0 && !psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
    8045       65082 :         psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    8046             :     {
    8047       65079 :         const int iArrowField = psHelper->m_mapOGRGeomFieldToArrowField[0];
    8048       65079 :         auto psArray = psHelper->m_out_array->children[iArrowField];
    8049       65079 :         size_t nWKBSize = 0;
    8050       65079 :         const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
    8051       65079 :         if (nSqlite3ColType == SQLITE_BLOB)
    8052             :         {
    8053             :             GPkgHeader oHeader;
    8054       64695 :             memset(&oHeader, 0, sizeof(oHeader));
    8055             : 
    8056       64695 :             const GByte *pabyWkb = nullptr;
    8057       64695 :             const int nBlobSize = sqlite3_value_bytes(argv[iCol]);
    8058             :             // coverity[tainted_data_return]
    8059             :             const GByte *pabyBlob =
    8060       64695 :                 static_cast<const GByte *>(sqlite3_value_blob(argv[iCol]));
    8061       64695 :             std::vector<GByte> abyWkb;
    8062       64695 :             if (nBlobSize >= 8 && pabyBlob && pabyBlob[0] == 'G' &&
    8063       64694 :                 pabyBlob[1] == 'P')
    8064             :             {
    8065       64694 :                 if (psFillArrowArray->poLayer->m_bUndoDiscardCoordLSBOnReading)
    8066             :                 {
    8067             :                     OGRGeometry *poGeomPtr =
    8068           1 :                         GPkgGeometryToOGR(pabyBlob, nBlobSize, nullptr);
    8069           1 :                     if (poGeomPtr)
    8070             :                     {
    8071           1 :                         poGeomPtr->roundCoordinates(
    8072           1 :                             psFillArrowArray->poFeatureDefn->GetGeomFieldDefn(0)
    8073             :                                 ->GetCoordinatePrecision());
    8074           1 :                         nWKBSize = poGeomPtr->WkbSize();
    8075           1 :                         abyWkb.resize(nWKBSize);
    8076           1 :                         if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
    8077           1 :                                                    wkbVariantIso) !=
    8078             :                             OGRERR_NONE)
    8079             :                         {
    8080           0 :                             nWKBSize = 0;
    8081             :                         }
    8082             :                         else
    8083             :                         {
    8084           1 :                             pabyWkb = abyWkb.data();
    8085             :                         }
    8086           1 :                         delete poGeomPtr;
    8087             :                     }
    8088             :                 }
    8089             :                 else
    8090             :                 {
    8091             :                     /* Read header */
    8092             :                     OGRErr err =
    8093       64693 :                         GPkgHeaderFromWKB(pabyBlob, nBlobSize, &oHeader);
    8094       64693 :                     if (err == OGRERR_NONE)
    8095             :                     {
    8096             :                         /* WKB pointer */
    8097       64693 :                         pabyWkb = pabyBlob + oHeader.nHeaderLen;
    8098       64693 :                         nWKBSize = nBlobSize - oHeader.nHeaderLen;
    8099             :                     }
    8100       64694 :                 }
    8101             :             }
    8102           1 :             else if (nBlobSize > 0 && pabyBlob)
    8103             :             {
    8104             :                 // Try also spatialite geometry blobs, although that is
    8105             :                 // not really expected...
    8106           1 :                 OGRGeometry *poGeomPtr = nullptr;
    8107           1 :                 if (OGRSQLiteImportSpatiaLiteGeometry(
    8108           1 :                         pabyBlob, nBlobSize, &poGeomPtr) != OGRERR_NONE)
    8109             :                 {
    8110           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    8111             :                              "Unable to read geometry");
    8112             :                 }
    8113             :                 else
    8114             :                 {
    8115           0 :                     nWKBSize = poGeomPtr->WkbSize();
    8116           0 :                     abyWkb.resize(nWKBSize);
    8117           0 :                     if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
    8118           0 :                                                wkbVariantIso) != OGRERR_NONE)
    8119             :                     {
    8120           0 :                         nWKBSize = 0;
    8121             :                     }
    8122             :                     else
    8123             :                     {
    8124           0 :                         pabyWkb = abyWkb.data();
    8125             :                     }
    8126             :                 }
    8127           1 :                 delete poGeomPtr;
    8128             :             }
    8129             : 
    8130       64695 :             if (nWKBSize != 0)
    8131             :             {
    8132             :                 // Deal with spatial filter
    8133       64694 :                 if (psFillArrowArray->poLayerForFilterGeom)
    8134             :                 {
    8135          33 :                     OGREnvelope sEnvelope;
    8136          33 :                     bool bEnvelopeAlreadySet = false;
    8137          33 :                     if (oHeader.bEmpty)
    8138             :                     {
    8139           0 :                         bEnvelopeAlreadySet = true;
    8140             :                     }
    8141          33 :                     else if (oHeader.bExtentHasXY)
    8142             :                     {
    8143          31 :                         bEnvelopeAlreadySet = true;
    8144          31 :                         sEnvelope.MinX = oHeader.MinX;
    8145          31 :                         sEnvelope.MinY = oHeader.MinY;
    8146          31 :                         sEnvelope.MaxX = oHeader.MaxX;
    8147          31 :                         sEnvelope.MaxY = oHeader.MaxY;
    8148             :                     }
    8149             : 
    8150          66 :                     if (!psFillArrowArray->poLayerForFilterGeom
    8151          33 :                              ->FilterWKBGeometry(pabyWkb, nWKBSize,
    8152             :                                                  bEnvelopeAlreadySet,
    8153             :                                                  sEnvelope))
    8154             :                     {
    8155           2 :                         return;
    8156             :                     }
    8157             :                 }
    8158             : 
    8159       64692 :                 if (iFeat > 0)
    8160             :                 {
    8161       64526 :                     auto panOffsets = static_cast<int32_t *>(
    8162       64526 :                         const_cast<void *>(psArray->buffers[1]));
    8163       64526 :                     const uint32_t nCurLength =
    8164       64526 :                         static_cast<uint32_t>(panOffsets[iFeat]);
    8165       64526 :                     if (nWKBSize <= nMemLimit &&
    8166       64526 :                         nWKBSize > nMemLimit - nCurLength)
    8167             :                     {
    8168          52 :                         DealWithTooBigArray(iFeat);
    8169          52 :                         if (psFillArrowArray->bAsynchronousMode)
    8170             :                         {
    8171          47 :                             goto begin;
    8172             :                         }
    8173             :                         else
    8174             :                         {
    8175           5 :                             return;
    8176             :                         }
    8177             :                     }
    8178             :                 }
    8179             : 
    8180       64640 :                 GByte *outPtr = psHelper->GetPtrForStringOrBinary(
    8181             :                     iArrowField, iFeat, nWKBSize);
    8182       64640 :                 if (outPtr == nullptr)
    8183             :                 {
    8184           0 :                     goto error;
    8185             :                 }
    8186       64640 :                 memcpy(outPtr, pabyWkb, nWKBSize);
    8187             :             }
    8188             :             else
    8189             :             {
    8190           1 :                 psHelper->SetEmptyStringOrBinary(psArray, iFeat);
    8191             :             }
    8192             :         }
    8193             : 
    8194       65025 :         if (nWKBSize == 0)
    8195             :         {
    8196         385 :             if (!psHelper->SetNull(iArrowField, iFeat))
    8197             :             {
    8198           0 :                 goto error;
    8199             :             }
    8200             :         }
    8201       65025 :         iCol++;
    8202             :     }
    8203             : 
    8204     1268260 :     for (; iField < psHelper->m_nFieldCount; iField++)
    8205             :     {
    8206     1203310 :         const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
    8207     1203310 :         if (iArrowField < 0)
    8208          73 :             continue;
    8209     1203240 :         if (iCol == SQLITE_MAX_FUNCTION_ARG)
    8210           6 :             break;
    8211             : 
    8212             :         const OGRFieldDefn *poFieldDefn =
    8213     1203240 :             psFillArrowArray->poFeatureDefn->GetFieldDefnUnsafe(iField);
    8214             : 
    8215     1203240 :         auto psArray = psHelper->m_out_array->children[iArrowField];
    8216             : 
    8217     1203240 :         const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
    8218     1203240 :         if (nSqlite3ColType == SQLITE_NULL)
    8219             :         {
    8220         784 :             if (!psHelper->SetNull(iArrowField, iFeat))
    8221             :             {
    8222           0 :                 goto error;
    8223             :             }
    8224         784 :             iCol++;
    8225         784 :             continue;
    8226             :         }
    8227             : 
    8228     1202450 :         switch (poFieldDefn->GetType())
    8229             :         {
    8230        1245 :             case OFTInteger:
    8231             :             {
    8232        1245 :                 const int nVal = sqlite3_value_int(argv[iCol]);
    8233        1245 :                 if (poFieldDefn->GetSubType() == OFSTBoolean)
    8234             :                 {
    8235         122 :                     if (nVal != 0)
    8236             :                     {
    8237          91 :                         psHelper->SetBoolOn(psArray, iFeat);
    8238             :                     }
    8239             :                 }
    8240        1123 :                 else if (poFieldDefn->GetSubType() == OFSTInt16)
    8241             :                 {
    8242          59 :                     psHelper->SetInt16(psArray, iFeat,
    8243             :                                        static_cast<int16_t>(nVal));
    8244             :                 }
    8245             :                 else
    8246             :                 {
    8247        1064 :                     psHelper->SetInt32(psArray, iFeat, nVal);
    8248             :                 }
    8249        1245 :                 break;
    8250             :             }
    8251             : 
    8252         220 :             case OFTInteger64:
    8253             :             {
    8254         220 :                 psHelper->SetInt64(psArray, iFeat,
    8255         220 :                                    sqlite3_value_int64(argv[iCol]));
    8256         220 :                 break;
    8257             :             }
    8258             : 
    8259         301 :             case OFTReal:
    8260             :             {
    8261         301 :                 const double dfVal = sqlite3_value_double(argv[iCol]);
    8262         301 :                 if (poFieldDefn->GetSubType() == OFSTFloat32)
    8263             :                 {
    8264          51 :                     psHelper->SetFloat(psArray, iFeat,
    8265             :                                        static_cast<float>(dfVal));
    8266             :                 }
    8267             :                 else
    8268             :                 {
    8269         250 :                     psHelper->SetDouble(psArray, iFeat, dfVal);
    8270             :                 }
    8271         301 :                 break;
    8272             :             }
    8273             : 
    8274         176 :             case OFTBinary:
    8275             :             {
    8276             :                 const uint32_t nBytes =
    8277         176 :                     static_cast<uint32_t>(sqlite3_value_bytes(argv[iCol]));
    8278             :                 // coverity[tainted_data_return]
    8279         176 :                 const void *pabyData = sqlite3_value_blob(argv[iCol]);
    8280         176 :                 if (pabyData != nullptr || nBytes == 0)
    8281             :                 {
    8282         176 :                     if (iFeat > 0)
    8283             :                     {
    8284         119 :                         auto panOffsets = static_cast<int32_t *>(
    8285         119 :                             const_cast<void *>(psArray->buffers[1]));
    8286         119 :                         const uint32_t nCurLength =
    8287         119 :                             static_cast<uint32_t>(panOffsets[iFeat]);
    8288         119 :                         if (nBytes <= nMemLimit &&
    8289         119 :                             nBytes > nMemLimit - nCurLength)
    8290             :                         {
    8291          41 :                             DealWithTooBigArray(iFeat);
    8292          41 :                             if (psFillArrowArray->bAsynchronousMode)
    8293             :                             {
    8294          37 :                                 goto begin;
    8295             :                             }
    8296             :                             else
    8297             :                             {
    8298           4 :                                 return;
    8299             :                             }
    8300             :                         }
    8301             :                     }
    8302             : 
    8303         135 :                     GByte *outPtr = psHelper->GetPtrForStringOrBinary(
    8304             :                         iArrowField, iFeat, nBytes);
    8305         135 :                     if (outPtr == nullptr)
    8306             :                     {
    8307           0 :                         goto error;
    8308             :                     }
    8309         135 :                     if (nBytes)
    8310         135 :                         memcpy(outPtr, pabyData, nBytes);
    8311             :                 }
    8312             :                 else
    8313             :                 {
    8314           0 :                     psHelper->SetEmptyStringOrBinary(psArray, iFeat);
    8315             :                 }
    8316         135 :                 break;
    8317             :             }
    8318             : 
    8319          51 :             case OFTDate:
    8320             :             {
    8321             :                 OGRField ogrField;
    8322             :                 const auto pszTxt = reinterpret_cast<const char *>(
    8323          51 :                     sqlite3_value_text(argv[iCol]));
    8324         102 :                 if (pszTxt != nullptr &&
    8325          51 :                     psFillArrowArray->poLayer->ParseDateField(
    8326             :                         pszTxt, &ogrField, poFieldDefn, nFID))
    8327             :                 {
    8328          51 :                     psHelper->SetDate(psArray, iFeat,
    8329          51 :                                       psFillArrowArray->brokenDown, ogrField);
    8330             :                 }
    8331          51 :                 break;
    8332             :             }
    8333             : 
    8334          65 :             case OFTDateTime:
    8335             :             {
    8336          65 :                 if (!bDateTimeAsString)
    8337             :                 {
    8338             :                     OGRField ogrField;
    8339             :                     const auto pszTxt = reinterpret_cast<const char *>(
    8340          55 :                         sqlite3_value_text(argv[iCol]));
    8341         110 :                     if (pszTxt != nullptr &&
    8342          55 :                         psFillArrowArray->poLayer->ParseDateTimeField(
    8343             :                             pszTxt, &ogrField, poFieldDefn, nFID))
    8344             :                     {
    8345          55 :                         psHelper->SetDateTime(
    8346          55 :                             psArray, iFeat, psFillArrowArray->brokenDown,
    8347          55 :                             psHelper->m_anTZFlags[iField], ogrField);
    8348             :                     }
    8349          55 :                     break;
    8350             :                 }
    8351             :                 else
    8352             :                 {
    8353             :                     [[fallthrough]];
    8354             :                 }
    8355             :             }
    8356             : 
    8357             :             case OFTString:
    8358             :             {
    8359             :                 const auto pszTxt = reinterpret_cast<const char *>(
    8360     1200400 :                     sqlite3_value_text(argv[iCol]));
    8361     1200400 :                 if (pszTxt != nullptr)
    8362             :                 {
    8363     1200400 :                     const size_t nBytes = strlen(pszTxt);
    8364     1200400 :                     if (iFeat > 0)
    8365             :                     {
    8366     1200280 :                         auto panOffsets = static_cast<int32_t *>(
    8367     1200280 :                             const_cast<void *>(psArray->buffers[1]));
    8368     1200280 :                         const uint32_t nCurLength =
    8369     1200280 :                             static_cast<uint32_t>(panOffsets[iFeat]);
    8370     1200280 :                         if (nBytes <= nMemLimit &&
    8371     1200280 :                             nBytes > nMemLimit - nCurLength)
    8372             :                         {
    8373          41 :                             DealWithTooBigArray(iFeat);
    8374          41 :                             if (psFillArrowArray->bAsynchronousMode)
    8375             :                             {
    8376          37 :                                 goto begin;
    8377             :                             }
    8378             :                             else
    8379             :                             {
    8380           4 :                                 return;
    8381             :                             }
    8382             :                         }
    8383             :                     }
    8384             : 
    8385     1200360 :                     GByte *outPtr = psHelper->GetPtrForStringOrBinary(
    8386             :                         iArrowField, iFeat, nBytes);
    8387     1200360 :                     if (outPtr == nullptr)
    8388             :                     {
    8389           0 :                         goto error;
    8390             :                     }
    8391     1200360 :                     if (nBytes)
    8392     1200360 :                         memcpy(outPtr, pszTxt, nBytes);
    8393             :                 }
    8394             :                 else
    8395             :                 {
    8396           0 :                     psHelper->SetEmptyStringOrBinary(psArray, iFeat);
    8397             :                 }
    8398     1200360 :                 break;
    8399             :             }
    8400             : 
    8401           0 :             default:
    8402           0 :                 break;
    8403             :         }
    8404             : 
    8405     1202370 :         iCol++;
    8406             :     }
    8407             : 
    8408       64957 :     if (iField == psHelper->m_nFieldCount)
    8409             :     {
    8410       64951 :         std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
    8411       64951 :         psFillArrowArray->nCountRows++;
    8412             :     }
    8413       64957 :     return;
    8414             : 
    8415           0 : error:
    8416           0 :     sqlite3_interrupt(psFillArrowArray->hDB);
    8417             : }
    8418             : 
    8419             : /************************************************************************/
    8420             : /*                  OGR_GPKG_FillArrowArray_Finalize()                  */
    8421             : /************************************************************************/
    8422             : 
    8423         157 : static void OGR_GPKG_FillArrowArray_Finalize(sqlite3_context * /*pContext*/)
    8424             : {
    8425         157 : }
    8426             : 
    8427             : /************************************************************************/
    8428             : /*                   GetNextArrowArrayAsynchronous()                    */
    8429             : /************************************************************************/
    8430             : 
    8431         202 : int OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronous(
    8432             :     struct ArrowArrayStream *stream, struct ArrowArray *out_array)
    8433             : {
    8434         202 :     memset(out_array, 0, sizeof(*out_array));
    8435             : 
    8436         202 :     m_bGetNextArrowArrayCalledSinceResetReading = true;
    8437             : 
    8438         202 :     if (m_poFillArrowArray)
    8439             :     {
    8440         150 :         std::lock_guard oLock(m_poFillArrowArray->oMutex);
    8441         150 :         if (m_poFillArrowArray->bIsFinished)
    8442             :         {
    8443          25 :             return 0;
    8444             :         }
    8445             :     }
    8446             : 
    8447             :     auto psHelper = std::make_unique<OGRArrowArrayHelper>(
    8448         354 :         m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
    8449         177 :     if (out_array->release == nullptr)
    8450             :     {
    8451           0 :         return ENOMEM;
    8452             :     }
    8453             : 
    8454         177 :     if (m_poFillArrowArray == nullptr)
    8455             :     {
    8456             :         // Check that the total number of arguments passed to
    8457             :         // OGR_GPKG_FillArrowArray_INTERNAL() doesn't exceed SQLITE_MAX_FUNCTION_ARG
    8458             :         // If it does, we cannot reliably use GetNextArrowArrayAsynchronous() in
    8459             :         // the situation where the ArrowArray would exceed the nMemLimit.
    8460             :         // So be on the safe side, and rely on the base OGRGeoPackageLayer
    8461             :         // implementation
    8462             :         const int SQLITE_MAX_FUNCTION_ARG =
    8463          52 :             sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
    8464          52 :         int nCountArgs = 1     // field index
    8465             :                          + 1;  // FID column
    8466         101 :         if (!psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
    8467          49 :             psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    8468             :         {
    8469          49 :             ++nCountArgs;
    8470             :         }
    8471         472 :         for (int iField = 0; iField < psHelper->m_nFieldCount; iField++)
    8472             :         {
    8473         422 :             const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
    8474         422 :             if (iArrowField >= 0)
    8475             :             {
    8476         422 :                 if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
    8477             :                 {
    8478           2 :                     psHelper.reset();
    8479           2 :                     if (out_array->release)
    8480           2 :                         out_array->release(out_array);
    8481           2 :                     return OGRGeoPackageLayer::GetNextArrowArray(stream,
    8482           2 :                                                                  out_array);
    8483             :                 }
    8484         420 :                 ++nCountArgs;
    8485             :             }
    8486             :         }
    8487             : 
    8488             :         m_poFillArrowArray =
    8489          50 :             std::make_unique<OGRGPKGTableLayerFillArrowArray>();
    8490          50 :         m_poFillArrowArray->psHelper = std::move(psHelper);
    8491         100 :         m_poFillArrowArray->bDateTimeAsString =
    8492          50 :             m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
    8493             :                                                    false);
    8494          50 :         m_poFillArrowArray->poFeatureDefn = m_poFeatureDefn;
    8495          50 :         m_poFillArrowArray->poLayer = this;
    8496          50 :         m_poFillArrowArray->hDB = m_poDS->GetDB();
    8497          50 :         memset(&m_poFillArrowArray->brokenDown, 0,
    8498             :                sizeof(m_poFillArrowArray->brokenDown));
    8499          50 :         m_poFillArrowArray->bAsynchronousMode = true;
    8500          50 :         if (m_poFilterGeom)
    8501          10 :             m_poFillArrowArray->poLayerForFilterGeom = this;
    8502             : 
    8503             :         try
    8504             :         {
    8505         100 :             m_oThreadNextArrowArray = std::thread(
    8506         100 :                 [this]() { GetNextArrowArrayAsynchronousWorker(); });
    8507             :         }
    8508           0 :         catch (const std::exception &e)
    8509             :         {
    8510           0 :             m_poFillArrowArray.reset();
    8511           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    8512           0 :                      "Cannot start worker thread: %s", e.what());
    8513           0 :             out_array->release(out_array);
    8514           0 :             return ENOMEM;
    8515             :         }
    8516             :     }
    8517             :     else
    8518             :     {
    8519         125 :         std::lock_guard oLock(m_poFillArrowArray->oMutex);
    8520         125 :         if (m_poFillArrowArray->bErrorOccurred)
    8521             :         {
    8522           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    8523           0 :                      m_poFillArrowArray->osErrorMsg.c_str());
    8524           0 :             out_array->release(out_array);
    8525           0 :             return EIO;
    8526             :         }
    8527             : 
    8528             :         // Resume worker thread
    8529         125 :         m_poFillArrowArray->psHelper = std::move(psHelper);
    8530         125 :         m_poFillArrowArray->nCountRows = 0;
    8531         125 :         m_poFillArrowArray->bThreadReady = false;
    8532         125 :         m_poFillArrowArray->bMemoryLimitReached = false;
    8533         125 :         m_poFillArrowArray->oCV.notify_one();
    8534             :     }
    8535             : 
    8536             :     // Wait for GetNextArrowArrayAsynchronousWorker() /
    8537             :     // OGR_GPKG_FillArrowArray_Step() to have generated a result set (or an
    8538             :     // error)
    8539             :     bool bIsFinished;
    8540         350 :     std::string osErrorMsg;
    8541             :     {
    8542         350 :         std::unique_lock<std::mutex> oLock(m_poFillArrowArray->oMutex);
    8543         343 :         while (!m_poFillArrowArray->bThreadReady)
    8544             :         {
    8545         168 :             m_poFillArrowArray->oCV.wait(oLock);
    8546             :         }
    8547         175 :         CPLAssert(m_poFillArrowArray->bErrorOccurred ||
    8548             :                   m_poFillArrowArray->bMemoryLimitReached ||
    8549             :                   m_poFillArrowArray->bIsFinished ||
    8550             :                   m_poFillArrowArray->nCountRows ==
    8551             :                       m_poFillArrowArray->psHelper->m_nMaxBatchSize);
    8552         175 :         bIsFinished = m_poFillArrowArray->bIsFinished;
    8553         175 :         osErrorMsg = m_poFillArrowArray->osErrorMsg;
    8554             :     }
    8555             : 
    8556         175 :     if (m_poFillArrowArray->bErrorOccurred)
    8557             :     {
    8558           2 :         m_oThreadNextArrowArray.join();
    8559           2 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
    8560           2 :         m_poFillArrowArray->psHelper->ClearArray();
    8561           2 :         return EIO;
    8562             :     }
    8563         173 :     else if (bIsFinished)
    8564             :     {
    8565          46 :         m_oThreadNextArrowArray.join();
    8566             :     }
    8567             : 
    8568         173 :     return 0;
    8569             : }
    8570             : 
    8571             : /************************************************************************/
    8572             : /*                GetNextArrowArrayAsynchronousWorker()                 */
    8573             : /************************************************************************/
    8574             : 
    8575          50 : void OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronousWorker()
    8576             : {
    8577          50 :     sqlite3_create_function(
    8578          50 :         m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
    8579          50 :         SQLITE_UTF8 | SQLITE_DETERMINISTIC, m_poFillArrowArray.get(), nullptr,
    8580             :         OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
    8581             : 
    8582         100 :     std::string osSQL;
    8583          50 :     osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
    8584             : 
    8585        1752 :     const auto AddFields = [this, &osSQL]()
    8586             :     {
    8587          54 :         if (m_pszFidColumn)
    8588             :         {
    8589          51 :             osSQL += "m.\"";
    8590          51 :             osSQL += SQLEscapeName(m_pszFidColumn);
    8591          51 :             osSQL += '"';
    8592             :         }
    8593             :         else
    8594             :         {
    8595           3 :             osSQL += "NULL";
    8596             :         }
    8597             : 
    8598          54 :         if (!m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField
    8599         105 :                  .empty() &&
    8600          51 :             m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    8601             :         {
    8602          51 :             osSQL += ",m.\"";
    8603          51 :             osSQL += SQLEscapeName(GetGeometryColumn());
    8604          51 :             osSQL += '"';
    8605             :         }
    8606         242 :         for (int iField = 0;
    8607         242 :              iField < m_poFillArrowArray->psHelper->m_nFieldCount; iField++)
    8608             :         {
    8609             :             const int iArrowField =
    8610         188 :                 m_poFillArrowArray->psHelper->m_mapOGRFieldToArrowField[iField];
    8611         188 :             if (iArrowField >= 0)
    8612             :             {
    8613             :                 const OGRFieldDefn *poFieldDefn =
    8614         188 :                     m_poFeatureDefn->GetFieldDefnUnsafe(iField);
    8615         188 :                 osSQL += ",m.\"";
    8616         188 :                 osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
    8617         188 :                 osSQL += '"';
    8618             :             }
    8619             :         }
    8620          54 :     };
    8621             : 
    8622          50 :     AddFields();
    8623             : 
    8624          50 :     osSQL += ") FROM ";
    8625          50 :     if (m_iNextShapeId > 0)
    8626             :     {
    8627           4 :         osSQL += "(SELECT ";
    8628           4 :         AddFields();
    8629           4 :         osSQL += " FROM ";
    8630             :     }
    8631          50 :     osSQL += '\"';
    8632          50 :     osSQL += SQLEscapeName(m_pszTableName);
    8633          50 :     osSQL += "\" m";
    8634          50 :     if (!m_soFilter.empty())
    8635             :     {
    8636          35 :         if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
    8637          10 :             HasSpatialIndex())
    8638             :         {
    8639          10 :             OGREnvelope sEnvelope;
    8640             : 
    8641          10 :             m_poFilterGeom->getEnvelope(&sEnvelope);
    8642             : 
    8643          10 :             bool bUseSpatialIndex = true;
    8644          20 :             if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
    8645           1 :                 sEnvelope.MinY <= m_poExtent->MinY &&
    8646          21 :                 sEnvelope.MaxX >= m_poExtent->MaxX &&
    8647           1 :                 sEnvelope.MaxY >= m_poExtent->MaxY)
    8648             :             {
    8649             :                 // Selecting from spatial filter on whole extent can be rather
    8650             :                 // slow. So use function based filtering, just in case the
    8651             :                 // advertized global extent might be wrong. Otherwise we might
    8652             :                 // just discard completely the spatial filter.
    8653           1 :                 bUseSpatialIndex = false;
    8654             :             }
    8655             : 
    8656           9 :             if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
    8657          28 :                 !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
    8658           9 :                 !std::isinf(sEnvelope.MaxY))
    8659             :             {
    8660             :                 osSQL +=
    8661             :                     CPLSPrintf(" JOIN \"%s\" r "
    8662             :                                "ON m.\"%s\" = r.id WHERE "
    8663             :                                "r.maxx >= %.12f AND r.minx <= %.12f AND "
    8664             :                                "r.maxy >= %.12f AND r.miny <= %.12f",
    8665          18 :                                SQLEscapeName(m_osRTreeName).c_str(),
    8666          18 :                                SQLEscapeName(m_osFIDForRTree).c_str(),
    8667           9 :                                sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
    8668          27 :                                sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
    8669             :             }
    8670             :         }
    8671             :         else
    8672             :         {
    8673          15 :             osSQL += " WHERE ";
    8674          15 :             osSQL += m_soFilter;
    8675             :         }
    8676             :     }
    8677             : 
    8678          50 :     if (m_iNextShapeId > 0)
    8679             :         osSQL +=
    8680           4 :             CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB ") m", m_iNextShapeId);
    8681             : 
    8682             :     // CPLDebug("GPKG", "%s", osSQL.c_str());
    8683             : 
    8684          50 :     char *pszErrMsg = nullptr;
    8685         100 :     std::string osErrorMsg;
    8686          50 :     if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
    8687          50 :                      &pszErrMsg) != SQLITE_OK)
    8688             :     {
    8689           2 :         osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
    8690             :     }
    8691          50 :     sqlite3_free(pszErrMsg);
    8692             : 
    8693             :     // Delete function
    8694          50 :     sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
    8695             :                             -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
    8696             :                             nullptr, nullptr, nullptr);
    8697             : 
    8698         100 :     std::lock_guard oLock(m_poFillArrowArray->oMutex);
    8699          50 :     m_poFillArrowArray->bIsFinished = true;
    8700          50 :     m_poFillArrowArray->bErrorOccurred = !osErrorMsg.empty();
    8701          50 :     m_poFillArrowArray->osErrorMsg = std::move(osErrorMsg);
    8702             : 
    8703          50 :     if (m_poFillArrowArray->nCountRows >= 0)
    8704             :     {
    8705          48 :         m_poFillArrowArray->psHelper->Shrink(m_poFillArrowArray->nCountRows);
    8706          48 :         if (m_poFillArrowArray->nCountRows == 0)
    8707             :         {
    8708          22 :             m_poFillArrowArray->psHelper->ClearArray();
    8709             :         }
    8710             :     }
    8711          50 :     m_poFillArrowArray->bThreadReady = true;
    8712          50 :     m_poFillArrowArray->oCV.notify_one();
    8713          50 : }
    8714             : 
    8715             : /************************************************************************/
    8716             : /*                         GetNextArrowArray()                          */
    8717             : /************************************************************************/
    8718             : 
    8719         370 : int OGRGeoPackageTableLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
    8720             :                                                struct ArrowArray *out_array)
    8721             : {
    8722         370 :     if (!m_bFeatureDefnCompleted)
    8723           1 :         GetLayerDefn();
    8724         370 :     if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    8725             :     {
    8726           0 :         memset(out_array, 0, sizeof(*out_array));
    8727           0 :         return EIO;
    8728             :     }
    8729             : 
    8730         370 :     if (m_poFilterGeom != nullptr)
    8731             :     {
    8732             :         // Both are exclusive
    8733          18 :         CreateSpatialIndexIfNecessary();
    8734          18 :         if (!RunDeferredSpatialIndexUpdate())
    8735             :         {
    8736           0 :             memset(out_array, 0, sizeof(*out_array));
    8737           0 :             return EIO;
    8738             :         }
    8739             :     }
    8740             : 
    8741         370 :     if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
    8742             :     {
    8743           6 :         return OGRGeoPackageLayer::GetNextArrowArray(stream, out_array);
    8744             :     }
    8745             : 
    8746         959 :     if (m_nIsCompatOfOptimizedGetNextArrowArray == FALSE ||
    8747         413 :         m_pszFidColumn == nullptr || !m_soFilter.empty() ||
    8748         777 :         m_poFillArrowArray ||
    8749         181 :         (!m_bGetNextArrowArrayCalledSinceResetReading && m_iNextShapeId > 0))
    8750             :     {
    8751         184 :         return GetNextArrowArrayAsynchronous(stream, out_array);
    8752             :     }
    8753             : 
    8754             :     // We can use this optimized version only if there is no hole in FID
    8755             :     // numbering. That is min(fid) == 1 and max(fid) == m_nTotalFeatureCount
    8756         180 :     if (m_nIsCompatOfOptimizedGetNextArrowArray < 0)
    8757             :     {
    8758          72 :         m_nIsCompatOfOptimizedGetNextArrowArray = FALSE;
    8759          72 :         const auto nTotalFeatureCount = GetTotalFeatureCount();
    8760          72 :         if (nTotalFeatureCount < 0)
    8761           3 :             return GetNextArrowArrayAsynchronous(stream, out_array);
    8762             :         {
    8763          69 :             char *pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
    8764             :                                            m_pszFidColumn, m_pszTableName);
    8765             :             OGRErr err;
    8766          69 :             const auto nMaxFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
    8767          69 :             sqlite3_free(pszSQL);
    8768          69 :             if (nMaxFID != nTotalFeatureCount)
    8769           3 :                 return GetNextArrowArrayAsynchronous(stream, out_array);
    8770             :         }
    8771             :         {
    8772          66 :             char *pszSQL = sqlite3_mprintf("SELECT MIN(\"%w\") FROM \"%w\"",
    8773             :                                            m_pszFidColumn, m_pszTableName);
    8774             :             OGRErr err;
    8775          66 :             const auto nMinFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
    8776          66 :             sqlite3_free(pszSQL);
    8777          66 :             if (nMinFID != 1)
    8778          12 :                 return GetNextArrowArrayAsynchronous(stream, out_array);
    8779             :         }
    8780          54 :         m_nIsCompatOfOptimizedGetNextArrowArray = TRUE;
    8781             :     }
    8782             : 
    8783         162 :     m_bGetNextArrowArrayCalledSinceResetReading = true;
    8784             : 
    8785             :     // CPLDebug("GPKG", "m_iNextShapeId = " CPL_FRMT_GIB, m_iNextShapeId);
    8786             : 
    8787         324 :     const int nMaxBatchSize = OGRArrowArrayHelper::GetMaxFeaturesInBatch(
    8788         162 :         m_aosArrowArrayStreamOptions);
    8789             : 
    8790             :     // Fetch the answer from a potentially queued asynchronous task
    8791         162 :     if (!m_oQueueArrowArrayPrefetchTasks.empty())
    8792             :     {
    8793          44 :         const size_t nTasks = m_oQueueArrowArrayPrefetchTasks.size();
    8794          44 :         auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
    8795          44 :         m_oQueueArrowArrayPrefetchTasks.pop();
    8796             : 
    8797             :         // Wait for thread to be ready
    8798             :         {
    8799          44 :             std::unique_lock<std::mutex> oLock(task->m_oMutex);
    8800          53 :             while (!task->m_bArrayReady)
    8801             :             {
    8802           9 :                 task->m_oCV.wait(oLock);
    8803             :             }
    8804          44 :             task->m_bArrayReady = false;
    8805             :         }
    8806          44 :         if (!task->m_osErrorMsg.empty())
    8807           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    8808           0 :                      task->m_osErrorMsg.c_str());
    8809             : 
    8810         145 :         const auto stopThread = [&task]()
    8811             :         {
    8812             :             {
    8813          58 :                 std::lock_guard oLock(task->m_oMutex);
    8814          29 :                 task->m_bStop = true;
    8815          29 :                 task->m_oCV.notify_one();
    8816             :             }
    8817          29 :             if (task->m_oThread.joinable())
    8818          29 :                 task->m_oThread.join();
    8819          29 :         };
    8820             : 
    8821          44 :         if (task->m_iStartShapeId != m_iNextShapeId)
    8822             :         {
    8823             :             // Should not normally happen, unless the user messes with
    8824             :             // GetNextFeature()
    8825           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    8826             :                      "Worker thread task has not expected m_iStartShapeId "
    8827             :                      "value. Got " CPL_FRMT_GIB ", expected " CPL_FRMT_GIB,
    8828           0 :                      task->m_iStartShapeId, m_iNextShapeId);
    8829           0 :             if (task->m_psArrowArray->release)
    8830           0 :                 task->m_psArrowArray->release(task->m_psArrowArray.get());
    8831             : 
    8832           0 :             stopThread();
    8833             :         }
    8834          44 :         else if (task->m_psArrowArray->release)
    8835             :         {
    8836          40 :             m_iNextShapeId += task->m_psArrowArray->length;
    8837             : 
    8838             :             // Transfer the task ArrowArray to the client array
    8839          40 :             memcpy(out_array, task->m_psArrowArray.get(),
    8840             :                    sizeof(struct ArrowArray));
    8841          40 :             memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
    8842             : 
    8843          80 :             const bool bMemoryLimitReached = [&task]()
    8844             :             {
    8845          40 :                 std::unique_lock oLock(task->m_oMutex);
    8846          80 :                 return task->m_bMemoryLimitReached;
    8847          40 :             }();
    8848             : 
    8849          40 :             if (bMemoryLimitReached)
    8850             :             {
    8851           2 :                 m_nIsCompatOfOptimizedGetNextArrowArray = false;
    8852           2 :                 stopThread();
    8853           2 :                 CancelAsyncNextArrowArray();
    8854           2 :                 return 0;
    8855             :             }
    8856             :             // Are the records still available for reading beyond the current
    8857             :             // queued tasks ? If so, recycle this task to read them
    8858          38 :             else if (task->m_iStartShapeId +
    8859          38 :                          static_cast<GIntBig>(nTasks) * nMaxBatchSize <=
    8860          38 :                      m_nTotalFeatureCount)
    8861             :             {
    8862          15 :                 task->m_iStartShapeId +=
    8863          15 :                     static_cast<GIntBig>(nTasks) * nMaxBatchSize;
    8864          15 :                 task->m_poLayer->m_iNextShapeId = task->m_iStartShapeId;
    8865             :                 try
    8866             :                 {
    8867             :                     // Wake-up thread with new task
    8868             :                     {
    8869          30 :                         std::lock_guard oLock(task->m_oMutex);
    8870          15 :                         task->m_bFetchRows = true;
    8871          15 :                         task->m_oCV.notify_one();
    8872             :                     }
    8873          15 :                     m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
    8874          15 :                     return 0;
    8875             :                 }
    8876           0 :                 catch (const std::exception &e)
    8877             :                 {
    8878           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    8879           0 :                              "Cannot start worker thread: %s", e.what());
    8880             :                 }
    8881             :             }
    8882             :             else
    8883             :             {
    8884          23 :                 stopThread();
    8885          23 :                 return 0;
    8886             :             }
    8887             :         }
    8888             : 
    8889           4 :         stopThread();
    8890             :     }
    8891             : 
    8892          32 :     const auto GetThreadsAvailable = []()
    8893             :     {
    8894             :         const char *pszMaxThreads =
    8895          32 :             CPLGetConfigOption("OGR_GPKG_NUM_THREADS", nullptr);
    8896          32 :         if (pszMaxThreads == nullptr)
    8897          32 :             return std::min(4, CPLGetNumCPUs());
    8898           0 :         else if (EQUAL(pszMaxThreads, "ALL_CPUS"))
    8899           0 :             return CPLGetNumCPUs();
    8900             :         else
    8901           0 :             return atoi(pszMaxThreads);
    8902             :     };
    8903             : 
    8904             :     // Start asynchronous tasks to prefetch the next ArrowArray
    8905         211 :     if (m_poDS->GetAccess() == GA_ReadOnly &&
    8906          89 :         m_oQueueArrowArrayPrefetchTasks.empty() &&
    8907          89 :         m_iNextShapeId + 2 * static_cast<GIntBig>(nMaxBatchSize) <=
    8908          89 :             m_nTotalFeatureCount &&
    8909         227 :         sqlite3_threadsafe() != 0 && GetThreadsAvailable() >= 2 &&
    8910          16 :         CPLGetUsablePhysicalRAM() > 1024 * 1024 * 1024)
    8911             :     {
    8912          16 :         const int nMaxTasks = static_cast<int>(std::min<GIntBig>(
    8913          16 :             DIV_ROUND_UP(m_nTotalFeatureCount - nMaxBatchSize - m_iNextShapeId,
    8914             :                          nMaxBatchSize),
    8915          32 :             GetThreadsAvailable()));
    8916          16 :         CPLDebug("GPKG", "Using %d threads", nMaxTasks);
    8917          32 :         GDALOpenInfo oOpenInfo(m_poDS->GetDescription(), GA_ReadOnly);
    8918          16 :         oOpenInfo.papszOpenOptions = m_poDS->GetOpenOptions();
    8919          16 :         oOpenInfo.nOpenFlags = GDAL_OF_VECTOR;
    8920          59 :         for (int iTask = 0; iTask < nMaxTasks; ++iTask)
    8921             :         {
    8922          43 :             auto task = std::make_unique<ArrowArrayPrefetchTask>();
    8923          86 :             task->m_iStartShapeId =
    8924          43 :                 m_iNextShapeId +
    8925          43 :                 static_cast<GIntBig>(iTask + 1) * nMaxBatchSize;
    8926          43 :             task->m_poDS = std::make_unique<GDALGeoPackageDataset>();
    8927          43 :             if (!task->m_poDS->Open(&oOpenInfo, m_poDS->m_osFilenameInZip))
    8928             :             {
    8929           0 :                 break;
    8930             :             }
    8931           0 :             auto poOtherLayer = dynamic_cast<OGRGeoPackageTableLayer *>(
    8932          43 :                 task->m_poDS->GetLayerByName(GetName()));
    8933          86 :             if (poOtherLayer == nullptr ||
    8934          43 :                 poOtherLayer->GetLayerDefn()->GetFieldCount() !=
    8935          43 :                     m_poFeatureDefn->GetFieldCount())
    8936             :             {
    8937           0 :                 break;
    8938             :             }
    8939             : 
    8940             :             // Install query logging callback
    8941          43 :             if (m_poDS->pfnQueryLoggerFunc)
    8942             :             {
    8943           0 :                 task->m_poDS->SetQueryLoggerFunc(m_poDS->pfnQueryLoggerFunc,
    8944           0 :                                                  m_poDS->poQueryLoggerArg);
    8945             :             }
    8946             : 
    8947          43 :             task->m_poLayer = poOtherLayer;
    8948          43 :             task->m_psArrowArray = std::make_unique<struct ArrowArray>();
    8949          43 :             memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
    8950             : 
    8951          43 :             poOtherLayer->m_nTotalFeatureCount = m_nTotalFeatureCount;
    8952             :             poOtherLayer->m_aosArrowArrayStreamOptions =
    8953          43 :                 m_aosArrowArrayStreamOptions;
    8954          43 :             OGRLayer *poOtherLayerAsLayer = poOtherLayer;
    8955          43 :             auto poOtherFDefn = poOtherLayerAsLayer->GetLayerDefn();
    8956          86 :             for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
    8957             :             {
    8958          86 :                 poOtherFDefn->GetGeomFieldDefn(i)->SetIgnored(
    8959          43 :                     m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored());
    8960             :             }
    8961         127 :             for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    8962             :             {
    8963         168 :                 poOtherFDefn->GetFieldDefn(i)->SetIgnored(
    8964          84 :                     m_poFeatureDefn->GetFieldDefn(i)->IsIgnored());
    8965             :             }
    8966             : 
    8967          43 :             poOtherLayer->m_iNextShapeId = task->m_iStartShapeId;
    8968             : 
    8969          43 :             auto taskPtr = task.get();
    8970         456 :             auto taskRunner = [taskPtr]()
    8971             :             {
    8972          86 :                 std::unique_lock oLock(taskPtr->m_oMutex);
    8973          15 :                 do
    8974             :                 {
    8975          58 :                     taskPtr->m_bFetchRows = false;
    8976          58 :                     taskPtr->m_poLayer->GetNextArrowArrayInternal(
    8977          58 :                         taskPtr->m_psArrowArray.get(), taskPtr->m_osErrorMsg,
    8978          58 :                         taskPtr->m_bMemoryLimitReached);
    8979          58 :                     taskPtr->m_bArrayReady = true;
    8980          58 :                     taskPtr->m_oCV.notify_one();
    8981          58 :                     if (taskPtr->m_bMemoryLimitReached)
    8982          12 :                         break;
    8983             :                     // cppcheck-suppress knownConditionTrueFalse
    8984             :                     // Coverity apparently is confused by the fact that we
    8985             :                     // use unique_lock here to guard access for m_bStop whereas
    8986             :                     // in other places we use a lock_guard, but there's nothing
    8987             :                     // wrong.
    8988             :                     // coverity[missing_lock:FALSE]
    8989          90 :                     while (!taskPtr->m_bStop && !taskPtr->m_bFetchRows)
    8990             :                     {
    8991          44 :                         taskPtr->m_oCV.wait(oLock);
    8992             :                     }
    8993          46 :                 } while (!taskPtr->m_bStop);
    8994          43 :             };
    8995             : 
    8996          43 :             task->m_bFetchRows = true;
    8997             :             try
    8998             :             {
    8999          43 :                 task->m_oThread = std::thread(taskRunner);
    9000             :             }
    9001           0 :             catch (const std::exception &e)
    9002             :             {
    9003           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9004           0 :                          "Cannot start worker thread: %s", e.what());
    9005           0 :                 break;
    9006             :             }
    9007          43 :             m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
    9008             :         }
    9009             :     }
    9010             : 
    9011         122 :     std::string osErrorMsg;
    9012         122 :     bool bMemoryLimitReached = false;
    9013             :     int ret =
    9014         122 :         GetNextArrowArrayInternal(out_array, osErrorMsg, bMemoryLimitReached);
    9015         122 :     if (!osErrorMsg.empty())
    9016           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
    9017         122 :     if (bMemoryLimitReached)
    9018             :     {
    9019           1 :         CancelAsyncNextArrowArray();
    9020           1 :         m_nIsCompatOfOptimizedGetNextArrowArray = false;
    9021             :     }
    9022         122 :     return ret;
    9023             : }
    9024             : 
    9025             : /************************************************************************/
    9026             : /*                     GetNextArrowArrayInternal()                      */
    9027             : /************************************************************************/
    9028             : 
    9029         180 : int OGRGeoPackageTableLayer::GetNextArrowArrayInternal(
    9030             :     struct ArrowArray *out_array, std::string &osErrorMsg,
    9031             :     bool &bMemoryLimitReached)
    9032             : {
    9033         180 :     bMemoryLimitReached = false;
    9034         180 :     memset(out_array, 0, sizeof(*out_array));
    9035             : 
    9036         180 :     if (m_iNextShapeId >= m_nTotalFeatureCount)
    9037             :     {
    9038          61 :         return 0;
    9039             :     }
    9040             : 
    9041             :     auto psHelper = std::make_unique<OGRArrowArrayHelper>(
    9042         238 :         m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
    9043         119 :     if (out_array->release == nullptr)
    9044             :     {
    9045           0 :         return ENOMEM;
    9046             :     }
    9047             : 
    9048         238 :     OGRGPKGTableLayerFillArrowArray sFillArrowArray;
    9049         119 :     sFillArrowArray.psHelper = std::move(psHelper);
    9050         119 :     sFillArrowArray.bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
    9051             :         GAS_OPT_DATETIME_AS_STRING, false);
    9052         119 :     sFillArrowArray.poFeatureDefn = m_poFeatureDefn;
    9053         119 :     sFillArrowArray.poLayer = this;
    9054         119 :     sFillArrowArray.hDB = m_poDS->GetDB();
    9055         119 :     memset(&sFillArrowArray.brokenDown, 0, sizeof(sFillArrowArray.brokenDown));
    9056             : 
    9057         119 :     sqlite3_create_function(
    9058         119 :         m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
    9059             :         SQLITE_UTF8 | SQLITE_DETERMINISTIC, &sFillArrowArray, nullptr,
    9060             :         OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
    9061             : 
    9062         238 :     std::string osSQL;
    9063         119 :     osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
    9064         119 :     int nCountArgs = 1;
    9065             : 
    9066         119 :     osSQL += '"';
    9067         119 :     osSQL += SQLEscapeName(m_pszFidColumn);
    9068         119 :     osSQL += '"';
    9069         119 :     ++nCountArgs;
    9070             : 
    9071         236 :     if (!sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
    9072         117 :         sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
    9073             :     {
    9074         116 :         osSQL += ',';
    9075         116 :         osSQL += '"';
    9076         116 :         osSQL += SQLEscapeName(GetGeometryColumn());
    9077         116 :         osSQL += '"';
    9078         116 :         ++nCountArgs;
    9079             :     }
    9080             :     const int SQLITE_MAX_FUNCTION_ARG =
    9081         119 :         sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
    9082         798 :     for (int iField = 0; iField < sFillArrowArray.psHelper->m_nFieldCount;
    9083             :          iField++)
    9084             :     {
    9085             :         const int iArrowField =
    9086         679 :             sFillArrowArray.psHelper->m_mapOGRFieldToArrowField[iField];
    9087         679 :         if (iArrowField >= 0)
    9088             :         {
    9089         664 :             if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
    9090             :             {
    9091             :                 // We cannot pass more than SQLITE_MAX_FUNCTION_ARG args
    9092             :                 // to a function... So we have to split in several calls...
    9093           3 :                 osSQL += "), OGR_GPKG_FillArrowArray_INTERNAL(";
    9094           3 :                 osSQL += CPLSPrintf("%d", iField);
    9095           3 :                 nCountArgs = 1;
    9096             :             }
    9097             :             const OGRFieldDefn *poFieldDefn =
    9098         664 :                 m_poFeatureDefn->GetFieldDefnUnsafe(iField);
    9099         664 :             osSQL += ',';
    9100         664 :             osSQL += '"';
    9101         664 :             osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
    9102         664 :             osSQL += '"';
    9103         664 :             ++nCountArgs;
    9104             :         }
    9105             :     }
    9106         119 :     osSQL += ") FROM \"";
    9107         119 :     osSQL += SQLEscapeName(m_pszTableName);
    9108         119 :     osSQL += "\" WHERE \"";
    9109         119 :     osSQL += SQLEscapeName(m_pszFidColumn);
    9110         119 :     osSQL += "\" BETWEEN ";
    9111         119 :     osSQL += std::to_string(m_iNextShapeId + 1);
    9112         119 :     osSQL += " AND ";
    9113         119 :     osSQL += std::to_string(m_iNextShapeId +
    9114         119 :                             sFillArrowArray.psHelper->m_nMaxBatchSize);
    9115             : 
    9116             :     // CPLDebug("GPKG", "%s", osSQL.c_str());
    9117             : 
    9118         119 :     char *pszErrMsg = nullptr;
    9119         119 :     if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
    9120         119 :                      &pszErrMsg) != SQLITE_OK)
    9121             :     {
    9122          13 :         if (!sFillArrowArray.bErrorOccurred &&
    9123          13 :             !sFillArrowArray.bMemoryLimitReached)
    9124             :         {
    9125           0 :             osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
    9126             :         }
    9127             :     }
    9128         119 :     sqlite3_free(pszErrMsg);
    9129             : 
    9130         119 :     bMemoryLimitReached = sFillArrowArray.bMemoryLimitReached;
    9131             : 
    9132             :     // Delete function
    9133         119 :     sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
    9134             :                             -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
    9135             :                             nullptr, nullptr, nullptr);
    9136             : 
    9137         119 :     if (sFillArrowArray.bErrorOccurred)
    9138             :     {
    9139           0 :         sFillArrowArray.psHelper->ClearArray();
    9140           0 :         return ENOMEM;
    9141             :     }
    9142             : 
    9143         119 :     sFillArrowArray.psHelper->Shrink(sFillArrowArray.nCountRows);
    9144         119 :     if (sFillArrowArray.nCountRows == 0)
    9145             :     {
    9146           0 :         sFillArrowArray.psHelper->ClearArray();
    9147             :     }
    9148             : 
    9149         119 :     m_iNextShapeId += sFillArrowArray.nCountRows;
    9150             : 
    9151         119 :     return 0;
    9152             : }
    9153             : 
    9154             : /************************************************************************/
    9155             : /*                 OGR_GPKG_GeometryExtent3DAggregate()                 */
    9156             : /************************************************************************/
    9157             : 
    9158             : namespace
    9159             : {
    9160             : struct GeometryExtent3DAggregateContext
    9161             : {
    9162             :     sqlite3 *m_hDB = nullptr;
    9163             :     OGREnvelope3D m_oExtent3D;
    9164             : 
    9165          18 :     explicit GeometryExtent3DAggregateContext(sqlite3 *hDB)
    9166          18 :         : m_hDB(hDB), m_oExtent3D()
    9167             :     {
    9168          18 :     }
    9169             : 
    9170             :     GeometryExtent3DAggregateContext(const GeometryExtent3DAggregateContext &) =
    9171             :         delete;
    9172             :     GeometryExtent3DAggregateContext &
    9173             :     operator=(const GeometryExtent3DAggregateContext &) = delete;
    9174             : };
    9175             : 
    9176             : }  // namespace
    9177             : 
    9178          26 : static void OGR_GPKG_GeometryExtent3DAggregate_Step(sqlite3_context *pContext,
    9179             :                                                     int /*argc*/,
    9180             :                                                     sqlite3_value **argv)
    9181             : {
    9182             :     const GByte *pabyBLOB =
    9183          26 :         reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
    9184             : 
    9185             :     auto poContext = static_cast<GeometryExtent3DAggregateContext *>(
    9186          26 :         sqlite3_user_data(pContext));
    9187             : 
    9188          26 :     if (pabyBLOB != nullptr)
    9189             :     {
    9190             :         GPkgHeader sHeader;
    9191          26 :         if (OGRGeoPackageGetHeader(pContext, 0, argv, &sHeader, true, true))
    9192             :         {
    9193          26 :             OGREnvelope3D extent3D;
    9194          26 :             extent3D.MinX = sHeader.MinX;
    9195          26 :             extent3D.MaxX = sHeader.MaxX;
    9196          26 :             extent3D.MinY = sHeader.MinY;
    9197          26 :             extent3D.MaxY = sHeader.MaxY;
    9198          26 :             extent3D.MinZ = sHeader.MinZ;
    9199          26 :             extent3D.MaxZ = sHeader.MaxZ;
    9200          26 :             poContext->m_oExtent3D.Merge(extent3D);
    9201             :         }
    9202           0 :         else if (!sHeader.bEmpty)
    9203             :         {
    9204             :             // Try also spatialite geometry blobs
    9205           0 :             const int nBLOBLen = sqlite3_value_bytes(argv[0]);
    9206           0 :             OGRGeometry *poGeom = nullptr;
    9207           0 :             if (OGRSQLiteImportSpatiaLiteGeometry(pabyBLOB, nBLOBLen,
    9208           0 :                                                   &poGeom) == OGRERR_NONE &&
    9209           0 :                 poGeom && !poGeom->IsEmpty())
    9210             :             {
    9211           0 :                 OGREnvelope3D extent3D;
    9212           0 :                 poGeom->getEnvelope(&extent3D);
    9213           0 :                 poContext->m_oExtent3D.Merge(extent3D);
    9214             :             }
    9215           0 :             delete poGeom;
    9216             :         }
    9217             :     }
    9218          26 : }
    9219             : 
    9220          18 : static void OGR_GPKG_GeometryExtent3DAggregate_Finalize(sqlite3_context *)
    9221             : {
    9222          18 : }
    9223             : 
    9224             : /************************************************************************/
    9225             : /*                             IGetExtent3D                             */
    9226             : /************************************************************************/
    9227          20 : OGRErr OGRGeoPackageTableLayer::IGetExtent3D(int iGeomField,
    9228             :                                              OGREnvelope3D *psExtent3D,
    9229             :                                              bool bForce)
    9230             : {
    9231             : 
    9232          20 :     const OGRFeatureDefn *poDefn = GetLayerDefn();
    9233             : 
    9234             :     /* -------------------------------------------------------------------- */
    9235             :     /*      Deferred actions, reset state.                                   */
    9236             :     /* -------------------------------------------------------------------- */
    9237          20 :     RunDeferredCreationIfNecessary();
    9238          20 :     if (!RunDeferredSpatialIndexUpdate())
    9239             :     {
    9240           0 :         return OGRERR_FAILURE;
    9241             :     }
    9242             : 
    9243          20 :     if (m_nZFlag == 0 && m_soFilter.empty())
    9244             :     {
    9245             :         // If the layer doesn't contain any 3D geometry and no filter is set,
    9246             :         // we can fallback to the fast 2D GetExtent()
    9247           2 :         const OGRErr retVal{GetExtent(iGeomField, psExtent3D, bForce)};
    9248           2 :         psExtent3D->MinZ = std::numeric_limits<double>::infinity();
    9249           2 :         psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
    9250           2 :         return retVal;
    9251             :     }
    9252             :     else
    9253             :     {
    9254          18 :         *psExtent3D = OGREnvelope3D();
    9255             :     }
    9256             : 
    9257             :     // For internal use only
    9258             : 
    9259          18 :     GeometryExtent3DAggregateContext sContext(m_poDS->hDB);
    9260             : 
    9261          36 :     CPLString osFuncName;
    9262             :     osFuncName.Printf("OGR_GPKG_GeometryExtent3DAggregate_INTERNAL_%p",
    9263          18 :                       &sContext);
    9264             : 
    9265          18 :     sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
    9266             :                             &sContext, nullptr,
    9267             :                             OGR_GPKG_GeometryExtent3DAggregate_Step,
    9268             :                             OGR_GPKG_GeometryExtent3DAggregate_Finalize);
    9269             : 
    9270          36 :     char *pszSQL = sqlite3_mprintf(
    9271             :         "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
    9272          18 :         poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
    9273          45 :         m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
    9274          18 :     char *pszErrMsg = nullptr;
    9275             :     const int rc =
    9276          18 :         sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
    9277             : 
    9278             :     // Delete function
    9279          18 :     sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
    9280             :                             nullptr, nullptr, nullptr, nullptr);
    9281             : 
    9282          18 :     if (rc != SQLITE_OK)
    9283             :     {
    9284           0 :         if (rc != SQLITE_INTERRUPT)
    9285             :         {
    9286           0 :             CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
    9287             :                      pszSQL, pszErrMsg);
    9288             :         }
    9289           0 :         sqlite3_free(pszErrMsg);
    9290           0 :         sqlite3_free(pszSQL);
    9291           0 :         return OGRERR_FAILURE;
    9292             :     }
    9293          18 :     sqlite3_free(pszErrMsg);
    9294          18 :     sqlite3_free(pszSQL);
    9295             : 
    9296          18 :     *psExtent3D = sContext.m_oExtent3D;
    9297             : 
    9298          18 :     return OGRERR_NONE;
    9299             : }
    9300             : 
    9301             : /************************************************************************/
    9302             : /*                              Truncate()                              */
    9303             : /************************************************************************/
    9304             : 
    9305             : /** Implements "DELETE FROM {table_name}" in an optimized way.
    9306             :  *
    9307             :  * Disable triggers if we detect that the only triggers on the table are ones
    9308             :  * under our control (i.e. the ones for the gpkg_ogr_contents table and the
    9309             :  * ones updating the RTree)
    9310             :  * And even if we cannot disable triggers, truncate the RTree before the main
    9311             :  * table, as this dramatically speeds up truncating the main table.
    9312             :  */
    9313           8 : OGRErr OGRGeoPackageTableLayer::Truncate()
    9314             : {
    9315           8 :     if (!m_poDS->GetUpdate())
    9316             :     {
    9317           2 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    9318             :                  "Truncate");
    9319           2 :         return OGRERR_FAILURE;
    9320             :     }
    9321             : 
    9322           6 :     ResetReading();
    9323           6 :     SyncToDisk();
    9324             : 
    9325           6 :     bool bOK = (m_poDS->SoftStartTransaction() == OGRERR_NONE);
    9326             : 
    9327             :     struct ReenableTriggers
    9328             :     {
    9329             :         sqlite3 *m_hDB = nullptr;
    9330             : 
    9331           4 :         explicit ReenableTriggers(sqlite3 *hDB) : m_hDB(hDB)
    9332             :         {
    9333           4 :         }
    9334             : 
    9335           4 :         ~ReenableTriggers()
    9336           4 :         {
    9337           4 :             sqlite3_db_config(m_hDB, SQLITE_DBCONFIG_ENABLE_TRIGGER, 1,
    9338             :                               nullptr);
    9339           4 :         }
    9340             :         CPL_DISALLOW_COPY_ASSIGN(ReenableTriggers)
    9341             :     };
    9342             : 
    9343             :     // to keep in top level scope!
    9344           0 :     std::unique_ptr<ReenableTriggers> reenableTriggers;
    9345             : 
    9346             :     // Temporarily disable triggers for greater speed if we detect that the
    9347             :     // only triggers on the table are the RTree ones and the ones for the
    9348             :     // gpkg_ogr_contents table
    9349           6 :     if (bOK && m_bIsTable)
    9350             :     {
    9351           6 :         char *pszSQL = sqlite3_mprintf(
    9352             :             "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' "
    9353             :             "AND tbl_name = '%q' "
    9354             :             "AND name NOT IN ('trigger_insert_feature_count_%q',"
    9355             :             "'trigger_delete_feature_count_%q') "
    9356             :             "AND name NOT LIKE 'rtree_%q_%%'",
    9357             :             m_pszTableName, m_pszTableName, m_pszTableName, m_pszTableName);
    9358           6 :         OGRErr eErr = OGRERR_NONE;
    9359          10 :         if (SQLGetInteger(m_poDS->GetDB(), pszSQL, &eErr) == 0 &&
    9360           4 :             eErr == OGRERR_NONE)
    9361             :         {
    9362           4 :             int nEnableTriggerOldVal = -1;
    9363           4 :             sqlite3_db_config(m_poDS->GetDB(), SQLITE_DBCONFIG_ENABLE_TRIGGER,
    9364             :                               -1, &nEnableTriggerOldVal);
    9365           4 :             if (nEnableTriggerOldVal == 1)
    9366             :             {
    9367           4 :                 int nNewVal = -1;
    9368           4 :                 sqlite3_db_config(m_poDS->GetDB(),
    9369             :                                   SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &nNewVal);
    9370           4 :                 if (nNewVal == 0)
    9371             :                 {
    9372           4 :                     CPLDebugOnly("GPKG",
    9373             :                                  "Disabling triggers during truncation of %s",
    9374             :                                  m_pszTableName);
    9375             :                     reenableTriggers =
    9376           4 :                         std::make_unique<ReenableTriggers>(m_poDS->GetDB());
    9377           4 :                     CPL_IGNORE_RET_VAL(reenableTriggers);  // to please cppcheck
    9378             :                 }
    9379             :             }
    9380             :         }
    9381           6 :         sqlite3_free(pszSQL);
    9382             :     }
    9383             : 
    9384           6 :     char *pszErrMsg = nullptr;
    9385           6 :     if (bOK && m_bIsTable && HasSpatialIndex())
    9386             :     {
    9387             :         // Manually clean the 3 tables that are used by the RTree:
    9388             :         // - rtree_{tablename}_{geom}_node: all rows, but nodeno = 1 for which
    9389             :         //   we reset the 'data' field to a zero blob of the same size
    9390             :         // - rtree_{tablename}_{geom}_parent: all rows
    9391             :         // - rtree_{tablename}_{geom}_rowid: all rows
    9392             : 
    9393           2 :         const char *pszT = m_pszTableName;
    9394           2 :         const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
    9395             : 
    9396           2 :         m_osRTreeName = "rtree_";
    9397           2 :         m_osRTreeName += pszT;
    9398           2 :         m_osRTreeName += "_";
    9399           2 :         m_osRTreeName += pszC;
    9400             : 
    9401             :         {
    9402             :             char *pszSQL =
    9403           2 :                 sqlite3_mprintf("DELETE FROM \"%w_node\" WHERE nodeno > 1;"
    9404             :                                 "DELETE FROM \"%w_parent\"; "
    9405             :                                 "DELETE FROM \"%w_rowid\"",
    9406             :                                 m_osRTreeName.c_str(), m_osRTreeName.c_str(),
    9407             :                                 m_osRTreeName.c_str());
    9408           2 :             bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
    9409             :                                &pszErrMsg) == SQLITE_OK;
    9410           2 :             sqlite3_free(pszSQL);
    9411             :         }
    9412             : 
    9413           2 :         if (bOK)
    9414             :         {
    9415           2 :             char *pszSQL = sqlite3_mprintf(
    9416             :                 "SELECT length(data) FROM \"%w_node\" WHERE nodeno = 1",
    9417             :                 m_osRTreeName.c_str());
    9418             :             const int nBlobSize =
    9419           2 :                 SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr);
    9420           2 :             sqlite3_free(pszSQL);
    9421             : 
    9422           2 :             pszSQL = sqlite3_mprintf(
    9423             :                 "UPDATE \"%w_node\" SET data = zeroblob(%d) WHERE nodeno = 1",
    9424             :                 m_osRTreeName.c_str(), nBlobSize);
    9425           2 :             bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
    9426             :                                &pszErrMsg) == SQLITE_OK;
    9427           2 :             sqlite3_free(pszSQL);
    9428             :         }
    9429             :     }
    9430             : 
    9431           6 :     if (bOK)
    9432             :     {
    9433             :         // Truncate main table
    9434           6 :         char *pszSQL = sqlite3_mprintf("DELETE FROM \"%w\"", m_pszTableName);
    9435           6 :         bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
    9436             :               SQLITE_OK;
    9437           6 :         sqlite3_free(pszSQL);
    9438             :     }
    9439             : 
    9440             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    9441             :     // Reset feature count
    9442           6 :     if (bOK && m_poDS->m_bHasGPKGOGRContents)
    9443             :     {
    9444             :         char *pszSQL =
    9445           5 :             sqlite3_mprintf("UPDATE gpkg_ogr_contents SET feature_count = 0 "
    9446             :                             "WHERE lower(table_name) = lower('%q')",
    9447             :                             m_pszTableName);
    9448           5 :         bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
    9449             :               SQLITE_OK;
    9450           5 :         sqlite3_free(pszSQL);
    9451             :     }
    9452             : 
    9453           6 :     if (bOK)
    9454             :     {
    9455           5 :         m_nTotalFeatureCount = 0;
    9456             :     }
    9457             : #endif
    9458             : 
    9459           6 :     if (bOK)
    9460             :     {
    9461           5 :         m_poDS->SoftCommitTransaction();
    9462             :     }
    9463             :     else
    9464             :     {
    9465           1 :         m_poDS->SoftRollbackTransaction();
    9466             : #ifdef ENABLE_GPKG_OGR_CONTENTS
    9467           1 :         DisableFeatureCount();
    9468             : #endif
    9469           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Truncate(%s) failed: %s",
    9470           1 :                  m_pszTableName, pszErrMsg ? pszErrMsg : "(unknown reason)");
    9471             :     }
    9472           6 :     sqlite3_free(pszErrMsg);
    9473             : 
    9474           6 :     return bOK ? OGRERR_NONE : OGRERR_FAILURE;
    9475             : }

Generated by: LCOV version 1.14