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

Generated by: LCOV version 1.14