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