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