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