LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpkg - ogrgeopackagetablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3796 4204 90.3 %
Date: 2025-03-28 11:40:40 Functions: 132 132 100.0 %

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

Generated by: LCOV version 1.14