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