LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagetablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3773 4185 90.2 %
Date: 2024-11-21 22:18:42 Functions: 132 132 100.0 %

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

Generated by: LCOV version 1.14