Coverage Report

Created: 2025-12-03 08:24

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