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