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