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