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