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